class MyFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, -1, DEFAULT_TITLE) self.settings = Settings(cmdFolder) self.materialSize = "" self.CreateStatusBar() self.SetStatusText("") self.images = Images(os.path.join(cmdFolder, "images")) ico = wx.Icon(os.path.join(cmdFolder, "images", "shapeoko.png"), wx.BITMAP_TYPE_PNG) self.SetIcon(ico) self.cnc = None self.material = None self.toolList = None self.pGrid = None self.objListBox = None self.bScrollUp = self.bScrollDown = None self.bMoveUp = self.bMoveDown = None self.fsWidth = self.fsHeight = self.fsThick = None self.bAdd = self.bDel = self.bRender = self.bNew = self.bLoad = self.bSave = self.bSaveAs = None self.setCnc() self.objectTypes = { "Rectangle": Rectangle, "Circle": Circle, "Arc": Arc, "Path": Path, "Polygon": Polygon, "Linear Drill": LinearDrill, "Circular Drill": CircularDrill, "Rectangular Drill": RectangularDrill, "Carve Grid": CarveGrid, "Carve Diamond": CarveDiamond } self.rawObjectTypes = { "Rectangle": Rectangle, "Circle": Circle, "Arc": Arc, "Path": Path, "Polygon": Polygon, "LinearDrill": LinearDrill, "CircularDrill": CircularDrill, "RectangularDrill": RectangularDrill, "CarveGrid": CarveGrid, "CarveDiamond": CarveDiamond } self.presets = Presets(cmdFolder) self.presetList = self.presets.getPresetList() self.fileMenu = None self.setMenu() self.t = 0 self.seq = 1 self.objList = [] self.modified = False self.currentFile = None szLeft = self.listAndButtonSizer() szRight = self.propertySizer() sz = wx.BoxSizer(wx.HORIZONTAL) sz.Add(szLeft) sz.Add(szRight, 1, wx.EXPAND) self.Bind(wx.EVT_CLOSE, self.onClose) self.SetSizer(sz) self.Layout() self.Fit() self.Show() self.msgTimer = 0 self.timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer) self.startTimer() def startTimer(self): self.timer.Start(1000) def stopTimer(self): self.timer.Stop() def OnTimer(self, _): if self.msgTimer > 0: self.msgTimer -= 1 if self.msgTimer <= 0: self.SetStatusText("") def setMenu(self): menuBar = wx.MenuBar() menu1 = wx.Menu() menu1.Append(MENU_FILE_NEW, "&New", "Create a new model file") menu1.Append(MENU_FILE_OPEN, "&Open", "Open an existing model file") menu1.Append(MENU_FILE_SAVE, "&Save", "Save the current model") menu1.Append(MENU_FILE_SAVEAS, "Sa&ve as", "Save the current model to a specified file") menu1.AppendSeparator() menu1.Append(MENU_FILE_SAVEEACH, "Save &each object", "Saves each object to a different specified file") menu1.AppendSeparator() menu1.Append(MENU_FILE_RENDER, "&Render", "Render the current model and create a G Code file") menu1.AppendSeparator() menu1.Append(MENU_FILE_EXIT, "E&xit", "Exit the application") menuBar.Append(menu1, "&File") self.fileMenu = menu1 self.fileMenu.Enable(MENU_FILE_SAVE, False) self.fileMenu.Enable(MENU_FILE_SAVEAS, False) self.fileMenu.Enable(MENU_FILE_SAVEEACH, False) self.fileMenu.Enable(MENU_FILE_RENDER, False) menu2 = wx.Menu() addOffset = 0 for ot in list(self.objectTypes.keys()): menu2.Append(MENU_ADD_BASE + addOffset, ot, "Add an object or type %s" % ot) self.Bind(wx.EVT_MENU, lambda evt, text=ot: self.onMenuAddObject(evt, text), id=MENU_ADD_BASE + addOffset) addOffset += 1 presetOffset = 0 presetCatOffset = 0 if len(self.presetList) > 0: menu2.AppendSeparator() presetMenu = wx.Menu() for cat in list(self.presetList.keys()): catMenu = wx.Menu() for lbl in list(self.presetList[cat].keys()): catMenu.Append(MENU_PRESET_BASE + presetOffset, lbl, "Add preset for %s: %s" % (cat, lbl)) self.Bind(wx.EVT_MENU, lambda evt, text=self.presetList[cat][lbl]: self.onMenuAddPreset(evt, text), id=MENU_PRESET_BASE + presetOffset) presetOffset += 1 presetMenu.AppendSubMenu(catMenu, cat) presetCatOffset += 1 menu2.AppendSubMenu(presetMenu, "Preset") menuBar.Append(menu2, "&Add") menu3 = wx.Menu() menu3.Append(MENU_SHAPEOKO_PROPERTIES, "&Properties", "Set shapeoko properties") menuBar.Append(menu3, "&Shapeoko") self.SetMenuBar(menuBar) self.Bind(wx.EVT_MENU, self.onBNew, id=MENU_FILE_NEW) self.Bind(wx.EVT_MENU, self.onBLoad, id=MENU_FILE_OPEN) self.Bind(wx.EVT_MENU, self.onBSave, id=MENU_FILE_SAVE) self.Bind(wx.EVT_MENU, self.onBSaveAs, id=MENU_FILE_SAVEAS) self.Bind(wx.EVT_MENU, self.onSaveEachObject, id=MENU_FILE_SAVEEACH) self.Bind(wx.EVT_MENU, self.onBRender, id=MENU_FILE_RENDER) self.Bind(wx.EVT_MENU, self.onClose, id=MENU_FILE_EXIT) self.Bind(wx.EVT_MENU, self.onShapeokoProperties, id=MENU_SHAPEOKO_PROPERTIES) def setTitle(self): t = DEFAULT_TITLE if self.currentFile is not None: t += " - %s" % self.currentFile if self.modified: t += " *" self.SetTitle(t) def setCnc(self): self.cnc = Shapeoko() self.loadTools() self.cnc.setTool(self.toolList[0]) self.material = Material({}) self.cnc.setMaterial(self.material) def loadTools(self): fn = os.path.join(cmdFolder, 'tools.json') try: with open(fn) as jfp: self.toolList = json.load(jfp) except (FileNotFoundError, PermissionError): print("Unable to open tools json file \"%s\"" % fn) self.toolList = [] return def setModified(self, flag=True): self.modified = flag self.bSave.Enable(flag) self.bSaveAs.Enable(len(self.objList) > 0) self.fileMenu.Enable(MENU_FILE_SAVE, flag) self.fileMenu.Enable(MENU_FILE_SAVEAS, flag) self.fileMenu.Enable(MENU_FILE_SAVEEACH, flag) self.setTitle() def warnIfModified(self): if not self.modified: return True dlg = wx.MessageDialog(self, 'Are you sure you want to continue?', 'Discard Changes?', wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION) rc = dlg.ShowModal() dlg.Destroy() if rc == wx.ID_NO: return False return True def message(self, text, duration=10): self.SetStatusText(text) self.msgTimer = duration def propertySizer(self): sz = wx.BoxSizer(wx.HORIZONTAL) sz.AddSpacer(20) self.pGrid = PropertiesGrid(self, self.images) sz.Add(self.pGrid, 1, wx.EXPAND) sz.AddSpacer(20) szProp = wx.BoxSizer(wx.VERTICAL) szProp.AddSpacer(20) szProp.Add(sz, 1, wx.EXPAND) szProp.AddSpacer(20) return szProp def listAndButtonSizer(self): sz = wx.BoxSizer(wx.VERTICAL) sz.AddSpacer(20) sz.Add(wx.StaticText(self, wx.ID_ANY, "Object List:")) hsz = wx.BoxSizer(wx.HORIZONTAL) self.objListBox = wx.ListBox(self, wx.ID_ANY, size=(200, 200)) self.Bind(wx.EVT_LISTBOX, self.onLbProp, self.objListBox) hsz.Add(self.objListBox, 1, wx.ALIGN_CENTER_HORIZONTAL) hsz.AddSpacer(10) vsz = wx.BoxSizer(wx.VERTICAL) bhsz = wx.BoxSizer(wx.HORIZONTAL) self.bScrollUp = wx.BitmapButton(self, wx.ID_ANY, self.images.getByName("scrollup"), size=BTNDIM) self.Bind(wx.EVT_BUTTON, self.onBScrollUp, self.bScrollUp) self.bScrollUp.SetToolTip("Move the selection cursor up 1 line") self.bScrollUp.Enable(False) bhsz.Add(self.bScrollUp) bhsz.AddSpacer(20) self.bMoveUp = wx.BitmapButton(self, wx.ID_ANY, self.images.getByName("moveup"), size=BTNDIM) self.Bind(wx.EVT_BUTTON, self.onBMoveUp, self.bMoveUp) self.bMoveUp.SetToolTip("Move the selected object up 1 line") self.bMoveUp.Enable(False) bhsz.Add(self.bMoveUp) vsz.Add(bhsz) vsz.AddSpacer(20) bhsz = wx.BoxSizer(wx.HORIZONTAL) self.bScrollDown = wx.BitmapButton(self, wx.ID_ANY, self.images.getByName("scrolldown"), size=BTNDIM) self.Bind(wx.EVT_BUTTON, self.onBScrollDown, self.bScrollDown) self.bScrollDown.SetToolTip("Move the selection cursor down 1 line") self.bScrollDown.Enable(False) bhsz.Add(self.bScrollDown) bhsz.AddSpacer(20) self.bMoveDown = wx.BitmapButton(self, wx.ID_ANY, self.images.getByName("movedown"), size=BTNDIM) self.Bind(wx.EVT_BUTTON, self.onBMoveDown, self.bMoveDown) self.bMoveDown.SetToolTip("Move the selected object down 1 line") self.bMoveDown.Enable(False) bhsz.Add(self.bMoveDown) vsz.Add(bhsz) hsz.Add(vsz) sz.Add(hsz) sz.AddSpacer(10) vsz = wx.BoxSizer(wx.VERTICAL) vsz.AddSpacer(5) box = wx.StaticBox(self, wx.ID_ANY, " Material Dimensions ") vbsizer = wx.StaticBoxSizer(box, wx.VERTICAL) bsizer = wx.BoxSizer(wx.HORIZONTAL) bsizer.AddSpacer(SBMARGIN) bsizer.Add(wx.StaticText(self, wx.ID_ANY, "Wd:"), 0, wx.TOP, 9) self.fsWidth = FS.FloatSpin(self, wx.ID_ANY, size=SPDIM, min_val=0, max_val=400, increment=0.1, value=self.material.getWidth(), agwStyle=FS.FS_RIGHT) self.fsWidth.SetFormat("%f") self.fsWidth.SetDigits(1) bsizer.Add(self.fsWidth, 0, wx.TOP+wx.BOTTOM, 5) self.fsWidth.Bind(FS.EVT_FLOATSPIN, self.onFSMaterialWidth) bsizer.AddSpacer(SBMARGIN) bsizer.Add(wx.StaticText(self, wx.ID_ANY, "Ht:"), 0, wx.TOP, 9) self.fsHeight = FS.FloatSpin(self, wx.ID_ANY, size=SPDIM, min_val=0, max_val=400, increment=0.1, value=self.material.getHeight(), agwStyle=FS.FS_RIGHT) self.fsHeight.SetFormat("%f") self.fsHeight.SetDigits(1) bsizer.Add(self.fsHeight, 0, wx.TOP+wx.BOTTOM, 5) self.fsHeight.Bind(FS.EVT_FLOATSPIN, self.onFSMaterialHeight) bsizer.AddSpacer(SBMARGIN) bsizer.Add(wx.StaticText(self, wx.ID_ANY, "Thk:"), 0, wx.TOP, 9) self.fsThick = FS.FloatSpin(self, wx.ID_ANY, size=SPDIM, min_val=0, max_val=50, increment=0.1, value=self.material.getThickness(), agwStyle=FS.FS_RIGHT) self.fsThick.SetFormat("%f") self.fsThick.SetDigits(1) bsizer.Add(self.fsThick, 0, wx.TOP+wx.BOTTOM, 5) self.fsThick.Bind(FS.EVT_FLOATSPIN, self.onFSMaterialThick) bsizer.AddSpacer(SBMARGIN) vbsizer.AddSpacer(SBMARGIN) vbsizer.Add(bsizer) vbsizer.AddSpacer(SBMARGIN) vsz.Add(vbsizer) sz.Add(vsz) sz.AddSpacer(10) bsz = wx.BoxSizer(wx.HORIZONTAL) self.bAdd = wx.BitmapButton(self, wx.ID_ANY, self.images.getByName("add"), size=BTNDIM) self.Bind(wx.EVT_BUTTON, self.onBAdd, self.bAdd) self.bAdd.SetToolTip("Add a new object to the bottom of the list") bsz.Add(self.bAdd) bsz.AddSpacer(10) self.bDel = wx.BitmapButton(self, wx.ID_ANY, self.images.getByName("delete"), size=BTNDIM) self.Bind(wx.EVT_BUTTON, self.onBDel, self.bDel) self.bDel.SetToolTip("Delete the selected object") self.bDel.Enable(False) bsz.Add(self.bDel) bsz.AddSpacer(10) self.bRender = wx.BitmapButton(self, wx.ID_ANY, self.images.getByName("render"), size=BTNDIM) self.Bind(wx.EVT_BUTTON, self.onBRender, self.bRender) self.bRender.SetToolTip("Render the G Code and save to file") self.bRender.Enable(False) bsz.Add(self.bRender) bsz.AddSpacer(10) self.bNew = wx.BitmapButton(self, wx.ID_ANY, self.images.getByName("new"), size=BTNDIM) self.Bind(wx.EVT_BUTTON, self.onBNew, self.bNew) self.bNew.SetToolTip("Create a new empty model") bsz.Add(self.bNew) bsz.AddSpacer(10) self.bLoad = wx.BitmapButton(self, wx.ID_ANY, self.images.getByName("open"), size=BTNDIM) self.Bind(wx.EVT_BUTTON, self.onBLoad, self.bLoad) self.bLoad.SetToolTip("Load a model from file") bsz.Add(self.bLoad) bsz.AddSpacer(10) self.bSave = wx.BitmapButton(self, wx.ID_ANY, self.images.getByName("save"), size=BTNDIM) self.Bind(wx.EVT_BUTTON, self.onBSave, self.bSave) self.bSave.SetToolTip("Save the model to the current file") self.bSave.Enable(False) bsz.Add(self.bSave) bsz.AddSpacer(10) self.bSaveAs = wx.BitmapButton(self, wx.ID_ANY, self.images.getByName("saveas"), size=BTNDIM) self.Bind(wx.EVT_BUTTON, self.onBSaveAs, self.bSaveAs) self.bSaveAs.SetToolTip("Save the model to a named file") self.bSaveAs.Enable(False) bsz.Add(self.bSaveAs) sz.AddSpacer(10) sz.Add(bsz, 0, wx.ALIGN_CENTER_HORIZONTAL) sz.AddSpacer(20) hsz = wx.BoxSizer(wx.HORIZONTAL) hsz.AddSpacer(20) hsz.Add(sz) return hsz def onLbProp(self, _): self.showObjectProperties() self.updateScrollButtons() def showObjectProperties(self): ox = self.objListBox.GetSelection() if ox == wx.NOT_FOUND: self.pGrid.clearProperties() else: self.pGrid.setProperties(self.objList[ox]) def onClose(self, _): if not self.warnIfModified(): return self.Destroy() def onBScrollUp(self, _): ox = self.objListBox.GetSelection() self.objListBox.SetSelection(ox-1) self.showObjectProperties() self.updateScrollButtons() def onBScrollDown(self, _): ox = self.objListBox.GetSelection() self.objListBox.SetSelection(ox+1) self.showObjectProperties() self.updateScrollButtons() def onBMoveUp(self, _): ox = self.objListBox.GetSelection() sobj = self.objList[ox] self.objList[ox] = self.objList[ox-1] self.objList[ox-1] = sobj self.rebuildObjectListBox() self.objListBox.SetSelection(ox-1) self.updateScrollButtons() def onBMoveDown(self, _): ox = self.objListBox.GetSelection() sobj = self.objList[ox] self.objList[ox] = self.objList[ox+1] self.objList[ox+1] = sobj self.rebuildObjectListBox() self.objListBox.SetSelection(ox+1) self.updateScrollButtons() def updateScrollButtons(self): l = len(self.objList) ox = self.objListBox.GetSelection() if l <= 1 or ox == wx.NOT_FOUND: self.bScrollUp.Enable(False) self.bScrollDown.Enable(False) self.bMoveUp.Enable(False) self.bMoveDown.Enable(False) return self.bScrollUp.Enable(not(ox == 0)) self.bMoveUp.Enable(not(ox == 0)) self.bScrollDown.Enable(not (ox == (l-1))) self.bMoveDown.Enable(not (ox == (l-1))) def onMenuAddObject(self, _, objType): self.doObjectAddType(objType) def onMenuAddPreset(self, _, fn): path = os.path.join(cmdFolder, "presets", fn) try: with open(path, 'r') as f: j = json.load(f) obj = self.makeCncObject(j) self.doObjectAdd(obj) except IOError: self.message("Cannot read preset file '%s'." % path) def onFSMaterialWidth(self, _): nw = self.fsWidth.GetValue() self.material.setWidth(nw) def onFSMaterialHeight(self, _): nh = self.fsHeight.GetValue() self.material.setHeight(nh) def onFSMaterialThick(self, _): nt = self.fsThick.GetValue() self.material.setThickness(nt) def onBAdd(self, _): dlg = wx.SingleChoiceDialog( self, 'Choose the type of object to add', 'Add New Object', list(self.objectTypes.keys()), wx.OK | wx.CANCEL ) rc = dlg.ShowModal() objType = None if rc == wx.ID_OK: objType = dlg.GetStringSelection() dlg.Destroy() if rc != wx.ID_OK: return self.doObjectAddType(objType) def doObjectAddType(self, objType): obj = self.objectTypes[objType](self, {}) el = obj.getErrors() if not el is None: for e in el: print(e) self.doObjectAdd(obj) def doObjectAdd(self, obj): self.setModified() self.pGrid.setProperties(obj) self.objListBox.Append(obj.getTitle()) self.objList.append(obj) self.objListBox.SetSelection(len(self.objList)-1) self.bDel.Enable(True) self.bRender.Enable(True) self.fileMenu.Enable(MENU_FILE_RENDER, True) self.updateScrollButtons() def updateLabel(self): ox = self.objListBox.GetSelection() if ox == wx.NOT_FOUND: return self.rebuildObjectListBox() def rebuildObjectListBox(self): self.objListBox.Clear() for obj in self.objList: self.objListBox.Append(obj.getTitle()) def onBDel(self, _): ox = self.objListBox.GetSelection() if ox == wx.NOT_FOUND: return ot = self.objList[ox].getTitle() dlg = wx.MessageDialog(self, "Are you sure you want to delete\n\n \"%s\"?" % ot, 'Confirm Object Deletion', wx.ICON_QUESTION | wx.YES_NO ) rc = dlg.ShowModal() dlg.Destroy() if rc != wx.ID_YES: return self.setModified() del(self.objList[ox]) self.rebuildObjectListBox() if ox >= len(self.objList): ox = len(self.objList)-1 if ox >= 0: self.objListBox.SetSelection(ox) else: self.bDel.Enable(False) self.bSave.Enable(False) self.bSaveAs.Enable(False) self.bRender.Enable(False) self.fileMenu.Enable(MENU_FILE_SAVE, False) self.fileMenu.Enable(MENU_FILE_SAVEAS, False) self.fileMenu.Enable(MENU_FILE_SAVEEACH, False) self.fileMenu.Enable(MENU_FILE_RENDER, False) self.showObjectProperties() self.updateScrollButtons() def onBRender(self, _): dlg = RenderDlg(self, self.objList, self.cnc, self.toolList, self.images, self.settings) dlg.ShowModal() dlg.Destroy() def onBSave(self, evt): if self.currentFile is None: return self.onBSaveAs(evt) self.saveFile(self.currentFile) def saveFile(self, fn): j = [o.toJson() for o in self.objList] j.append(self.material.toJson()) try: with open(fn, 'w') as f: f.write(json.dumps(j, indent=2)) self.message("Model saved to file '%s'" % fn) except IOError: self.message("Cannot write current model file '%s'." % fn) self.setModified(False) def onBSaveAs(self, _): skey = "modeldir" sdir = self.settings.setting(skey) with wx.FileDialog(self, "Save Model file", wildcard="JSON files (*.json)|*.json", defaultDir=sdir, style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) as fileDialog: if fileDialog.ShowModal() == wx.ID_CANCEL: return # the user changed their mind self.currentFile = fileDialog.GetPath() ndir = os.path.dirname(self.currentFile) if ndir != sdir: self.settings.setSetting(skey, ndir) self.saveFile(self.currentFile) def onSaveEachObject(self, _): skey = "objectdir" sdir = self.settings.setting(skey) for o in self.objList: lbl = o.getLabel() with wx.FileDialog(self, "Save Object '%s' to file" % lbl, wildcard="JSON files (*.json)|*.json", defaultDir = sdir, style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) as fileDialog: if fileDialog.ShowModal() == wx.ID_CANCEL: continue # the user changed their mind # save the current object in the file fn = fileDialog.GetPath() ndir = os.path.dirname(fn) if ndir != sdir: self.settings.setSetting(skey, ndir) j = o.toJson() try: with open(fn, 'w') as f: f.write(json.dumps(j, indent=2)) self.message("Object '%s' saved to file '%s'" % (lbl, fn)) except IOError: self.message("Cannot write current object '%s' to file '%s'." % (lbl, fn)) def onBLoad(self, evt): if not self.warnIfModified(): return skey = "modeldir" sdir = self.settings.setting(skey) with wx.FileDialog(self, "Open Model file", wildcard="JSON files (*.json)|*.json", defaultDir=sdir, style=wx.FD_OPEN) as fileDialog: if fileDialog.ShowModal() == wx.ID_CANCEL: return # the user changed their mind # load the current contents in the file fn = fileDialog.GetPath() ndir = os.path.dirname(fn) if ndir != sdir: self.settings.setSetting(skey, ndir) try: with open(fn, 'r') as f: j = json.load(f) self.currentFile = fn except IOError: self.message("Cannot read from input model file '%s'." % fn) return self.onBNew(evt) self.objList = [] self.objListBox.Clear() for o in j: obj = self.makeCncObject(o) if obj is None: continue self.pGrid.setProperties(obj) self.objListBox.Append(obj.getTitle()) self.objList.append(obj) self.objListBox.SetSelection(len(self.objList)-1) if len(self.objList) > 0: self.bDel.Enable(True) else: self.bDel.Enable(False) self.bRender.Enable(True) self.fileMenu.Enable(MENU_FILE_RENDER, False) self.updateScrollButtons() self.setModified(False) def makeCncObject(self, o): k = list(o.keys())[0] params = o[k] if k == "Material": self.material = Material(params) self.fsWidth.SetValue(self.material.getWidth()) self.fsHeight.SetValue(self.material.getHeight()) self.fsThick.SetValue(self.material.getThickness()) self.cnc.setMaterial(self.material) return None if k not in self.rawObjectTypes.keys(): print("Unknown object type: (%s)" % k) return None obj = self.rawObjectTypes[k](self, params) el = obj.getErrors() if not el is None: print("errors in makecncobject") for e in el: print(e) return obj def onBNew(self, _): if not self.warnIfModified(): return self.objList = [] self.objListBox.Clear() self.pGrid.clearProperties() self.currentFile = None self.updateScrollButtons() self.setModified(False) def onShapeokoProperties(self, _): dlg = ShapeokoDlg(self, self.cnc, self.images) dlg.ShowModal() dlg.Destroy()
class MyFrame(wx.Frame): def __init__(self): self.destroyed = False self.t = 0 self.seq = 1 self.images = Images(os.path.join(cmdFolder, "images")) self.toolicons = Images(os.path.join(cmdFolder, "images", "tools")) self.printerList = [] self.settings = Settings(cmdFolder) wx.Frame.__init__(self, None, -1, "Octoprint Monitor", size=(300, 300)) self.Bind(wx.EVT_CLOSE, self.onClose) ico = wx.Icon(os.path.join(cmdFolder, "images", "octomon.png"), wx.BITMAP_TYPE_PNG) self.SetIcon(ico) sz = wx.BoxSizer(wx.VERTICAL) sz.AddSpacer(20) box = wx.StaticBox(self, -1, "Printers") bsizer = wx.StaticBoxSizer(box, wx.VERTICAL) self.buttons = {} self.dialogs = {} self.plist = self.settings.getSetting("printers") for pn in self.plist: b = wx.Button(self, wx.ID_ANY, pn) self.Bind(wx.EVT_BUTTON, self.pressPrinter, b) bsizer.Add(b, 0, wx.ALL, 5) self.buttons[pn] = b sz.Add(bsizer) sz.AddSpacer(10) self.tbx = ToolBox(cmdFolder) tools = self.tbx.getTools() for s in tools.keys(): box = wx.StaticBox(self, wx.ID_ANY, s) bxsz = wx.StaticBoxSizer(box, wx.HORIZONTAL) try: ordr = tools[s]["order"] except KeyError: ordr = list(tools[s].keys()) for t in ordr: i = tools[s][t]["icon"] png = self.toolicons.getByName(i) b = wx.BitmapButton(self, wx.ID_ANY, png, size=BTNDIM) bxsz.Add(b, 0, wx.ALL, 5) b.SetToolTip(tools[s][t]["helptext"]) l = lambda evt, section=s, tool=t: self.onToolButton( evt, section, tool) self.Bind(wx.EVT_BUTTON, l, b) sz.Add(bxsz) sz.AddSpacer(10) hsz = wx.BoxSizer(wx.HORIZONTAL) hsz.AddSpacer(20) hsz.Add(sz) hsz.AddSpacer(20) hsz.Fit(self) self.SetSizer(hsz) self.Layout() self.Fit() self.enableButtons() self.printerInstances = PrinterInstances(self.plist, self.settings, self.registerPrinter) self.Bind(EVT_REGISTER, self.printerRegistration) self.Show() def onToolButton(self, _, section, tool): self.tbx.execute(section, tool) def enableButtons(self): for pn in self.plist: self.buttons[pn].Enable(pn in self.printerList and pn not in self.dialogs.keys()) def pressPrinter(self, evt): pName = evt.GetEventObject().GetLabel() if pName in self.dialogs.keys(): return try: svr = self.printerInstances.getPrinterServer(pName) except: dlg = wx.MessageDialog( self, "Unable to obtain printer server for %s" % pName, "Printer Initialization Error", wx.OK | wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return try: rc, json = svr.state(exclude=["temperature", "sd"]) except: dlg = wx.MessageDialog( self, "Unable to obtain initial printer state from %s" % pName, "Printer Initialization Error", wx.OK | wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return d = PrinterDlg(self, svr, pName, self.settings, self.images) self.dialogs[pName] = d self.enableButtons() d.Show() def registerPrinter(self, action, pName): if self.destroyed: print( " printer registration after window has been destroyed - terminating thread" ) exit(0) evt = RegisterEvent(action=action, pname=pName) wx.PostEvent(self, evt) def printerRegistration(self, evt): action = evt.action pName = evt.pname if action == "add": if pName in self.printerList: pass else: self.printerList.append(pName) elif action == "remove": if pName not in self.printerList: pass else: self.printerList.remove(pName) if pName in self.dialogs.keys(): self.dialogs[pName].sever() self.dialogs[pName].Destroy() del self.dialogs[pName] self.enableButtons() def onClose(self, _): if self.destroyed: return self.destroyed = True for p in self.dialogs.keys(): self.dialogs[p].sever() self.dialogs[p].Destroy() self.printerInstances.close() self.Destroy() def connectionSevered(self, pName): if pName in self.dialogs.keys(): self.dialogs[pName].sever() self.dialogs[pName].Destroy() del self.dialogs[pName] self.enableButtons()