|
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 |