Module skeinforge_engine_gui
|
|
1
2
3 from threading import Thread
4 import ConfigParser
5 import logging
6 import os
7 import skeinforge_engine
8 import sys
9 import shutil
10 import StringIO
11 try:
12 import wx
13 except:
14 print "WX is not installed. This program requires WX to run."
15 raise
16
17 import printrun_utilities.gviz as gviz
18 import printrun_utilities.SimpleEditor as SimpleEditor
19
20 guiConfig = ConfigParser.ConfigParser(allow_no_value=True)
21 guiConfigFilename="skeinforge_engine_gui.cfg"
22 runtimeConfig = ConfigParser.ConfigParser(allow_no_value=True)
23 runtimeConfigFilename="skeinforge_engine_gui.runtime"
24 engineConfig = ConfigParser.ConfigParser(allow_no_value=True)
25 engineConfigFilename="skeinforge_engine.cfg"
26
29 kwds["style"] = wx.DEFAULT_DIALOG_STYLE | wx.MAXIMIZE_BOX | wx.MINIMIZE_BOX | wx.RESIZE_BORDER
30 wx.Frame.__init__(self, *args, **kwds)
31 self.SetIcon(wx.Icon("SkeinforgeEngine.ico", wx.BITMAP_TYPE_ICO))
32 self.SetBackgroundColour(wx.WHITE)
33 self.dialogs = []
34 self.lastProfileName = runtimeConfig.get('runtime', 'last.profile')
35 self.lastShowGcode = runtimeConfig.getboolean('runtime', 'last.show.gcode')
36 self.openGcodeFilesVisualisation = guiConfig.getboolean('general', 'open.gcode.files.visualisation')
37 self.lastProfileIndex = 0
38
39 lastPath = runtimeConfig.get('runtime','last.path')
40 self.fileTxt = wx.TextCtrl(self, -1, lastPath, size=(300, -1))
41
42 self.logTxtCtrl = wx.TextCtrl(self, -1, "", style=wx.TE_MULTILINE | wx.TE_READONLY | wx.HSCROLL, size=(-1, 200))
43
44 self.profilesDirectory = guiConfig.get('general', 'profiles.location')
45
46 self.profilesCB = wx.ComboBox(self, -1, size=(300, -1), style=wx.CB_READONLY)
47 self.profilesCB.SetSelection(self.lastProfileIndex)
48
49 if not os.path.exists(self.profilesDirectory):
50 self.logTxtCtrl.WriteText("Invalid profiles directory: %s" % self.profilesDirectory)
51 else:
52 self.updateProfileList()
53
54 self.profileLbl = wx.StaticText(self, -1, "Profile", size=(-1, -1))
55 self.fileLbl = wx.StaticText(self, -1, "File", size=(-1, -1))
56 self.logLbl = wx.StaticText(self, -1, "Log", size=(-1, -1))
57
58 self.fileBtn = wx.Button(self, -1, "&Open...", size=(100, -1))
59 self.skeinBtn = wx.Button(self, -1, "&Skein", size=(100, -1))
60 self.editProfileBtn = wx.Button(self, -1, "Edit...", size=(100, -1))
61
62 self.editProfileBtn.Enable(self.profilesCB.GetSelection() > 0)
63
64 self.showGcodeCheckBox = wx.CheckBox(self, -1, 'Show Gcode', (10, 10))
65 self.showGcodeCheckBox.SetValue(self.lastShowGcode)
66
67 self.Bind(wx.EVT_BUTTON, self.onOpenFile, self.fileBtn)
68 self.Bind(wx.EVT_BUTTON, self.onEditProfile, self.editProfileBtn)
69 self.Bind(wx.EVT_BUTTON, self.onSkein, self.skeinBtn)
70
71 self.Bind(wx.EVT_COMBOBOX, self.onProfileChange, self.profilesCB)
72 self.Bind(wx.EVT_CHECKBOX, self.onShowGcodeChange, self.showGcodeCheckBox)
73
74 self.Bind(wx.EVT_CLOSE, self.onExit)
75
76 skein_id = wx.NewId()
77 self.Bind(wx.EVT_MENU, self.onSkein, id=skein_id)
78
79 openFile_id = wx.NewId()
80 self.Bind(wx.EVT_MENU, self.onOpenFile, id=openFile_id)
81
82 self.accel_tbl = wx.AcceleratorTable([(wx.ACCEL_CTRL, ord('S'), skein_id), (wx.ACCEL_CTRL, ord('O'), openFile_id)])
83
84 self.SetAcceleratorTable(self.accel_tbl)
85
86 self.skeinforgeEngine = skeinforge_engine
87 logHandler = WxLog(self.logTxtCtrl)
88 logHandler.setLevel(logging.getLevelName(guiConfig.get('general','skeinforge.engine.log.handler.level')))
89 self.skeinforgeEngine.logger.addHandler(logHandler)
90
91 self.prepMenu()
92
93 self.__set_properties()
94 self.__do_layout()
95
97 self.profilesCB.Clear()
98 self.profilesCB.Append("None (Default)")
99 index = 1
100 self.lastProfileIndex = None
101 for f in os.listdir(self.profilesDirectory):
102 profilePath = os.path.join(self.profilesDirectory, f)
103 if os.path.isfile(profilePath):
104 if f == self.lastProfileName:
105 self.lastProfileIndex = index
106 self.profilesCB.Append(f)
107 index += 1
108 if self.lastProfileIndex == None:
109 self.lastProfileIndex = 0
110 self.profilesCB.SetSelection(self.lastProfileIndex)
111
113 self.SetTitle("SkeinforgeEngine")
114
116 mainSizer = wx.GridBagSizer(hgap=5, vgap=5)
117 mainSizer.AddGrowableCol(1, 0)
118 mainSizer.AddGrowableRow(2, 0)
119 mainSizer.Add(self.fileLbl, pos=(0, 0), flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=5)
120 mainSizer.Add(self.fileTxt, pos=(0, 1), flag=wx.ALL | wx.EXPAND, border=5)
121 mainSizer.Add(self.fileBtn, pos=(0, 2), flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=5)
122
123 mainSizer.Add(self.profileLbl, pos=(1, 0), flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=5)
124 mainSizer.Add(self.profilesCB, pos=(1, 1), flag=wx.ALL | wx.EXPAND, border=5)
125 mainSizer.Add(self.editProfileBtn, pos=(1, 2), flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=5)
126
127 mainSizer.Add(self.logLbl, pos=(2, 0), flag=wx.ALL | wx.ALIGN_TOP, border=5)
128 mainSizer.Add(self.logTxtCtrl, pos=(2, 1), span=wx.GBSpan(1, 2), flag=wx.EXPAND | wx.ALL, border=5)
129
130
131 mainSizer.Add(self.showGcodeCheckBox, pos=(3, 1), flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=5)
132 mainSizer.Add(self.skeinBtn, pos=(3, 2), flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=5)
133
134 self.SetSizer(mainSizer)
135 self.SetMinSize((500, 400))
136 mainSizer.Fit(self)
137 self.Center()
138 self.Layout()
139
141 self.menustrip = wx.MenuBar()
142
143 fileMenu = wx.Menu()
144 self.Bind(wx.EVT_MENU, self.onOpenFile, fileMenu.Append(-1, "&Open...", " Opens file"))
145 self.Bind(wx.EVT_MENU, self.onClearOutput, fileMenu.Append(-1, "Clear log", " Clear log"))
146 self.Bind(wx.EVT_MENU, self.onExit, fileMenu.Append(wx.ID_EXIT, "E&xit", " Closes the Window"))
147 self.menustrip.Append(fileMenu, "&File")
148
149 profilesMenu = wx.Menu()
150 self.Bind(wx.EVT_MENU, self.onEditProfile, profilesMenu.Append(-1, "&Edit...", " Edit currently selected profile"))
151 self.Bind(wx.EVT_MENU, self.onCopyProfile, profilesMenu.Append(-1, "&Copy", " Make a new copy of the currently selected profile"))
152 self.Bind(wx.EVT_MENU, self.onRefreshProfilesList, profilesMenu.Append(-1, "&Refresh", " Refresh list of profiles"))
153 self.Bind(wx.EVT_MENU, self.onViewEffectiveProfile, profilesMenu.Append(-1, "&View effective profile", " View the complete profile the engine would use"))
154 self.Bind(wx.EVT_MENU, self.onCopyEffectiveProfile, profilesMenu.Append(-1, "Copy effective profile", " Make a new copy of the currently effective profile"))
155 self.menustrip.Append(profilesMenu, "&Profiles")
156
157 configMenu = wx.Menu()
158 self.Bind(wx.EVT_MENU, self.onEditEngineConfig, configMenu.Append(-1, "&Edit skeinforge_engine.cfg...", " Edit engine config"))
159 self.Bind(wx.EVT_MENU, self.onEditEngineGuiConfig, configMenu.Append(-1, "&Edit skeinforge_engine_gui.cfg...", " Edit engine gui config"))
160 self.menustrip.Append(configMenu, "&Config")
161
162 self.SetMenuBar(self.menustrip)
163
167
169 effectiveConfigString = StringIO.StringIO()
170 for section in effectiveConfig.sections():
171 effectiveConfigString.write("[%s]\n" % section)
172 for option in effectiveConfig.options(section):
173 value = effectiveConfig.get(section, option)
174 effectiveConfigString.write("%s=%s\n" % (option, value))
175 effectiveConfigString.write("\n")
176 return effectiveConfigString
177
179 defaultProfile = engineConfig.get('general', 'default.profile')
180 effectiveConfig = ConfigParser.ConfigParser(allow_no_value=True)
181 effectiveConfig.read(defaultProfile)
182
183 profileSelectionIndex = self.profilesCB.GetSelection()
184 if profileSelectionIndex > 0:
185 profileName = self.profilesCB.GetString(profileSelectionIndex).encode()
186 profilePath = os.path.join(self.profilesDirectory, profileName)
187 effectiveConfig.read(profilePath)
188 return effectiveConfig
189
191 profileSelectionIndex = self.profilesCB.GetSelection()
192 if profileSelectionIndex > 0:
193 profileName = self.profilesCB.GetString(profileSelectionIndex).encode()
194 else:
195 profileName = "default.profile"
196 dlg = wx.TextEntryDialog(self, 'New profile name:', 'Copy Effective Profile', "%s.copy" % profileName)
197 txtCtrl = dlg.FindWindowById(3000)
198 txtCtrl.Validator = NewFileNameValidator(self.profilesDirectory, dlg)
199 if dlg.ShowModal() == wx.ID_OK:
200 newProfileName = txtCtrl.GetValue()
201 newProfilePath = os.path.join(self.profilesDirectory, newProfileName)
202 try:
203 fd = os.open(newProfilePath, os.O_RDWR | os.O_CREAT)
204 f = os.fdopen(fd, "w+")
205 effectiveProfile = self.getEffectiveProfile()
206 effectiveProfile.set('profile', 'name', newProfileName)
207 f.write (self.getEffectiveProfileString(effectiveProfile).getvalue())
208 f.close()
209 self.lastProfileName = newProfileName
210 self.updateProfileList()
211 self.GetEventHandler().ProcessEvent(wx.PyCommandEvent(wx.EVT_COMBOBOX.typeId, self.profilesCB.GetId()))
212 except (IOError, os.error), why:
213 msgDlg = wx.MessageDialog(dlg, 'Unable to copy effective profile: %s.' % str(why), 'Error', wx.OK | wx.ICON_ERROR)
214 msgDlg.ShowModal()
215 msgDlg.Destroy()
216 dlg.Destroy()
217
219 self.editFile('skeinforge_engine_gui.cfg')
220
222 self.editFile('skeinforge_engine.cfg')
223
225 '''Opens the profile for editing.'''
226 profileSelectionIndex = self.profilesCB.GetSelection()
227 if profileSelectionIndex < 1:
228 return
229 profileName = self.profilesCB.GetString(profileSelectionIndex).encode()
230 profilePath = os.path.join(self.profilesDirectory, profileName)
231 self.editFile(profilePath)
232
234 f2 = open(filePath)
235 def saveFileCallback(text):
236 f = open(filePath, 'w+')
237 if len(text.strip()) == 0:
238 return
239 f.write(text)
240 f.close()
241 se = SimpleEditor(os.path.basename(filePath), f2.read(), saveFileCallback)
242 self.dialogs.append(se)
243
245 profileSelectionIndex = self.profilesCB.GetSelection()
246 if profileSelectionIndex < 1:
247 return
248 profileName = self.profilesCB.GetString(profileSelectionIndex).encode()
249 profilePath = os.path.join(self.profilesDirectory, profileName)
250 dlg = wx.TextEntryDialog(self, 'New profile name:', 'Copy Profile', "%s.copy" % profileName)
251
252 txtCtrl = dlg.FindWindowById(3000)
253 txtCtrl.Validator = NewFileNameValidator(self.profilesDirectory, dlg)
254 if dlg.ShowModal() == wx.ID_OK:
255 newProfileName = txtCtrl.GetValue()
256 newProfilePath = os.path.join(self.profilesDirectory, newProfileName)
257 try:
258 shutil.copyfile(profilePath, newProfilePath)
259 self.lastProfileName = newProfileName
260 self.updateProfileList()
261 self.GetEventHandler().ProcessEvent(wx.PyCommandEvent(wx.EVT_COMBOBOX.typeId, self.profilesCB.GetId()))
262 except (IOError, os.error), why:
263 msgDlg = wx.MessageDialog(dlg, 'Unable to copy profile: %s.' % str(why), 'Error', wx.OK | wx.ICON_ERROR)
264 msgDlg.ShowModal()
265 msgDlg.Destroy()
266 dlg.Destroy()
267
269 for d in self.dialogs:
270 try:
271 d.Destroy()
272 except:
273 pass
274 self.Destroy()
275
277 self.updateProfileList()
278 self.GetEventHandler().ProcessEvent(wx.PyCommandEvent(wx.EVT_COMBOBOX.typeId, self.profilesCB.GetId()))
279
281 self.logTxtCtrl.Clear()
282
284 '''Calls skeinforge_engine and optionally shows the created gcode afterwards.'''
285 if self.fileTxt.GetValue() == "":
286 self.logTxtCtrl.WriteText("No file given.\n")
287 return
288
289 args = []
290 profileSelectionIndex = self.profilesCB.GetSelection()
291
292 if profileSelectionIndex > 0:
293 profileName = self.profilesCB.GetString(profileSelectionIndex).encode()
294 profilePath = os.path.join(self.profilesDirectory, profileName)
295 args.append("-p")
296 args.append(profilePath)
297
298 args.append(self.fileTxt.GetValue().encode())
299
300 self.logTxtCtrl.WriteText("Skeining...\n")
301
302 slicedModel = self.skeinforgeEngine.main(args)
303
304 if self.showGcodeCheckBox.GetValue():
305 if slicedModel.runtimeParameters.outputFilename == None:
306 self.logTxtCtrl.WriteText("Unable to find output filename in sliced model. Cannot preview Gcode.\n")
307 else:
308 self.showGcodeVisualisation(slicedModel.runtimeParameters.outputFilename)
309
310 self.logTxtCtrl.WriteText("Done!\n\n")
311
313 gwindow = gviz.window([], title=os.path.basename(filename))
314 self.dialogs.append(gwindow)
315 f = open(filename)
316 for i in f:
317 gwindow.p.addgcode(i)
318 gwindow.Show()
319
321 '''Shows the file choice dialog.'''
322 if runtimeConfig.get('runtime', 'last.path') != None:
323 startFolder = os.path.dirname(runtimeConfig.get('runtime', 'last.path'))
324 else:
325 startFolder = os.getcwd()
326 dlg = wx.FileDialog(self, "Choose a file", startFolder, "", "*.*", wx.OPEN)
327 if dlg.ShowModal() == wx.ID_OK:
328 path = dlg.GetPath()
329
330 if path.endswith('.gcode') and self.openGcodeFilesVisualisation:
331 self.showGcodeVisualisation(path)
332 else:
333 self.saveRuntimeParameter('last.path', path)
334 self.fileTxt.SetValue(path)
335 dlg.Destroy()
336
338 self.lastProfileIndex = self.profilesCB.GetSelection()
339 self.editProfileBtn.Enable(self.lastProfileIndex > 0)
340 self.lastProfileName = self.profilesCB.GetString(self.lastProfileIndex).encode()
341 if self.lastProfileIndex > 0:
342 self.saveRuntimeParameter('last.profile', self.lastProfileName)
343 else:
344 self.saveRuntimeParameter('last.profile', '')
345
348
354
355 -class WxLog(logging.Handler):
357 logging.Handler.__init__(self)
358 self.ctrl = ctrl
359 - def emit(self, record):
360 self.ctrl.AppendText(self.format(record) + "\n")
361 self.ctrl.Update()
362
364 - def __init__(self, directory, parentDialog):
365 wx.PyValidator.__init__(self)
366 self.directory = directory
367 self.parentDialog = parentDialog
368
370 '''Every validator must implement Clone() method!'''
371 return NewFileNameValidator(self.directory, self.parentDialog)
372
374 txtCtrl = self.Window
375 newFilename = txtCtrl.GetValue()
376 newPath = os.path.join(self.directory, newFilename)
377 if os.path.exists(newPath):
378 msgDlg = wx.MessageDialog(self.parentDialog, 'File already exists: %s' % newPath, 'Error', wx.OK | wx.ICON_ERROR)
379 msgDlg.ShowModal()
380 msgDlg.Destroy()
381 return False
382 else:
383 return True
384
386 return True
387
389 return True
390
393 wx.InitAllImageHandlers()
394 frame_1 = GuiFrame(None, -1, "")
395 self.SetTopWindow(frame_1)
396 frame_1.Show()
397 return 1
398
399 if __name__ == "__main__":
400 guiConfig.read(guiConfigFilename)
401 runtimeConfig.read(runtimeConfigFilename)
402 engineConfig.read(engineConfigFilename)
403 skeinforgeEngineGui = SkeinforgeEngineGui(0)
404 skeinforgeEngineGui.MainLoop()
405