Wed, 20 Jan 2021 11:37:03 +0100
reimplemented lasercutter changes
46 | 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 | # Copyright 2017 Rock Storm <rockstorm@gmx.com> | |
17 | ||
18 | import wx | |
19 | from . import spoolmanager | |
20 | ||
21 | class SpoolManagerMainWindow(wx.Frame): | |
22 | """ | |
23 | Front-end for the Spool Manager. | |
24 | ||
25 | Main window which displays the currently loaded spools and the list of | |
26 | recorded ones with buttons to add, load, edit or delete them. | |
27 | """ | |
28 | ||
29 | def __init__(self, parent, spool_manager): | |
30 | wx.Frame.__init__(self, parent, | |
31 | title = "Spool Manager", | |
32 | style = wx.DEFAULT_FRAME_STYLE | wx.FRAME_FLOAT_ON_PARENT) | |
33 | ||
34 | self.statusbar = self.CreateStatusBar() | |
35 | ||
36 | self.SetIcon(parent.GetIcon()) | |
37 | ||
38 | # Initiate the back-end | |
39 | self.spool_manager = spool_manager | |
40 | self.spool_manager.refresh() | |
41 | ||
42 | # Generate the dialogs showing the current spools | |
43 | self.current_spools_dialog = CurrentSpoolDialog(self, | |
44 | self.spool_manager) | |
45 | ||
46 | # Generate the list of recorded spools | |
47 | self.spool_list = SpoolListView(self, self.spool_manager) | |
48 | ||
49 | # Generate the buttons | |
50 | self.new_button = wx.Button(self, wx.ID_ADD) | |
51 | self.new_button.SetToolTip("Add a new spool") | |
52 | self.edit_button = wx.Button(self, wx.ID_EDIT) | |
53 | self.edit_button.SetToolTip("Edit the selected spool") | |
54 | self.delete_button = wx.Button(self, wx.ID_DELETE) | |
55 | self.delete_button.SetToolTip("Delete the selected spool") | |
56 | ||
57 | # "Program" the buttons | |
58 | self.new_button.Bind(wx.EVT_BUTTON, self.onClickAdd) | |
59 | self.edit_button.Bind(wx.EVT_BUTTON, self.onClickEdit) | |
60 | self.delete_button.Bind(wx.EVT_BUTTON, self.onClickDelete) | |
61 | ||
62 | # Layout | |
63 | ## Group the buttons | |
64 | self.button_sizer = wx.BoxSizer(wx.VERTICAL) | |
65 | self.button_sizer.Add(self.new_button, 1, | |
66 | wx.FIXED_MINSIZE | wx.ALIGN_CENTER) | |
67 | self.button_sizer.Add(self.edit_button, 1, | |
68 | wx.FIXED_MINSIZE | wx.ALIGN_CENTER) | |
69 | self.button_sizer.Add(self.delete_button, 1, | |
70 | wx.FIXED_MINSIZE | wx.ALIGN_CENTER) | |
71 | ||
72 | ## Group the buttons with the spool list | |
73 | self.list_sizer = wx.BoxSizer(wx.HORIZONTAL) | |
74 | self.list_sizer.Add(self.spool_list, 1, wx.EXPAND) | |
75 | self.list_sizer.Add(self.button_sizer, 0, wx.ALIGN_CENTER) | |
76 | ||
77 | ## Layout the whole thing | |
78 | self.full_sizer = wx.BoxSizer(wx.VERTICAL) | |
79 | self.full_sizer.Add(self.current_spools_dialog, 0, wx.EXPAND) | |
80 | self.full_sizer.Add(self.list_sizer, 1, wx.ALL | wx.EXPAND, 10) | |
81 | ||
82 | self.SetSizerAndFit(self.full_sizer) | |
83 | ||
84 | def onClickAdd(self, event): | |
85 | """Open the window for customizing the new spool.""" | |
86 | SpoolManagerAddWindow(self).Show(True) | |
87 | ||
88 | def onClickLoad(self, event, extruder): | |
89 | """Load the selected spool to the correspondent extruder.""" | |
90 | ||
91 | # Check whether there is a spool selected | |
92 | spool_index = self.spool_list.GetFirstSelected() | |
93 | if spool_index == -1 : | |
94 | self.statusbar.SetStatusText( | |
95 | "Could not load the spool. No spool selected.") | |
96 | return 0 | |
97 | else: | |
98 | spool_name = self.spool_list.GetItemText(spool_index) | |
99 | self.statusbar.SetStatusText("") | |
100 | ||
101 | # If selected spool is already loaded, do nothing | |
102 | spool_extruder = self.spool_manager.isLoaded(spool_name) | |
103 | if spool_extruder > -1: | |
104 | self.statusbar.SetStatusText( | |
105 | "Spool '%s' is already loaded for Extruder %d." % | |
106 | (spool_name, spool_extruder)) | |
107 | return 0 | |
108 | ||
109 | # Load the selected spool and refresh the current spools dialog | |
110 | self.spool_manager.load(spool_name, extruder) | |
111 | self.current_spools_dialog.refreshDialog(self.spool_manager) | |
112 | self.statusbar.SetStatusText( | |
113 | "Loaded spool '%s' for Extruder %d." % (spool_name, extruder)) | |
114 | ||
115 | def onClickUnload(self, event, extruder): | |
116 | """Unload the spool from the correspondent extruder.""" | |
117 | ||
118 | spool_name = self.spool_manager.getSpoolName(extruder) | |
119 | if spool_name != None: | |
120 | self.spool_manager.unload(extruder) | |
121 | self.current_spools_dialog.refreshDialog(self.spool_manager) | |
122 | self.statusbar.SetStatusText( | |
123 | "Unloaded spool from Extruder %d." % extruder) | |
124 | else: | |
125 | self.statusbar.SetStatusText( | |
126 | "There is no spool loaded for Extruder %d." % extruder) | |
127 | ||
128 | def onClickEdit(self, event): | |
129 | """Open the window for editing the data of the selected spool.""" | |
130 | ||
131 | # Check whether there is a spool selected | |
132 | spool_index = self.spool_list.GetFirstSelected() | |
133 | if spool_index == -1 : | |
134 | self.statusbar.SetStatusText( | |
135 | "Could not edit the spool. No spool selected.") | |
136 | return 0 | |
137 | ||
138 | # Open the edit window | |
139 | spool_name = self.spool_list.GetItemText(spool_index) | |
140 | spool_length = self.spool_list.GetItemText(spool_index, 1) | |
141 | SpoolManagerEditWindow(self, spool_name, spool_length).Show(True) | |
142 | self.statusbar.SetStatusText("") | |
143 | ||
144 | def onClickDelete(self, event): | |
145 | """Delete the selected spool.""" | |
146 | ||
147 | # Get the selected spool | |
148 | spool_index = self.spool_list.GetFirstSelected() | |
149 | if spool_index == -1 : | |
150 | self.statusbar.SetStatusText( | |
151 | "Could not delete the spool. No spool selected.") | |
152 | return 0 | |
153 | else: | |
154 | spool_name = self.spool_list.GetItemText(spool_index) | |
155 | self.statusbar.SetStatusText("") | |
156 | ||
157 | # Ask confirmation for deleting | |
158 | delete_dialog = wx.MessageDialog(self, | |
159 | message = "Are you sure you want to delete the '%s' spool" % | |
160 | spool_name, | |
161 | caption = "Delete Spool", | |
162 | style = wx.YES_NO | wx.ICON_EXCLAMATION) | |
163 | ||
164 | if delete_dialog.ShowModal() == wx.ID_YES: | |
165 | # Remove spool | |
166 | self.spool_manager.remove(spool_name) | |
167 | self.spool_list.refreshList(self.spool_manager) | |
168 | self.current_spools_dialog.refreshDialog(self.spool_manager) | |
169 | self.statusbar.SetStatusText( | |
170 | "Deleted spool '%s'." % spool_name) | |
171 | ||
172 | ||
173 | class SpoolListView(wx.ListView): | |
174 | """ | |
175 | Custom wxListView object which visualizes the list of available spools. | |
176 | """ | |
177 | ||
178 | def __init__(self, parent, spool_manager): | |
179 | wx.ListView.__init__(self, parent, | |
180 | style = wx.LC_REPORT | wx.LC_SINGLE_SEL) | |
181 | self.InsertColumn(0, "Spool", width = wx.LIST_AUTOSIZE_USEHEADER) | |
182 | self.InsertColumn(1, "Filament", width = wx.LIST_AUTOSIZE_USEHEADER) | |
183 | self.populateList(spool_manager) | |
184 | ||
185 | # "Program" the layout | |
186 | self.Bind(wx.EVT_SIZE, self.onResizeList) | |
187 | ||
188 | def populateList(self, spool_manager): | |
189 | """Get the list of recorded spools from the Spool Manager.""" | |
190 | spool_list = spool_manager.getSpoolList() | |
191 | for i in range(len(spool_list)): | |
192 | self.Append(spool_list[i]) | |
193 | ||
194 | def refreshList(self, spool_manager): | |
195 | """Refresh the list by re-reading the Spool Manager list.""" | |
196 | self.DeleteAllItems() | |
197 | self.populateList(spool_manager) | |
198 | ||
199 | def onResizeList(self, event): | |
200 | list_size = self.GetSize() | |
201 | self.SetColumnWidth(1, -2) | |
202 | filament_column_width = self.GetColumnWidth(1) | |
203 | self.SetColumnWidth(col = 0, | |
204 | width = list_size.width - filament_column_width) | |
205 | event.Skip() | |
206 | ||
207 | ||
208 | class CurrentSpoolDialog(wx.Panel): | |
209 | """ | |
210 | Custom wxStaticText object to display the currently loaded spools and | |
211 | their remaining filament. | |
212 | """ | |
213 | ||
214 | def __init__(self, parent, spool_manager): | |
215 | wx.Panel.__init__(self, parent) | |
216 | self.parent = parent | |
217 | self.extruders = spool_manager.getExtruderCount() | |
218 | ||
219 | full_sizer = wx.BoxSizer(wx.VERTICAL) | |
220 | ||
221 | # Calculate the minimum size needed to properly display the | |
222 | # extruder information | |
223 | min_size = self.GetTextExtent(" Remaining filament: 0000000.00") | |
224 | ||
225 | # Generate a dialog for every extruder | |
226 | self.extruder_dialog = [] | |
227 | load_button = [] | |
228 | unload_button = [] | |
229 | button_sizer = [] | |
230 | dialog_sizer = [] | |
231 | for i in range(self.extruders): | |
232 | # Generate the dialog with the spool information | |
233 | self.extruder_dialog.append( | |
234 | wx.StaticText(self, style = wx.ST_ELLIPSIZE_END)) | |
235 | self.extruder_dialog[i].SetMinSize(wx.Size(min_size.width, -1)) | |
236 | ||
237 | # Generate the "load" and "unload" buttons | |
238 | load_button.append(wx.Button(self, label = "Load")) | |
239 | load_button[i].SetToolTip( | |
240 | "Load selected spool for Extruder %d" % i) | |
241 | unload_button.append(wx.Button(self, label = "Unload")) | |
242 | unload_button[i].SetToolTip( | |
243 | "Unload the spool for Extruder %d" % i) | |
244 | ||
245 | # "Program" the buttons | |
246 | load_button[i].Bind(wx.EVT_BUTTON, | |
247 | lambda event, extruder=i: parent.onClickLoad(event, extruder)) | |
248 | unload_button[i].Bind(wx.EVT_BUTTON, | |
249 | lambda event, extruder=i: parent.onClickUnload(event, extruder)) | |
250 | ||
251 | # Layout | |
252 | button_sizer.append(wx.BoxSizer(wx.VERTICAL)) | |
253 | button_sizer[i].Add(load_button[i], 0, | |
254 | wx.FIXED_MINSIZE | wx.ALIGN_CENTER) | |
255 | button_sizer[i].Add(unload_button[i], 0, | |
256 | wx.FIXED_MINSIZE | wx.ALIGN_CENTER) | |
257 | ||
258 | dialog_sizer.append(wx.BoxSizer(wx.HORIZONTAL)) | |
259 | dialog_sizer[i].Add(self.extruder_dialog[i], 1, wx.ALIGN_CENTER) | |
260 | dialog_sizer[i].AddSpacer(10) | |
261 | dialog_sizer[i].Add(button_sizer[i], 0, wx.EXPAND) | |
262 | ||
263 | full_sizer.Add(dialog_sizer[i], 0, wx.ALL | wx.EXPAND, 10) | |
264 | ||
265 | self.refreshDialog(spool_manager) | |
266 | ||
267 | self.SetSizerAndFit(full_sizer) | |
268 | ||
269 | ||
270 | def refreshDialog(self, spool_manager): | |
271 | """Retrieve the current spools from the Spool Manager.""" | |
272 | ||
273 | for i in range(self.extruders): | |
274 | spool_name = spool_manager.getSpoolName(i) | |
275 | spool_filament = spool_manager.getRemainingFilament(i) | |
276 | label = ("Spool for Extruder %d:\n" % i + | |
277 | " Name: %s\n" % spool_name + | |
278 | " Remaining filament: %.2f" % spool_filament) | |
279 | self.extruder_dialog[i].SetLabelText(label) | |
280 | ||
281 | ||
282 | # --------------------------------------------------------------------------- | |
283 | def checkOverwrite(parent, spool_name): | |
284 | """Ask the user whether or not to overwrite the existing spool.""" | |
285 | ||
286 | overwrite_dialog = wx.MessageDialog(parent, | |
287 | message = "A spool with the name '%s'' already exists." % | |
288 | spool_name + | |
289 | "Do you wish to overwrite it?", | |
290 | caption = "Overwrite", | |
291 | style = wx.YES_NO | wx.ICON_EXCLAMATION) | |
292 | ||
293 | if overwrite_dialog.ShowModal() == wx.ID_YES: | |
294 | return True | |
295 | else: | |
296 | return False | |
297 | ||
298 | def getFloat(parent, number): | |
299 | """ | |
300 | Check whether the input number is a float. Either return the number or | |
301 | return False. | |
302 | """ | |
303 | try: | |
304 | return float(number) | |
305 | except ValueError: | |
306 | parent.statusbar.SetStatusText("Unrecognized number: %s" % number) | |
307 | return False | |
308 | ||
309 | ||
310 | # --------------------------------------------------------------------------- | |
311 | class SpoolManagerAddWindow(wx.Frame): | |
312 | """Window for adding spools.""" | |
313 | ||
314 | def __init__(self, parent): | |
315 | ||
316 | wx.Frame.__init__(self, parent, | |
317 | title = "Add Spool", | |
318 | style = wx.DEFAULT_FRAME_STYLE | wx.FRAME_FLOAT_ON_PARENT) | |
319 | ||
320 | self.statusbar = self.CreateStatusBar() | |
321 | ||
322 | self.parent = parent | |
323 | ||
324 | self.SetIcon(parent.GetIcon()) | |
325 | ||
326 | # Generate the dialogs | |
327 | self.name_dialog = LabeledTextCtrl(self, | |
328 | "Name", "Default Spool", "") | |
329 | self.diameter_dialog = LabeledTextCtrl(self, | |
330 | "Diameter", "1.75", "mm") | |
331 | self.diameter_dialog.SetToolTip( | |
332 | "Typically, either 1.75 mm or 2.85 mm (a.k.a '3')") | |
333 | self.weight_dialog = LabeledTextCtrl(self, | |
334 | "Weight", "1", "Kg") | |
335 | self.density_dialog = LabeledTextCtrl(self, | |
336 | "Density", "1.25", "g/cm^3") | |
337 | self.density_dialog.SetToolTip( | |
338 | "Typical densities are 1.25 g/cm^3 for PLA and 1.08 g/cm^3 for" + | |
339 | " ABS") | |
340 | self.length_dialog = LabeledTextCtrl(self, | |
341 | "Length", "332601.35", "mm") | |
342 | ||
343 | # "Program" the dialogs | |
344 | self.diameter_dialog.Bind(wx.EVT_TEXT, self.calculateLength) | |
345 | self.weight_dialog.Bind(wx.EVT_TEXT, self.calculateLength) | |
346 | self.density_dialog.Bind(wx.EVT_TEXT, self.calculateLength) | |
347 | self.length_dialog.Bind(wx.EVT_TEXT, self.calculateWeight) | |
348 | ||
349 | # Generate the bottom buttons | |
350 | self.add_button = wx.Button(self, wx.ID_ADD) | |
351 | self.cancel_button = wx.Button(self, wx.ID_CANCEL) | |
352 | ||
353 | # "Program" the bottom buttons | |
354 | self.add_button.Bind(wx.EVT_BUTTON, self.onClickAdd) | |
355 | self.cancel_button.Bind(wx.EVT_BUTTON, self.onClickCancel) | |
356 | ||
357 | # Layout | |
358 | ## Group the bottom buttons | |
359 | self.bottom_buttons_sizer = wx.BoxSizer(wx.HORIZONTAL) | |
360 | self.bottom_buttons_sizer.Add(self.add_button, 0, wx.FIXED_MINSIZE) | |
361 | self.bottom_buttons_sizer.Add(self.cancel_button, 0, wx.FIXED_MINSIZE) | |
362 | ||
363 | ## Group the whole window | |
364 | self.full_sizer = wx.BoxSizer(wx.VERTICAL) | |
365 | self.full_sizer.Add(self.name_dialog, 0, | |
366 | wx.TOP | wx.BOTTOM | wx.EXPAND, 10) | |
367 | self.full_sizer.Add(self.diameter_dialog, 0, wx.EXPAND) | |
368 | self.full_sizer.Add(self.weight_dialog, 0, wx.EXPAND) | |
369 | self.full_sizer.Add(self.density_dialog, 0, wx.EXPAND) | |
370 | self.full_sizer.Add(self.length_dialog, 0, wx.EXPAND) | |
371 | self.full_sizer.Add(self.bottom_buttons_sizer, 0, | |
372 | wx.ALL | wx.ALIGN_CENTER_HORIZONTAL, 10) | |
373 | ||
374 | self.SetSizerAndFit(self.full_sizer) | |
375 | ||
376 | # Don't allow this window to be resized in height | |
377 | add_window_size = self.GetSize() | |
378 | self.SetMaxSize((-1, add_window_size.height)) | |
379 | ||
380 | def onClickAdd(self, event): | |
381 | """Add the new spool and close the window.""" | |
382 | ||
383 | spool_name = self.name_dialog.field.GetValue() | |
384 | spool_length = getFloat(self, self.length_dialog.field.GetValue()) | |
385 | ||
386 | # Check whether the length is actually a number | |
387 | if not spool_length: | |
388 | self.statusbar.SetStatusText( | |
389 | "ERROR: Unrecognized length: %s." % | |
390 | self.length_dialog.field.GetValue()) | |
391 | return -1 | |
392 | ||
393 | # The remaining filament should always be a positive number | |
394 | if not spool_length > 0: | |
395 | self.statusbar.SetStatusText( | |
396 | "ERROR: Length is zero or negative: %.2f." % spool_length) | |
397 | return -1 | |
398 | ||
399 | # Check whether the name is already used. If it is used, prompt the | |
400 | # user before overwriting it | |
401 | if self.parent.spool_manager.isListed(spool_name): | |
402 | if checkOverwrite(self, spool_name): | |
403 | # Remove the "will be overwritten" spool | |
404 | self.parent.spool_manager.remove(spool_name) | |
405 | else: | |
406 | return 0 | |
407 | ||
408 | # Add the new spool | |
409 | self.parent.spool_manager.add(spool_name, spool_length) | |
410 | self.parent.spool_list.refreshList(self.parent.spool_manager) | |
411 | self.parent.current_spools_dialog.refreshDialog( | |
412 | self.parent.spool_manager) | |
413 | self.parent.statusbar.SetStatusText( | |
414 | "Added new spool '%s'" % spool_name + | |
415 | " with %.2f mm of remaining filament." % spool_length) | |
416 | ||
417 | self.Close(True) | |
418 | ||
419 | def onClickCancel(self, event): | |
420 | """Do nothing and close the window.""" | |
421 | self.Close(True) | |
422 | self.parent.statusbar.SetStatusText("") | |
423 | ||
424 | def calculateLength(self, event): | |
425 | """ | |
426 | Calculate the length of the filament given the mass, diameter and | |
427 | density of the filament. Set the 'Length' field to this quantity. | |
428 | """ | |
429 | ||
430 | mass = getFloat(self, self.weight_dialog.field.GetValue()) | |
431 | diameter = getFloat(self, self.diameter_dialog.field.GetValue()) | |
432 | density = getFloat(self, self.density_dialog.field.GetValue()) | |
433 | if mass and diameter and density: | |
434 | pi = 3.14159265359 | |
435 | length = 4e6 * mass / pi / diameter**2 / density | |
436 | self.length_dialog.field.ChangeValue("%.2f" % length) | |
437 | self.statusbar.SetStatusText("") | |
438 | else: | |
439 | self.length_dialog.field.ChangeValue("---") | |
440 | ||
441 | def calculateWeight(self, event): | |
442 | """ | |
443 | Calculate the weight of the filament given the length, diameter and | |
444 | density of the filament. Set the 'Weight' field to this value. | |
445 | """ | |
446 | ||
447 | length = getFloat(self, self.length_dialog.field.GetValue()) | |
448 | diameter = getFloat(self, self.diameter_dialog.field.GetValue()) | |
449 | density = getFloat(self, self.density_dialog.field.GetValue()) | |
450 | if length and diameter and density: | |
451 | pi = 3.14159265359 | |
452 | mass = length * pi * diameter**2 * density / 4e6 | |
453 | self.weight_dialog.field.ChangeValue("%.2f" % mass) | |
454 | self.statusbar.SetStatusText("") | |
455 | else: | |
456 | self.weight_dialog.field.ChangeValue("---") | |
457 | ||
458 | ||
459 | class LabeledTextCtrl(wx.Panel): | |
460 | """ | |
461 | Group together a wxTextCtrl with a preceding and a subsequent wxStaticText. | |
462 | """ | |
463 | ||
464 | def __init__(self, parent, preceding_text, field_value, subsequent_text): | |
465 | wx.Panel.__init__(self, parent) | |
466 | self.pretext = wx.StaticText(self, label = preceding_text, | |
467 | style = wx.ALIGN_RIGHT) | |
468 | self.field = wx.TextCtrl(self, value = field_value) | |
469 | self.subtext = wx.StaticText(self, label = subsequent_text) | |
470 | ||
471 | # Layout the panel | |
472 | self.sizer = wx.BoxSizer(wx.HORIZONTAL) | |
473 | self.sizer.Add(self.pretext, 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 10) | |
474 | self.sizer.SetItemMinSize(self.pretext, (80, -1)) | |
475 | self.sizer.Add(self.field, 1, wx.EXPAND) | |
476 | self.sizer.Add(self.subtext, 0, wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 10) | |
477 | self.sizer.SetItemMinSize(self.subtext, (50, -1)) | |
478 | ||
479 | self.SetSizerAndFit(self.sizer) | |
480 | ||
481 | ||
482 | # --------------------------------------------------------------------------- | |
483 | class SpoolManagerEditWindow(wx.Frame): | |
484 | """Window for editing the name or the length of a spool.""" | |
485 | ||
486 | def __init__(self, parent, spool_name, spool_length): | |
487 | ||
488 | wx.Frame.__init__(self, parent, | |
489 | title = "Edit Spool", | |
490 | style = wx.DEFAULT_FRAME_STYLE | wx.FRAME_FLOAT_ON_PARENT) | |
491 | ||
492 | self.statusbar = self.CreateStatusBar() | |
493 | ||
494 | self.parent = parent | |
495 | ||
496 | self.SetIcon(parent.GetIcon()) | |
497 | ||
498 | self.old_spool_name = spool_name | |
499 | self.old_spool_length = getFloat(self, spool_length) | |
500 | ||
501 | # Set how many millimeters will the buttons add or subtract | |
502 | self.quantities = [-100.0, -50.0, -10.0, 10.0, 50.0, 100.0] | |
503 | ||
504 | # Generate the name field | |
505 | self.name_field = LabeledTextCtrl(self, | |
506 | "Name", self.old_spool_name, "") | |
507 | ||
508 | # Generate the length field and buttons | |
509 | self.length_title = wx.StaticText(self, label = "Remaining filament:") | |
510 | self.minus3_button = wx.Button(self, | |
511 | label = str(self.quantities[0]), style = wx.BU_EXACTFIT) | |
512 | self.minus2_button = wx.Button(self, | |
513 | label = str(self.quantities[1]), style = wx.BU_EXACTFIT) | |
514 | self.minus1_button = wx.Button(self, | |
515 | label = str(self.quantities[2]), style = wx.BU_EXACTFIT) | |
516 | self.length_field = wx.TextCtrl(self, | |
517 | value = str(self.old_spool_length)) | |
518 | self.plus1_button = wx.Button(self, | |
519 | label = "+" + str(self.quantities[3]), style = wx.BU_EXACTFIT) | |
520 | self.plus2_button = wx.Button(self, | |
521 | label = "+" + str(self.quantities[4]), style = wx.BU_EXACTFIT) | |
522 | self.plus3_button = wx.Button(self, | |
523 | label = "+" + str(self.quantities[5]), style = wx.BU_EXACTFIT) | |
524 | ||
525 | # "Program" the length buttons | |
526 | self.minus3_button.Bind(wx.EVT_BUTTON, self.changeLength) | |
527 | self.minus2_button.Bind(wx.EVT_BUTTON, self.changeLength) | |
528 | self.minus1_button.Bind(wx.EVT_BUTTON, self.changeLength) | |
529 | self.plus1_button.Bind(wx.EVT_BUTTON, self.changeLength) | |
530 | self.plus2_button.Bind(wx.EVT_BUTTON, self.changeLength) | |
531 | self.plus3_button.Bind(wx.EVT_BUTTON, self.changeLength) | |
532 | ||
533 | # Generate the bottom buttons | |
534 | self.save_button = wx.Button(self, wx.ID_SAVE) | |
535 | self.cancel_button = wx.Button(self, wx.ID_CANCEL) | |
536 | ||
537 | # "Program" the bottom buttons | |
538 | self.save_button.Bind(wx.EVT_BUTTON, self.onClickSave) | |
539 | self.cancel_button.Bind(wx.EVT_BUTTON, self.onClickCancel) | |
540 | ||
541 | # Layout | |
542 | ## Group the length field and its correspondent buttons | |
543 | self.length_sizer = wx.BoxSizer(wx.HORIZONTAL) | |
544 | self.length_sizer.Add(self.minus3_button, 0, | |
545 | wx.FIXED_MINSIZE | wx.ALIGN_CENTER) | |
546 | self.length_sizer.Add(self.minus2_button, 0, | |
547 | wx.FIXED_MINSIZE | wx.ALIGN_CENTER) | |
548 | self.length_sizer.Add(self.minus1_button, 0, | |
549 | wx.FIXED_MINSIZE | wx.ALIGN_CENTER) | |
550 | self.length_sizer.Add(self.length_field, 1, wx.EXPAND) | |
551 | self.length_sizer.Add(self.plus1_button, 0, | |
552 | wx.FIXED_MINSIZE | wx.ALIGN_CENTER) | |
553 | self.length_sizer.Add(self.plus2_button, 0, | |
554 | wx.FIXED_MINSIZE | wx.ALIGN_CENTER) | |
555 | self.length_sizer.Add(self.plus3_button, 0, | |
556 | wx.FIXED_MINSIZE | wx.ALIGN_CENTER) | |
557 | ||
558 | ## Group the bottom buttons | |
559 | self.bottom_buttons_sizer = wx.BoxSizer(wx.HORIZONTAL) | |
560 | self.bottom_buttons_sizer.Add(self.save_button, 0, wx.EXPAND) | |
561 | self.bottom_buttons_sizer.Add(self.cancel_button, 0, wx.EXPAND) | |
562 | ||
563 | ## Lay out the whole window | |
564 | self.full_sizer = wx.BoxSizer(wx.VERTICAL) | |
565 | self.full_sizer.Add(self.name_field, 0, wx.EXPAND) | |
566 | self.full_sizer.AddSpacer(10) | |
567 | self.full_sizer.Add(self.length_title, 0, | |
568 | wx.LEFT | wx.RIGHT | wx.EXPAND, 10) | |
569 | self.full_sizer.Add(self.length_sizer, 0, | |
570 | wx.LEFT | wx.RIGHT | wx.EXPAND, 10) | |
571 | self.full_sizer.AddSpacer(10) | |
572 | self.full_sizer.Add(self.bottom_buttons_sizer, 0, wx.ALIGN_CENTER) | |
573 | ||
574 | self.SetSizerAndFit(self.full_sizer) | |
575 | ||
576 | # Don't allow this window to be resized in height | |
577 | edit_window_size = self.GetSize() | |
578 | self.SetMaxSize((-1, edit_window_size.height)) | |
579 | ||
580 | def changeLength(self, event): | |
581 | new_length = getFloat(self, self.length_field.GetValue()) | |
582 | if new_length: | |
583 | new_length = new_length + float(event.GetEventObject().GetLabel()) | |
584 | self.length_field.ChangeValue("%.2f" % new_length) | |
585 | self.statusbar.SetStatusText("") | |
586 | ||
587 | def onClickSave(self, event): | |
588 | ||
589 | new_spool_name = self.name_field.field.GetValue() | |
590 | new_spool_length = getFloat(self, self.length_field.GetValue()) | |
591 | ||
592 | # Check whether the length is actually a number | |
593 | if not new_spool_length: | |
594 | self.statusbar.SetStatusText( | |
595 | "ERROR: Unrecognized length: %s." % | |
596 | self.length_field.GetValue()) | |
597 | return -1 | |
598 | ||
599 | if not new_spool_length > 0: | |
600 | self.statusbar.SetStatusText( | |
601 | "ERROR: Length is zero or negative: %.2f." % new_spool_length) | |
602 | return -1 | |
603 | ||
604 | # Check whether the "old" spool was loaded | |
605 | new_spool_extruder = self.parent.spool_manager.isLoaded( | |
606 | self.old_spool_name) | |
607 | ||
608 | # Check whether the name has changed | |
609 | if new_spool_name == self.old_spool_name: | |
610 | # Remove only the "old" spool | |
611 | self.parent.spool_manager.remove(self.old_spool_name) | |
612 | else: | |
613 | # Check whether the new name is already used | |
614 | if self.parent.spool_manager.isListed(new_spool_name): | |
615 | if checkOverwrite(self, new_spool_name): | |
616 | # Remove the "old" and the "will be overwritten" spools | |
617 | self.parent.spool_manager.remove(self.old_spool_name) | |
618 | self.parent.spool_manager.remove(new_spool_name) | |
619 | else: | |
620 | return 0 | |
621 | else: | |
622 | # Remove only the "old" spool | |
623 | self.parent.spool_manager.remove(self.old_spool_name) | |
624 | ||
625 | # Add "new" or edited spool | |
626 | self.parent.spool_manager.add(new_spool_name, new_spool_length) | |
627 | self.parent.spool_manager.load(new_spool_name, new_spool_extruder) | |
628 | self.parent.spool_list.refreshList(self.parent.spool_manager) | |
629 | self.parent.current_spools_dialog.refreshDialog( | |
630 | self.parent.spool_manager) | |
631 | self.parent.statusbar.SetStatusText( | |
632 | "Edited spool '%s'" % new_spool_name + | |
633 | " with %.2f mm of remaining filament." % new_spool_length) | |
634 | ||
635 | self.Close(True) | |
636 | ||
637 | def onClickCancel(self, event): | |
638 | self.Close(True) | |
639 | self.parent.statusbar.SetStatusText("") |