Fri, 03 Jun 2016 21:14:09 +0200
Bugfixing, Added M400 magic
15 | 1 | # This file is part of the Printrun suite. |
2 | # | |
3 | # Printrun is free software: you can redistribute it and/or modify | |
4 | # it under the terms of the GNU General Public License as published by | |
5 | # the Free Software Foundation, either version 3 of the License, or | |
6 | # (at your option) any later version. | |
7 | # | |
8 | # Printrun is distributed in the hope that it will be useful, | |
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | # GNU General Public License for more details. | |
12 | # | |
13 | # You should have received a copy of the GNU General Public License | |
14 | # along with Printrun. If not, see <http://www.gnu.org/licenses/>. | |
15 | ||
16 | import wx | |
17 | import re | |
18 | ||
19 | class MacroEditor(wx.Dialog): | |
20 | """Really simple editor to edit macro definitions""" | |
21 | ||
22 | def __init__(self, macro_name, definition, callback, gcode = False): | |
23 | self.indent_chars = " " | |
24 | title = " macro %s" | |
25 | if gcode: | |
26 | title = " %s" | |
27 | self.gcode = gcode | |
28 | wx.Dialog.__init__(self, None, title = title % macro_name, | |
29 | style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) | |
30 | self.callback = callback | |
31 | self.panel = wx.Panel(self, -1) | |
32 | titlesizer = wx.BoxSizer(wx.HORIZONTAL) | |
33 | self.titletext = wx.StaticText(self.panel, -1, " _") # title%macro_name) | |
34 | titlesizer.Add(self.titletext, 1) | |
35 | self.findb = wx.Button(self.panel, -1, _("Find"), style = wx.BU_EXACTFIT) # New button for "Find" (Jezmy) | |
36 | self.findb.Bind(wx.EVT_BUTTON, self.find) | |
37 | self.okb = wx.Button(self.panel, -1, _("Save"), style = wx.BU_EXACTFIT) | |
38 | self.okb.Bind(wx.EVT_BUTTON, self.save) | |
39 | self.Bind(wx.EVT_CLOSE, self.close) | |
40 | titlesizer.Add(self.findb) | |
41 | titlesizer.Add(self.okb) | |
42 | self.cancelb = wx.Button(self.panel, -1, _("Cancel"), style = wx.BU_EXACTFIT) | |
43 | self.cancelb.Bind(wx.EVT_BUTTON, self.close) | |
44 | titlesizer.Add(self.cancelb) | |
45 | topsizer = wx.BoxSizer(wx.VERTICAL) | |
46 | topsizer.Add(titlesizer, 0, wx.EXPAND) | |
47 | self.e = wx.TextCtrl(self.panel, style = wx.HSCROLL | wx.TE_MULTILINE | wx.TE_RICH2, size = (400, 400)) | |
48 | if not self.gcode: | |
49 | self.e.SetValue(self.unindent(definition)) | |
50 | else: | |
51 | self.e.SetValue("\n".join(definition)) | |
52 | topsizer.Add(self.e, 1, wx.ALL | wx.EXPAND) | |
53 | self.panel.SetSizer(topsizer) | |
54 | topsizer.Layout() | |
55 | topsizer.Fit(self) | |
56 | self.Show() | |
57 | self.e.SetFocus() | |
58 | ||
59 | def find(self, ev): | |
60 | # Ask user what to look for, find it and point at it ... (Jezmy) | |
61 | S = self.e.GetStringSelection() | |
62 | if not S: | |
63 | S = "Z" | |
64 | FindValue = wx.GetTextFromUser('Please enter a search string:', caption = "Search", default_value = S, parent = None) | |
65 | somecode = self.e.GetValue() | |
66 | position = somecode.find(FindValue, self.e.GetInsertionPoint()) | |
67 | if position == -1: | |
68 | self.titletext.SetLabel(_("Not Found!")) | |
69 | else: | |
70 | self.titletext.SetLabel(str(position)) | |
71 | ||
72 | # ananswer = wx.MessageBox(str(numLines)+" Lines detected in file\n"+str(position), "OK") | |
73 | self.e.SetFocus() | |
74 | self.e.SetInsertionPoint(position) | |
75 | self.e.SetSelection(position, position + len(FindValue)) | |
76 | self.e.ShowPosition(position) | |
77 | ||
78 | def ShowMessage(self, ev, message): | |
79 | dlg = wx.MessageDialog(self, message, | |
80 | "Info!", wx.OK | wx.ICON_INFORMATION) | |
81 | dlg.ShowModal() | |
82 | dlg.Destroy() | |
83 | ||
84 | def save(self, ev): | |
85 | self.Destroy() | |
86 | if not self.gcode: | |
87 | self.callback(self.reindent(self.e.GetValue())) | |
88 | else: | |
89 | self.callback(self.e.GetValue().split("\n")) | |
90 | ||
91 | def close(self, ev): | |
92 | self.Destroy() | |
93 | ||
94 | def unindent(self, text): | |
95 | self.indent_chars = text[:len(text) - len(text.lstrip())] | |
96 | if len(self.indent_chars) == 0: | |
97 | self.indent_chars = " " | |
98 | unindented = "" | |
99 | lines = re.split(r"(?:\r\n?|\n)", text) | |
100 | if len(lines) <= 1: | |
101 | return text | |
102 | for line in lines: | |
103 | if line.startswith(self.indent_chars): | |
104 | unindented += line[len(self.indent_chars):] + "\n" | |
105 | else: | |
106 | unindented += line + "\n" | |
107 | return unindented | |
108 | ||
109 | def reindent(self, text): | |
110 | lines = re.split(r"(?:\r\n?|\n)", text) | |
111 | if len(lines) <= 1: | |
112 | return text | |
113 | reindented = "" | |
114 | for line in lines: | |
115 | if line.strip() != "": | |
116 | reindented += self.indent_chars + line + "\n" | |
117 | return reindented | |
118 | ||
119 | SETTINGS_GROUPS = {"Printer": _("Printer settings"), | |
120 | "UI": _("User interface"), | |
121 | "Viewer": _("Viewer"), | |
122 | "Colors": _("Colors"), | |
123 | "External": _("External commands")} | |
124 | ||
125 | class PronterOptionsDialog(wx.Dialog): | |
126 | """Options editor""" | |
127 | def __init__(self, pronterface): | |
128 | wx.Dialog.__init__(self, parent = None, title = _("Edit settings"), | |
129 | size = (400, 500), style = wx.DEFAULT_DIALOG_STYLE) | |
130 | panel = wx.Panel(self) | |
131 | header = wx.StaticBox(panel, label = _("Settings")) | |
132 | sbox = wx.StaticBoxSizer(header, wx.VERTICAL) | |
133 | notebook = wx.Notebook(panel) | |
134 | all_settings = pronterface.settings._all_settings() | |
135 | group_list = [] | |
136 | groups = {} | |
137 | for group in ["Printer", "UI", "Viewer", "Colors", "External"]: | |
138 | group_list.append(group) | |
139 | groups[group] = [] | |
140 | for setting in all_settings: | |
141 | if setting.group not in group_list: | |
142 | group_list.append(setting.group) | |
143 | groups[setting.group] = [] | |
144 | groups[setting.group].append(setting) | |
145 | for group in group_list: | |
146 | grouppanel = wx.Panel(notebook, -1) | |
147 | notebook.AddPage(grouppanel, SETTINGS_GROUPS[group]) | |
148 | settings = groups[group] | |
149 | grid = wx.GridBagSizer(hgap = 8, vgap = 2) | |
150 | current_row = 0 | |
151 | for setting in settings: | |
152 | if setting.name.startswith("separator_"): | |
153 | sep = wx.StaticLine(grouppanel, size = (-1, 5), style = wx.LI_HORIZONTAL) | |
154 | grid.Add(sep, pos = (current_row, 0), span = (1, 2), | |
155 | border = 3, flag = wx.ALIGN_CENTER | wx.ALL | wx.EXPAND) | |
156 | current_row += 1 | |
157 | label, widget = setting.get_label(grouppanel), setting.get_widget(grouppanel) | |
158 | if setting.name.startswith("separator_"): | |
159 | font = label.GetFont() | |
160 | font.SetWeight(wx.BOLD) | |
161 | label.SetFont(font) | |
162 | grid.Add(label, pos = (current_row, 0), | |
163 | flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT) | |
164 | grid.Add(widget, pos = (current_row, 1), | |
165 | flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND) | |
166 | if hasattr(label, "set_default"): | |
167 | label.Bind(wx.EVT_MOUSE_EVENTS, label.set_default) | |
168 | if hasattr(widget, "Bind"): | |
169 | widget.Bind(wx.EVT_MOUSE_EVENTS, label.set_default) | |
170 | current_row += 1 | |
171 | grid.AddGrowableCol(1) | |
172 | grouppanel.SetSizer(grid) | |
173 | sbox.Add(notebook, 1, wx.EXPAND) | |
174 | panel.SetSizer(sbox) | |
175 | topsizer = wx.BoxSizer(wx.VERTICAL) | |
176 | topsizer.Add(panel, 1, wx.ALL | wx.EXPAND) | |
177 | topsizer.Add(self.CreateButtonSizer(wx.OK | wx.CANCEL), 0, wx.ALIGN_RIGHT) | |
178 | self.SetSizerAndFit(topsizer) | |
179 | self.SetMinSize(self.GetSize()) | |
180 | ||
181 | def PronterOptions(pronterface): | |
182 | dialog = PronterOptionsDialog(pronterface) | |
183 | if dialog.ShowModal() == wx.ID_OK: | |
184 | for setting in pronterface.settings._all_settings(): | |
185 | old_value = setting.value | |
186 | setting.update() | |
187 | if setting.value != old_value: | |
188 | pronterface.set(setting.name, setting.value) | |
189 | dialog.Destroy() | |
190 | ||
191 | class ButtonEdit(wx.Dialog): | |
192 | """Custom button edit dialog""" | |
193 | def __init__(self, pronterface): | |
194 | wx.Dialog.__init__(self, None, title = _("Custom button"), | |
195 | style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) | |
196 | self.pronterface = pronterface | |
197 | topsizer = wx.BoxSizer(wx.VERTICAL) | |
198 | grid = wx.FlexGridSizer(rows = 0, cols = 2, hgap = 4, vgap = 2) | |
199 | grid.AddGrowableCol(1, 1) | |
200 | grid.Add(wx.StaticText(self, -1, _("Button title")), 0, wx.BOTTOM | wx.RIGHT) | |
201 | self.name = wx.TextCtrl(self, -1, "") | |
202 | grid.Add(self.name, 1, wx.EXPAND) | |
203 | grid.Add(wx.StaticText(self, -1, _("Command")), 0, wx.BOTTOM | wx.RIGHT) | |
204 | self.command = wx.TextCtrl(self, -1, "") | |
205 | xbox = wx.BoxSizer(wx.HORIZONTAL) | |
206 | xbox.Add(self.command, 1, wx.EXPAND) | |
207 | self.command.Bind(wx.EVT_TEXT, self.macrob_enabler) | |
208 | self.macrob = wx.Button(self, -1, "..", style = wx.BU_EXACTFIT) | |
209 | self.macrob.Bind(wx.EVT_BUTTON, self.macrob_handler) | |
210 | xbox.Add(self.macrob, 0) | |
211 | grid.Add(xbox, 1, wx.EXPAND) | |
212 | grid.Add(wx.StaticText(self, -1, _("Color")), 0, wx.BOTTOM | wx.RIGHT) | |
213 | self.color = wx.TextCtrl(self, -1, "") | |
214 | grid.Add(self.color, 1, wx.EXPAND) | |
215 | topsizer.Add(grid, 0, wx.EXPAND) | |
216 | topsizer.Add((0, 0), 1) | |
217 | topsizer.Add(self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL), 0, wx.ALIGN_CENTER) | |
218 | self.SetSizer(topsizer) | |
219 | ||
220 | def macrob_enabler(self, e): | |
221 | macro = self.command.GetValue() | |
222 | valid = False | |
223 | try: | |
224 | if macro == "": | |
225 | valid = True | |
226 | elif macro in self.pronterface.macros: | |
227 | valid = True | |
228 | elif hasattr(self.pronterface.__class__, u"do_" + macro): | |
229 | valid = False | |
230 | elif len([c for c in macro if not c.isalnum() and c != "_"]): | |
231 | valid = False | |
232 | else: | |
233 | valid = True | |
234 | except: | |
235 | if macro == "": | |
236 | valid = True | |
237 | elif macro in self.pronterface.macros: | |
238 | valid = True | |
239 | elif len([c for c in macro if not c.isalnum() and c != "_"]): | |
240 | valid = False | |
241 | else: | |
242 | valid = True | |
243 | self.macrob.Enable(valid) | |
244 | ||
245 | def macrob_handler(self, e): | |
246 | macro = self.command.GetValue() | |
247 | macro = self.pronterface.edit_macro(macro) | |
248 | self.command.SetValue(macro) | |
249 | if self.name.GetValue() == "": | |
250 | self.name.SetValue(macro) | |
251 | ||
252 | class TempGauge(wx.Panel): | |
253 | ||
254 | def __init__(self, parent, size = (200, 22), title = "", | |
255 | maxval = 240, gaugeColour = None, bgcolor = "#FFFFFF"): | |
256 | wx.Panel.__init__(self, parent, -1, size = size) | |
257 | self.Bind(wx.EVT_PAINT, self.paint) | |
258 | self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM) | |
259 | self.bgcolor = wx.Colour() | |
260 | self.bgcolor.SetFromName(bgcolor) | |
261 | self.width, self.height = size | |
262 | self.title = title | |
263 | self.max = maxval | |
264 | self.gaugeColour = gaugeColour | |
265 | self.value = 0 | |
266 | self.setpoint = 0 | |
267 | self.recalc() | |
268 | ||
269 | def recalc(self): | |
270 | mmax = max(int(self.setpoint * 1.05), self.max) | |
271 | self.scale = float(self.width - 2) / float(mmax) | |
272 | self.ypt = max(16, int(self.scale * max(self.setpoint, self.max / 6))) | |
273 | ||
274 | def SetValue(self, value): | |
275 | self.value = value | |
276 | wx.CallAfter(self.Refresh) | |
277 | ||
278 | def SetTarget(self, value): | |
279 | self.setpoint = value | |
280 | wx.CallAfter(self.Refresh) | |
281 | ||
282 | def interpolatedColour(self, val, vmin, vmid, vmax, cmin, cmid, cmax): | |
283 | if val < vmin: return cmin | |
284 | if val > vmax: return cmax | |
285 | if val <= vmid: | |
286 | lo, hi, val, valhi = cmin, cmid, val - vmin, vmid - vmin | |
287 | else: | |
288 | lo, hi, val, valhi = cmid, cmax, val - vmid, vmax - vmid | |
289 | vv = float(val) / valhi | |
290 | rgb = lo.Red() + (hi.Red() - lo.Red()) * vv, lo.Green() + (hi.Green() - lo.Green()) * vv, lo.Blue() + (hi.Blue() - lo.Blue()) * vv | |
291 | rgb = map(lambda x: x * 0.8, rgb) | |
292 | return wx.Colour(*map(int, rgb)) | |
293 | ||
294 | def paint(self, ev): | |
295 | self.width, self.height = self.GetClientSizeTuple() | |
296 | self.recalc() | |
297 | x0, y0, x1, y1, xE, yE = 1, 1, self.ypt + 1, 1, self.width + 1 - 2, 20 | |
298 | dc = wx.PaintDC(self) | |
299 | dc.SetBackground(wx.Brush(self.bgcolor)) | |
300 | dc.Clear() | |
301 | cold, medium, hot = wx.Colour(0, 167, 223), wx.Colour(239, 233, 119), wx.Colour(210, 50.100) | |
302 | # gauge1, gauge2 = wx.Colour(255, 255, 210), (self.gaugeColour or wx.Colour(234, 82, 0)) | |
303 | gauge1 = wx.Colour(255, 255, 210) | |
304 | shadow1, shadow2 = wx.Colour(110, 110, 110), self.bgcolor | |
305 | gc = wx.GraphicsContext.Create(dc) | |
306 | # draw shadow first | |
307 | # corners | |
308 | gc.SetBrush(gc.CreateRadialGradientBrush(xE - 7, 9, xE - 7, 9, 8, shadow1, shadow2)) | |
309 | gc.DrawRectangle(xE - 7, 1, 8, 8) | |
310 | gc.SetBrush(gc.CreateRadialGradientBrush(xE - 7, 17, xE - 7, 17, 8, shadow1, shadow2)) | |
311 | gc.DrawRectangle(xE - 7, 17, 8, 8) | |
312 | gc.SetBrush(gc.CreateRadialGradientBrush(x0 + 6, 17, x0 + 6, 17, 8, shadow1, shadow2)) | |
313 | gc.DrawRectangle(0, 17, x0 + 6, 8) | |
314 | # edges | |
315 | gc.SetBrush(gc.CreateLinearGradientBrush(xE - 6, 0, xE + 1, 0, shadow1, shadow2)) | |
316 | gc.DrawRectangle(xE - 7, 9, 8, 8) | |
317 | gc.SetBrush(gc.CreateLinearGradientBrush(x0, yE - 2, x0, yE + 5, shadow1, shadow2)) | |
318 | gc.DrawRectangle(x0 + 6, yE - 2, xE - 12, 7) | |
319 | # draw gauge background | |
320 | gc.SetBrush(gc.CreateLinearGradientBrush(x0, y0, x1 + 1, y1, cold, medium)) | |
321 | gc.DrawRoundedRectangle(x0, y0, x1 + 4, yE, 6) | |
322 | gc.SetBrush(gc.CreateLinearGradientBrush(x1 - 2, y1, xE, y1, medium, hot)) | |
323 | gc.DrawRoundedRectangle(x1 - 2, y1, xE - x1, yE, 6) | |
324 | # draw gauge | |
325 | width = 12 | |
326 | w1 = y0 + 9 - width / 2 | |
327 | w2 = w1 + width | |
328 | value = x0 + max(10, min(self.width + 1 - 2, int(self.value * self.scale))) | |
329 | # gc.SetBrush(gc.CreateLinearGradientBrush(x0, y0 + 3, x0, y0 + 15, gauge1, gauge2)) | |
330 | # gc.SetBrush(gc.CreateLinearGradientBrush(0, 3, 0, 15, wx.Colour(255, 255, 255), wx.Colour(255, 90, 32))) | |
331 | gc.SetBrush(gc.CreateLinearGradientBrush(x0, y0 + 3, x0, y0 + 15, gauge1, self.interpolatedColour(value, x0, x1, xE, cold, medium, hot))) | |
332 | val_path = gc.CreatePath() | |
333 | val_path.MoveToPoint(x0, w1) | |
334 | val_path.AddLineToPoint(value, w1) | |
335 | val_path.AddLineToPoint(value + 2, w1 + width / 4) | |
336 | val_path.AddLineToPoint(value + 2, w2 - width / 4) | |
337 | val_path.AddLineToPoint(value, w2) | |
338 | # val_path.AddLineToPoint(value-4, 10) | |
339 | val_path.AddLineToPoint(x0, w2) | |
340 | gc.DrawPath(val_path) | |
341 | # draw setpoint markers | |
342 | setpoint = x0 + max(10, int(self.setpoint * self.scale)) | |
343 | gc.SetBrush(gc.CreateBrush(wx.Brush(wx.Colour(0, 0, 0)))) | |
344 | setp_path = gc.CreatePath() | |
345 | setp_path.MoveToPoint(setpoint - 4, y0) | |
346 | setp_path.AddLineToPoint(setpoint + 4, y0) | |
347 | setp_path.AddLineToPoint(setpoint, y0 + 5) | |
348 | setp_path.MoveToPoint(setpoint - 4, yE) | |
349 | setp_path.AddLineToPoint(setpoint + 4, yE) | |
350 | setp_path.AddLineToPoint(setpoint, yE - 5) | |
351 | gc.DrawPath(setp_path) | |
352 | # draw readout | |
353 | text = u"T\u00B0 %u/%u" % (self.value, self.setpoint) | |
354 | # gc.SetFont(gc.CreateFont(wx.Font(12, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD), wx.WHITE)) | |
355 | # gc.DrawText(text, 29,-2) | |
356 | gc.SetFont(gc.CreateFont(wx.Font(10, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD), wx.WHITE)) | |
357 | gc.DrawText(self.title, x0 + 19, y0 + 4) | |
358 | gc.DrawText(text, x0 + 119, y0 + 4) | |
359 | gc.SetFont(gc.CreateFont(wx.Font(10, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD))) | |
360 | gc.DrawText(self.title, x0 + 18, y0 + 3) | |
361 | gc.DrawText(text, x0 + 118, y0 + 3) | |
362 | ||
363 | class SpecialButton(object): | |
364 | ||
365 | label = None | |
366 | command = None | |
367 | background = None | |
368 | tooltip = None | |
369 | custom = None | |
370 | ||
371 | def __init__(self, label, command, background = None, | |
372 | tooltip = None, custom = False): | |
373 | self.label = label | |
374 | self.command = command | |
375 | self.background = background | |
376 | self.tooltip = tooltip | |
377 | self.custom = custom |