From: Daid Date: Mon, 5 Mar 2012 21:30:54 +0000 (+0100) Subject: Large update adding: X-Git-Tag: RC1~133 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;h=d3af8002175ac2408a030aa91956b9cbacfb2c49;p=cura.git Large update adding: -Python based firmware loader (not used yet) -Beginning of the first run wizard -Preferences window Also moved around a few functions, cleaned somethings up --- diff --git a/SkeinPyPy_NewUI/avr_isp/chipDB.py b/SkeinPyPy_NewUI/avr_isp/chipDB.py new file mode 100644 index 00000000..8155f322 --- /dev/null +++ b/SkeinPyPy_NewUI/avr_isp/chipDB.py @@ -0,0 +1,14 @@ + +avrChipDB = { + 'ATMega2560': { + 'signature': [0x1E, 0x98, 0x01], + 'pageSize': 128, + 'pageCount': 1024, + }, +} + +def getChipFromDB(sig): + for chip in avrChipDB.values(): + if chip['signature'] == sig: + return chip + return False diff --git a/SkeinPyPy_NewUI/avr_isp/intelHex.py b/SkeinPyPy_NewUI/avr_isp/intelHex.py new file mode 100644 index 00000000..95c77a44 --- /dev/null +++ b/SkeinPyPy_NewUI/avr_isp/intelHex.py @@ -0,0 +1,35 @@ +import io + +def readHex(filename): + data = [] + extraAddr = 0 + f = io.open(filename, "r") + for line in f: + line = line.strip() + if line[0] != ':': + raise Exception("Hex file has a line not starting with ':'") + recLen = int(line[1:3], 16) + addr = int(line[3:7], 16) + extraAddr + recType = int(line[7:9], 16) + if len(line) != recLen * 2 + 11: + raise Exception("Error in hex file: " + line) + checkSum = 0 + for i in xrange(0, recLen + 5): + checkSum += int(line[i*2+1:i*2+3], 16) + checkSum &= 0xFF + if checkSum != 0: + raise Exception("Checksum error in hex file: " + line) + + if recType == 0:#Data record + while len(data) < addr + recLen: + data.append(0) + for i in xrange(0, recLen): + data[addr + i] = int(line[i*2+9:i*2+11], 16) + elif recType == 1: #End Of File record + pass + elif recType == 2: #Extended Segment Address Record + extraAddr = int(line[9:13], 16) * 16 + else: + print recType, recLen, addr, checkSum, line + f.close() + return data \ No newline at end of file diff --git a/SkeinPyPy_NewUI/avr_isp/ispBase.py b/SkeinPyPy_NewUI/avr_isp/ispBase.py new file mode 100644 index 00000000..553b7fec --- /dev/null +++ b/SkeinPyPy_NewUI/avr_isp/ispBase.py @@ -0,0 +1,38 @@ +import os, struct, sys, time + +from serial import Serial + +import chipDB + +class IspBase(): + def programChip(self, flashData): + self.curExtAddr = -1 + self.chip = chipDB.getChipFromDB(self.getSignature()) + if self.chip == False: + print "Chip with signature: " + str(self.getSignature()) + "not found" + return False + self.chipErase() + + print "Flashing %i bytes" % len(flashData) + self.writeFlash(flashData) + print "Verifying %i bytes" % len(flashData) + self.verifyFlash(flashData) + + return True + + #low level ISP commands + def getSignature(self): + sig = [] + sig.append(self.sendISP([0x30, 0x00, 0x00, 0x00])[3]) + sig.append(self.sendISP([0x30, 0x00, 0x01, 0x00])[3]) + sig.append(self.sendISP([0x30, 0x00, 0x02, 0x00])[3]) + return sig + + def chipErase(self): + self.sendISP([0xAC, 0x80, 0x00, 0x00]) + +class IspError(): + def __init__(self, value): + self.value = value + def __str__(self): + return repr(self.value) diff --git a/SkeinPyPy_NewUI/avr_isp/stk500v2.py b/SkeinPyPy_NewUI/avr_isp/stk500v2.py new file mode 100644 index 00000000..c7688c26 --- /dev/null +++ b/SkeinPyPy_NewUI/avr_isp/stk500v2.py @@ -0,0 +1,112 @@ +import os, struct, sys, time + +from serial import Serial + +import ispBase, intelHex + +class Stk500v2(ispBase.IspBase): + def __init__(self): + self.serial = None + self.seq = 1 + self.lastAddr = -1 + + def connect(self, port = 'COM3', speed = 115200): + if self.serial != None: + self.serial.close() + self.serial = Serial(port, speed, timeout=5) + self.seq = 1 + + #Reset the controller + self.serial.setDTR(1) + self.serial.setDTR(0) + time.sleep(0.2) + + self.sendMessage([1]) + if self.sendMessage([0x10, 0xc8, 0x64, 0x19, 0x20, 0x00, 0x53, 0x03, 0xac, 0x53, 0x00, 0x00]) != [0x10, 0x00]: + raise ispBase.IspError("Failed to enter programming mode") + + def sendISP(self, data): + recv = self.sendMessage([0x1D, 4, 4, 0, data[0], data[1], data[2], data[3]]) + return recv[2:6] + + def writeFlash(self, flashData): + #Set load addr to 0 (with more then 64k load) + self.sendMessage([0x06, 0x80, 0x00, 0x00, 0x00]) + + loadCount = (len(flashData) + 0xFF) / 0x100 + for i in xrange(0, loadCount): + recv = self.sendMessage([0x13, 0x01, 0x00, 0xc1, 0x0a, 0x40, 0x4c, 0x20, 0x00, 0x00] + flashData[(i * 0x100):(i * 0x100 + 0x100)]) + print "#%i#%i#" % (i + 1, loadCount) + + def verifyFlash(self, flashData): + #Set load addr to 0 (with more then 64k load) + self.sendMessage([0x06, 0x80, 0x00, 0x00, 0x00]) + + loadCount = (len(flashData) + 0xFF) / 0x100 + for i in xrange(0, loadCount): + recv = self.sendMessage([0x14, 0x01, 0x00, 0x20])[2:0x102] + print "#%i#%i#" % (i + 1, loadCount) + for j in xrange(0, 0x100): + if i * 0x100 + j < len(flashData) and flashData[i * 0x100 + j] != recv[j]: + raise ispBase.IspError('Verify error at: 0x%x' % (i * 0x100 + j)) + + def sendMessage(self, data): + message = struct.pack(">BBHB", 0x1B, self.seq, len(data), 0x0E) + for c in data: + message += struct.pack(">B", c) + checksum = 0 + for c in message: + checksum ^= ord(c) + message += struct.pack(">B", checksum) + self.serial.write(message) + self.serial.flush() + self.seq = (self.seq + 1) & 0xFF + return self.recvMessage() + + def recvMessage(self): + state = 'Start' + checksum = 0 + while True: + s = self.serial.read() + if len(s) < 1: + raise ispBase.IspError("Timeout") + b = struct.unpack(">B", s)[0] + checksum ^= b + #print hex(b) + if state == 'Start': + if b == 0x1B: + state = 'GetSeq' + checksum = 0x1B + elif state == 'GetSeq': + state = 'MsgSize1' + elif state == 'MsgSize1': + msgSize = b << 8 + state = 'MsgSize2' + elif state == 'MsgSize2': + msgSize |= b + state = 'Token' + elif state == 'Token': + if b != 0x0E: + state = 'Start' + else: + state = 'Data' + data = [] + elif state == 'Data': + data.append(b) + if len(data) == msgSize: + state = 'Checksum' + elif state == 'Checksum': + if checksum != 0: + state = 'Start' + else: + return data + + +def main(): + programmer = Stk500v2() + programmer.connect() + programmer.programChip(intelHex.readHex("cfg_4f55234def059.hex")) + sys.exit(1) + +if __name__ == '__main__': + main() diff --git a/SkeinPyPy_NewUI/fabmetheus_utilities/settings.py b/SkeinPyPy_NewUI/fabmetheus_utilities/settings.py index 67ec597a..32727e34 100644 --- a/SkeinPyPy_NewUI/fabmetheus_utilities/settings.py +++ b/SkeinPyPy_NewUI/fabmetheus_utilities/settings.py @@ -17,20 +17,20 @@ def DEFSET(setting): return setting.value def storedSetting(name): - return lambda setting: getSetting(name, setting.value) + return lambda setting: getProfileSetting(name, setting.value) def ifSettingAboveZero(name): - return lambda setting: float(getSetting(name, '0.0')) > 0 + return lambda setting: float(getProfileSetting(name, '0.0')) > 0 def ifSettingIs(name, value): - return lambda setting: getSetting(name) == value + return lambda setting: getProfileSetting(name) == value def storedPercentSetting(name): - return lambda setting: float(getSetting(name, setting.value)) / 100 + return lambda setting: float(getProfileSetting(name, setting.value)) / 100 def calculateEdgeWidth(setting): - wallThickness = float(getSetting('wall_thickness')) - nozzleSize = float(getSetting('nozzle_size')) + wallThickness = float(getProfileSetting('wall_thickness')) + nozzleSize = float(getProfileSetting('nozzle_size')) if wallThickness < nozzleSize: return wallThickness @@ -43,13 +43,13 @@ def calculateEdgeWidth(setting): return lineWidth def calculateShells(setting): - return calculateShellsImp(float(getSetting('wall_thickness'))) + return calculateShellsImp(float(getProfileSetting('wall_thickness'))) def calculateShellsBase(setting): - return calculateShellsImp(float(getSetting('wall_thickness')) + float(getSetting('extra_base_wall_thickness', '0'))) + return calculateShellsImp(float(getProfileSetting('wall_thickness')) + float(getProfileSetting('extra_base_wall_thickness', '0'))) def calculateShellsImp(wallThickness): - nozzleSize = float(getSetting('nozzle_size')) + nozzleSize = float(getProfileSetting('nozzle_size')) if wallThickness < nozzleSize: return 0 @@ -62,14 +62,14 @@ def calculateShellsImp(wallThickness): return lineCount - 1 def calculateSolidLayerCount(setting): - layerHeight = float(getSetting('layer_height')) - solidThickness = float(getSetting('solid_layer_thickness')) + layerHeight = float(getProfileSetting('layer_height')) + solidThickness = float(getProfileSetting('solid_layer_thickness')) ret = int(math.ceil(solidThickness / layerHeight - 0.0001)) return ret def firstLayerSpeedRatio(setting): - bottomSpeed = float(getSetting('bottom_layer_speed')) - speed = float(getSetting('print_speed')) + bottomSpeed = float(getProfileSetting('bottom_layer_speed')) + speed = float(getProfileSetting('print_speed')) return bottomSpeed/speed def getSkeinPyPyProfileInformation(): @@ -379,7 +379,7 @@ def loadGlobalProfile(filename): def saveGlobalProfile(filename): globalProfileParser.write(open(filename, 'w')) -def getSetting(name, default = "", section = 'profile'): +def getProfileSetting(name, default = "ERR", section = 'profile'): #Check if we have a configuration file loaded, else load the default. if not globals().has_key('globalProfileParser'): loadGlobalProfile(getDefaultProfilePath()) @@ -387,11 +387,11 @@ def getSetting(name, default = "", section = 'profile'): if not globalProfileParser.has_section(section): globalProfileParser.add_section(section) globalProfileParser.set(section, name, str(default)) - print name + " not found in profile, so using default" + print name + " not found in profile, so using default: " + str(default) return default return globalProfileParser.get(section, name) -def putSetting(name, value, section = 'profile'): +def putProfileSetting(name, value, section = 'profile'): #Check if we have a configuration file loaded, else load the default. if not globals().has_key('globalProfileParser'): loadGlobalProfile(getDefaultProfilePath()) @@ -399,6 +399,37 @@ def putSetting(name, value, section = 'profile'): globalProfileParser.add_section(section) globalProfileParser.set(section, name, str(value)) +global globalPreferenceParser +globalPreferenceParser = None + +def getPreferencePath(): + return os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../preferences.ini")) + +def getPreference(name, default = "ERR"): + global globalPreferenceParser + if globalPreferenceParser == None: + globalPreferenceParser = ConfigParser.ConfigParser() + globalPreferenceParser.read(getPreferencePath()) + if not globalPreferenceParser.has_option('preference', name): + if not globalPreferenceParser.has_section('preference'): + globalPreferenceParser.add_section('preference') + globalPreferenceParser.set('preference', name, str(default)) + print name + " not found in preferences, so using default: " + str(default) + return default + return globalPreferenceParser.get('preference', name) + +def putPreference(name, value): + #Check if we have a configuration file loaded, else load the default. + global globalPreferenceParser + if globalPreferenceParser == None: + globalPreferenceParser = ConfigParser.ConfigParser() + globalPreferenceParser.read(getPreferencePath()) + if not globalPreferenceParser.has_section('preference'): + globalPreferenceParser.add_section('preference') + globalPreferenceParser.set('preference', name, str(value)) + globalPreferenceParser.write(open(getPreferencePath(), 'w')) + + def getDefaultProfilePath(): return os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../current_profile.ini")) @@ -450,10 +481,10 @@ def getAlterationFile(fileName, allowMagicPrefix = True): if fileName == 'start.gcode': #For the start code, hack the temperature and the steps per E value into it. So the temperature is reached before the start code extrusion. #We also set our steps per E here, if configured. - eSteps = float(getSetting('steps_per_e_unit', '0')) + eSteps = float(getProfileSetting('steps_per_e_unit', '0')) if eSteps > 0: prefix += 'M92 E'+str(eSteps)+'\n' - temp = float(getSetting('print_temperature', '0')) + temp = float(getProfileSetting('print_temperature', '0')) if temp > 0: prefix += 'M109 S'+str(temp)+'\n' elif fileName == 'replace.csv': diff --git a/SkeinPyPy_NewUI/newui/advancedConfig.py b/SkeinPyPy_NewUI/newui/advancedConfig.py index eabab860..7de21eff 100644 --- a/SkeinPyPy_NewUI/newui/advancedConfig.py +++ b/SkeinPyPy_NewUI/newui/advancedConfig.py @@ -17,12 +17,13 @@ class advancedConfigWindow(configBase.configWindowBase): def __init__(self): super(advancedConfigWindow, self).__init__(title='Advanced config') + wx.EVT_CLOSE(self, self.OnClose) + left, right, main = self.CreateConfigPanel(self) configBase.TitleRow(left, "Accuracy") c = configBase.SettingRow(left, "Extra Wall thickness for bottom/top (mm)", 'extra_base_wall_thickness', '0.0', 'Additional wall thickness of the bottom and top layers.') validators.validFloat(c, 0.0) - validators.wallThicknessValidator(c) configBase.TitleRow(left, "Sequence") c = configBase.SettingRow(left, "Print order sequence", 'sequence', ['Loops > Perimeter > Infill', 'Loops > Infill > Perimeter', 'Infill > Loops > Perimeter', 'Infill > Perimeter > Loops', 'Perimeter > Infill > Loops', 'Perimeter > Loops > Infill'], 'Sequence of printing. The perimeter is the outer print edge, the loops are the insides of the walls, and the infill is the insides.'); c = configBase.SettingRow(left, "Force first layer sequence", 'force_first_layer_sequence', ['True', 'False'], 'This setting forces the order of the first layer to be \'Perimeter > Loops > Infill\'') @@ -37,3 +38,5 @@ class advancedConfigWindow(configBase.configWindowBase): main.Fit() self.Fit() + def OnClose(self, e): + self.Destroy() diff --git a/SkeinPyPy_NewUI/newui/configBase.py b/SkeinPyPy_NewUI/newui/configBase.py index 09280fee..4cf0a169 100644 --- a/SkeinPyPy_NewUI/newui/configBase.py +++ b/SkeinPyPy_NewUI/newui/configBase.py @@ -2,7 +2,6 @@ from __future__ import absolute_import import __init__ import wx, os, platform, types -import ConfigParser from fabmetheus_utilities import settings @@ -53,11 +52,11 @@ class configWindowBase(wx.Frame): def OnPopupDisplay(self, setting): x, y = setting.ctrl.ClientToScreenXY(0, 0) sx, sy = setting.ctrl.GetSizeTuple() - if platform.system() == "Windows": - #for some reason, under windows, the popup is relative to the main window... - wx, wy = self.ClientToScreenXY(0, 0) - x -= wx - y -= wy + #if platform.system() == "Windows": + # for some reason, under windows, the popup is relative to the main window... in some cases. (Wierd ass bug) + # wx, wy = self.ClientToScreenXY(0, 0) + # x -= wx + # y -= wy self.popup.setting = setting self.UpdatePopup(setting) self.popup.SetPosition((x, y+sy)) @@ -78,7 +77,10 @@ class configWindowBase(wx.Frame): def updateProfileToControls(self): "Update the configuration wx controls to show the new configuration settings" for setting in self.settingControlList: - setting.SetValue(settings.getSetting(setting.configName)) + if setting.type == 'profile': + setting.SetValue(settings.getProfileSetting(setting.configName)) + else: + setting.SetValue(settings.getPreference(setting.configName)) class TitleRow(): def __init__(self, panel, name): @@ -91,7 +93,7 @@ class TitleRow(): sizer.SetRows(sizer.GetRows() + 2) class SettingRow(): - def __init__(self, panel, label, configName, defaultValue = '', helpText = 'Help: TODO'): + def __init__(self, panel, label, configName, defaultValue = '', helpText = 'Help: TODO', type = 'profile'): "Add a setting to the configuration panel" sizer = panel.GetSizer() x = sizer.GetRows() @@ -102,12 +104,19 @@ class SettingRow(): self.helpText = helpText self.configName = configName self.panel = panel + self.type = type self.label = wx.StaticText(panel, -1, label) - if isinstance(defaultValue, types.StringTypes): - self.ctrl = wx.TextCtrl(panel, -1, settings.getSetting(configName, defaultValue)) + if self.type == 'profile': + if isinstance(defaultValue, types.StringTypes): + self.ctrl = wx.TextCtrl(panel, -1, settings.getProfileSetting(configName, defaultValue)) + else: + self.ctrl = wx.ComboBox(panel, -1, settings.getProfileSetting(configName, defaultValue[0]), choices=defaultValue, style=wx.CB_DROPDOWN|wx.CB_READONLY) else: - self.ctrl = wx.ComboBox(panel, -1, settings.getSetting(configName, defaultValue[0]), choices=defaultValue, style=wx.CB_DROPDOWN|wx.CB_READONLY) + if isinstance(defaultValue, types.StringTypes): + self.ctrl = wx.TextCtrl(panel, -1, settings.getPreference(configName, defaultValue)) + else: + self.ctrl = wx.ComboBox(panel, -1, settings.getPreference(configName, defaultValue[0]), choices=defaultValue, style=wx.CB_DROPDOWN|wx.CB_READONLY) #self.helpButton = wx.Button(panel, -1, "?", style=wx.BU_EXACTFIT) #self.helpButton.SetToolTip(wx.ToolTip(help)) @@ -123,7 +132,10 @@ class SettingRow(): sizer.SetRows(x+1) def OnSettingTextChange(self, e): - settings.putSetting(self.configName, self.GetValue()) + if self.type == 'profile': + settings.putProfileSetting(self.configName, self.GetValue()) + else: + settings.putPreference(self.configName, self.GetValue()) result = validators.SUCCESS msgs = [] for validator in self.validators: @@ -165,29 +177,3 @@ class settingNotify(): return validators.SUCCESS, '' except ValueError: return validators.SUCCESS, '' - -def getPreferencePath(): - return os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../preferences.ini")) - -def getPreference(name, default): - if not globals().has_key('globalPreferenceParser'): - globalPreferenceParser = ConfigParser.ConfigParser() - globalPreferenceParser.read(getPreferencePath()) - if not globalPreferenceParser.has_option('preference', name): - if not globalPreferenceParser.has_section('preference'): - globalPreferenceParser.add_section('preference') - globalPreferenceParser.set('preference', name, str(default)) - print name + " not found in profile, so using default" - return default - return globalPreferenceParser.get('preference', name) - -def putPreference(name, value): - #Check if we have a configuration file loaded, else load the default. - if not globals().has_key('globalPreferenceParser'): - globalPreferenceParser = ConfigParser.ConfigParser() - globalPreferenceParser.read(getPreferencePath()) - if not globalPreferenceParser.has_section('preference'): - globalPreferenceParser.add_section('preference') - globalPreferenceParser.set('preference', name, str(value)) - globalPreferenceParser.write(open(getPreferencePath(), 'w')) - diff --git a/SkeinPyPy_NewUI/newui/configWizard.py b/SkeinPyPy_NewUI/newui/configWizard.py new file mode 100644 index 00000000..862fffa6 --- /dev/null +++ b/SkeinPyPy_NewUI/newui/configWizard.py @@ -0,0 +1,129 @@ +from __future__ import absolute_import +import __init__ + +import wx, os, platform, types +import wx.wizard + +from fabmetheus_utilities import settings + +class InfoPage(wx.wizard.WizardPageSimple): + def __init__(self, parent, title): + """Constructor""" + wx.wizard.WizardPageSimple.__init__(self, parent) + + sizer = wx.BoxSizer(wx.VERTICAL) + self.sizer = sizer + self.SetSizer(sizer) + + title = wx.StaticText(self, -1, title) + title.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD)) + sizer.Add(title, 0, wx.ALIGN_CENTRE|wx.ALL, 5) + sizer.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.ALL, 5) + + def AddText(self,info): + self.GetSizer().Add(wx.StaticText(self, -1, info), 0, wx.LEFT|wx.RIGHT, 5) + + def AddSeperator(self): + self.GetSizer().Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.ALL, 5) + + def AddHiddenSeperator(self): + self.AddText('') + + def AddRadioButton(self, label, style = 0): + radio = wx.RadioButton(self, -1, label, style=style) + self.GetSizer().Add(radio, 0, wx.EXPAND|wx.ALL, 5) + return radio + + def AllowNext(self): + return True + + def StoreData(self): + pass + +class FirstInfoPage(InfoPage): + def __init__(self, parent): + super(FirstInfoPage, self).__init__(parent, "First time run wizard") + self.AddText('Welcome, and thanks for trying SkeinPyPy!') + self.AddSeperator() + self.AddText('This wizard will help you with the following steps:') + self.AddText('* Configure SkeinPyPy for your machine') + self.AddText('* Upgrade your firmware') + self.AddText('* Calibrate your machine') + #self.AddText('* Do your first print') + +class MachineSelectPage(InfoPage): + def __init__(self, parent): + super(MachineSelectPage, self).__init__(parent, "Select your machine") + self.AddText('What kind of machine do you have:') + + self.UltimakerRadio = self.AddRadioButton("Ultimaker", style=wx.RB_GROUP) + self.UltimakerRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimakerSelect) + self.OtherRadio = self.AddRadioButton("Other (Ex: RepRap)") + self.OtherRadio.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect) + + def OnUltimakerSelect(self, e): + wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimakerFirmwareUpgradePage) + + def OnOtherSelect(self, e): + wx.wizard.WizardPageSimple.Chain(self, self.GetParent().configureMachineDimensions) + + def StoreData(self): + if self.UltimakerRadio.GetValue(): + settings.putPreference('machine_width', '205') + settings.putPreference('machine_depth', '205') + settings.putPreference('machine_height', '200') + settings.putProfileSetting('nozzle_size', '0.4') + settings.putProfileSetting('machine_center_x', '100') + settings.putProfileSetting('machine_center_x', '100') + +class FirmwareUpgradePage(InfoPage): + def __init__(self, parent): + super(FirmwareUpgradePage, self).__init__(parent, "Upgrade Ultimaker Firmware") + self.AddText('Firmware is the piece of software running directly on your 3D printer.\nThis firmware controls the step motors, regulates the temperature\nand ultimately makes your printer work.') + self.AddHiddenSeperator() + self.AddText('The firmware shipping with new Ultimakers works, but upgrades\nhave been made to make better prints, and make calibration easier.') + self.AddHiddenSeperator() + self.AddText('SkeinPyPy requires these new features and thus\nyour firmware will most likely need to be upgraded.\nYou will get the chance to do so now.') + self.AddHiddenSeperator() + button = wx.Button(self, -1, 'Upgrade firmware') + self.Bind(wx.EVT_BUTTON, self.OnUpgradeClick) + self.GetSizer().Add(button, 0) + self.AddHiddenSeperator() + self.AddText('Do not upgrade to this firmware if:') + self.AddText('* You have an older machine based on ATMega1280') + self.AddText('* Using an LCD panel') + self.AddText('* Have other changes in the firmware') + + def OnUpgradeClick(self, e): + pass + +class configWizard(wx.wizard.Wizard): + def __init__(self): + super(configWizard, self).__init__(None, -1, "Configuration Wizard") + + self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged) + self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging) + + self.firstInfoPage = FirstInfoPage(self) + self.machineSelectPage = MachineSelectPage(self) + self.ultimakerFirmwareUpgradePage = FirmwareUpgradePage(self) + self.configureMachineDimensions = InfoPage(self, 'BLA2') + + wx.wizard.WizardPageSimple.Chain(self.firstInfoPage, self.machineSelectPage) + wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerFirmwareUpgradePage) + + self.FitToPage(self.firstInfoPage) + self.GetPageAreaSizer().Add(self.firstInfoPage) + + self.RunWizard(self.firstInfoPage) + self.Destroy() + + def OnPageChanging(self, e): + e.GetPage().StoreData() + + def OnPageChanged(self, e): + if e.GetPage().AllowNext(): + self.FindWindowById(wx.ID_FORWARD).Enable() + else: + self.FindWindowById(wx.ID_FORWARD).Disable() + self.FindWindowById(wx.ID_BACKWARD).Disable() diff --git a/SkeinPyPy_NewUI/newui/mainWindow.py b/SkeinPyPy_NewUI/newui/mainWindow.py index 912ee6e6..64dbafc2 100644 --- a/SkeinPyPy_NewUI/newui/mainWindow.py +++ b/SkeinPyPy_NewUI/newui/mainWindow.py @@ -2,7 +2,6 @@ from __future__ import absolute_import import __init__ import wx, os, platform, types -import ConfigParser from fabmetheus_utilities import settings @@ -12,6 +11,8 @@ from newui import preview3d from newui import sliceProgessPanel from newui import alterationPanel from newui import validators +from newui import preferencesDialog +from newui import configWizard def main(): app = wx.App(False) @@ -25,12 +26,19 @@ class mainWindow(configBase.configWindowBase): wx.EVT_CLOSE(self, self.OnClose) + if settings.getPreference('wizardDone', 'False') == 'False': + configWizard.configWizard() + menubar = wx.MenuBar() fileMenu = wx.Menu() i = fileMenu.Append(-1, 'Open Profile...', 'Open Profile...') self.Bind(wx.EVT_MENU, self.OnLoadProfile, i) i = fileMenu.Append(-1, 'Save Profile...', 'Save Profile...') self.Bind(wx.EVT_MENU, self.OnSaveProfile, i) + fileMenu.AppendSeparator() + i = fileMenu.Append(-1, 'Preferences...', 'Preferences...') + self.Bind(wx.EVT_MENU, self.OnPreferences, i) + fileMenu.AppendSeparator() i = fileMenu.Append(wx.ID_EXIT, 'Quit', 'Quit application') self.Bind(wx.EVT_MENU, self.OnQuit, i) menubar.Append(fileMenu, '&File') @@ -42,7 +50,7 @@ class mainWindow(configBase.configWindowBase): self.SetMenuBar(menubar) self.lastPath = "" - self.filename = configBase.getPreference('lastFile', None) + self.filename = settings.getPreference('lastFile', "None") self.progressPanelList = [] #Preview window @@ -89,7 +97,7 @@ class mainWindow(configBase.configWindowBase): c = configBase.SettingRow(right, "Support type", 'support', ['None', 'Exterior only', 'Everywhere', 'Empty layers only'], 'Type of support structure build.\nNone does not do any support.\nExterior only only creates support on the outside.\nEverywhere creates support even on the insides of the model.\nOnly on empty layers is for stacked objects.') configBase.TitleRow(right, "Filament") - c = configBase.SettingRow(right, "Diameter (mm)", 'filament_diameter', '2.98', 'Diameter of your filament, as accurately as possible.\nIf you cannot measure this value you will have to callibrate it, a higher number means less extrusion, a smaller number generates more extrusion.') + c = configBase.SettingRow(right, "Diameter (mm)", 'filament_diameter', '2.89', 'Diameter of your filament, as accurately as possible.\nIf you cannot measure this value you will have to callibrate it, a higher number means less extrusion, a smaller number generates more extrusion.') validators.validFloat(c, 1.0) c = configBase.SettingRow(right, "Packing Density", 'filament_density', '1.00', 'Packing density of your filament. This should be 1.00 for PLA and 0.85 for ABS') validators.validFloat(c, 0.5, 1.5) @@ -156,7 +164,7 @@ class mainWindow(configBase.configWindowBase): sizer.Add(sliceButton, (1,2)) self.sizer = sizer - if self.filename != None: + if self.filename != "None": self.preview3d.loadModelFile(self.filename) self.lastPath = os.path.split(self.filename)[0] @@ -185,12 +193,17 @@ class mainWindow(configBase.configWindowBase): settings.saveGlobalProfile(profileFile) dlg.Destroy() + def OnPreferences(self, e): + prefDialog = preferencesDialog.preferencesDialog(self) + prefDialog.Centre() + prefDialog.Show(True) + def OnLoadSTL(self, e): dlg=wx.FileDialog(self, "Open file to print", self.lastPath, style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST) dlg.SetWildcard("OBJ, STL files (*.stl;*.obj)|*.stl;*.obj") if dlg.ShowModal() == wx.ID_OK: self.filename=dlg.GetPath() - configBase.putPreference('lastFile', self.filename) + settings.putPreference('lastFile', self.filename) if not(os.path.exists(self.filename)): return self.lastPath = os.path.split(self.filename)[0] @@ -229,11 +242,6 @@ class mainWindow(configBase.configWindowBase): self.sizer.Add(spp, (i,0), span=(1,4), flag=wx.EXPAND) i += 1 self.sizer.Layout() - - def updateProfileToControls(self): - "Update the configuration wx controls to show the new configuration settings" - for setting in self.settingControlList: - setting.SetValue(settings.getSetting(setting.configName)) def OnQuit(self, e): self.Close() diff --git a/SkeinPyPy_NewUI/newui/preferencesDialog.py b/SkeinPyPy_NewUI/newui/preferencesDialog.py new file mode 100644 index 00000000..4db629fa --- /dev/null +++ b/SkeinPyPy_NewUI/newui/preferencesDialog.py @@ -0,0 +1,36 @@ +from __future__ import absolute_import +import __init__ + +import wx, os, platform, types +import ConfigParser + +from newui import configBase + +class preferencesDialog(configBase.configWindowBase): + def __init__(self, parent): + super(preferencesDialog, self).__init__(title="Preferences") + + wx.EVT_CLOSE(self, self.OnClose) + + left, right, main = self.CreateConfigPanel(self) + configBase.TitleRow(left, 'Machine settings') + c = configBase.SettingRow(left, 'Steps per E', 'steps_per_e', '0', 'Amount of steps per mm filament extrusion', type = 'preference') + validators.validFloat(c, 0.1) + c = configBase.SettingRow(left, 'Machine width', 'machine_width', '205', 'Size of the machine in mm', type = 'preference') + validators.validFloat(c, 10.0) + c = configBase.SettingRow(left, 'Machine depth', 'machine_depth', '205', 'Size of the machine in mm', type = 'preference') + validators.validFloat(c, 10.0) + c = configBase.SettingRow(left, 'Machine height', 'machine_height', '200', 'Size of the machine in mm', type = 'preference') + validators.validFloat(c, 10.0) + + configBase.TitleRow(left, 'Communication settings') + c = configBase.SettingRow(left, 'Serial port', 'serial_port', 'AUTO', 'Serial port to use for communication with the printer', type = 'preference') + c = configBase.SettingRow(left, 'Baudrate', 'serial_baud', '250000', 'Speed of the serial port communication\nNeeds to match your firmware settings', type = 'preference') + + self.MakeModal(True) + main.Fit() + self.Fit() + + def OnClose(self, e): + self.MakeModal(False) + self.Destroy() diff --git a/SkeinPyPy_NewUI/newui/preview3d.py b/SkeinPyPy_NewUI/newui/preview3d.py index 1615fed5..d9f96f3a 100644 --- a/SkeinPyPy_NewUI/newui/preview3d.py +++ b/SkeinPyPy_NewUI/newui/preview3d.py @@ -30,38 +30,35 @@ class previewPanel(wx.Panel): self.init = 0 self.triangleMesh = None self.gcode = None - self.machineSize = Vector3(210, 210, 200) + self.machineSize = Vector3(float(settings.getPreference('machine_width', '205')), float(settings.getPreference('machine_depth', '205')), float(settings.getPreference('machine_height', '200'))) self.machineCenter = Vector3(0, 0, 0) - tb = wx.ToolBar( self, -1 ) - self.ToolBar = tb - tb.SetToolBitmapSize( ( 21, 21 ) ) + self.toolbar = wx.ToolBar( self, -1 ) + self.toolbar.SetToolBitmapSize( ( 21, 21 ) ) - button = wx.Button(tb, -1, "3D", size=(21*2,21)) - tb.AddControl(button) + button = wx.Button(self.toolbar, -1, "3D", size=(21*2,21)) + self.toolbar.AddControl(button) self.Bind(wx.EVT_BUTTON, self.On3DClick, button) - button = wx.Button(tb, -1, "Top", size=(21*2,21)) - tb.AddControl(button) + button = wx.Button(self.toolbar, -1, "Top", size=(21*2,21)) + self.toolbar.AddControl(button) self.Bind(wx.EVT_BUTTON, self.OnTopClick, button) - self.transparentButton = wx.Button(tb, -1, "T", size=(21,21)) - tb.AddControl(self.transparentButton) + self.transparentButton = wx.Button(self.toolbar, -1, "T", size=(21,21)) + self.toolbar.AddControl(self.transparentButton) self.Bind(wx.EVT_BUTTON, self.OnTransparentClick, self.transparentButton) - self.depthComplexityButton = wx.Button(tb, -1, "DC", size=(21*2,21)) - tb.AddControl(self.depthComplexityButton) + self.depthComplexityButton = wx.Button(self.toolbar, -1, "DC", size=(21*2,21)) + self.toolbar.AddControl(self.depthComplexityButton) self.Bind(wx.EVT_BUTTON, self.OnDepthComplexityClick, self.depthComplexityButton) - self.layerSpin = wx.SpinCtrl(tb, -1, '', size=(21*4,21), style=wx.SP_ARROW_KEYS) - tb.AddControl(self.layerSpin) + self.layerSpin = wx.SpinCtrl(self.toolbar, -1, '', size=(21*4,21), style=wx.SP_ARROW_KEYS) + self.toolbar.AddControl(self.layerSpin) self.Bind(wx.EVT_SPINCTRL, self.OnLayerNrChange, self.layerSpin) - self.transparentButton.Show(False) - self.layerSpin.Show(False) - - tb.Realize() - + + self.updateToolbar() + sizer = wx.BoxSizer(wx.VERTICAL) - sizer.Add(tb, 0, flag=wx.EXPAND|wx.TOP|wx.LEFT|wx.RIGHT, border=1) + sizer.Add(self.toolbar, 0, flag=wx.EXPAND|wx.TOP|wx.LEFT|wx.RIGHT, border=1) sizer.Add(self.glCanvas, 1, flag=wx.EXPAND) self.SetSizer(sizer) @@ -97,7 +94,7 @@ class previewPanel(wx.Panel): self.glCanvas.lineWidth = settings.calculateEdgeWidth(setting) def updateInfillLineWidth(self, setting): - self.glCanvas.infillLineWidth = settings.getSetting('nozzle_size') + self.glCanvas.infillLineWidth = settings.getProfileSetting('nozzle_size') def loadModelFile(self, filename): self.modelFilename = filename @@ -130,9 +127,11 @@ class previewPanel(wx.Panel): def updateToolbar(self): self.transparentButton.Show(self.triangleMesh != None) + self.depthComplexityButton.Show(self.triangleMesh != None) self.layerSpin.Show(self.gcode != None) if self.gcode != None: self.layerSpin.SetRange(1, self.gcode.layerCount) + self.toolbar.Realize() def OnTransparentClick(self, e): self.glCanvas.renderTransparent = not self.glCanvas.renderTransparent @@ -245,10 +244,10 @@ class PreviewGLCanvas(glcanvas.GLCanvas): glEnd() glLineWidth(2) glBegin(GL_LINES) - for i in xrange(0, machineSize.x, 10): + for i in xrange(0, int(machineSize.x), 10): glVertex3f(i, 0, 0) glVertex3f(i, machineSize.y, 0) - for i in xrange(0, machineSize.y, 10): + for i in xrange(0, int(machineSize.y), 10): glVertex3f(0, i, 0) glVertex3f(machineSize.x, i, 0) glEnd() @@ -390,19 +389,19 @@ class PreviewGLCanvas(glcanvas.GLCanvas): glStencilFunc(GL_EQUAL, i, 0xFF); glColor(0, float(i)/10, 0) glBegin(GL_QUADS) - glVertex3f(-10,-10,-1) - glVertex3f( 10,-10,-1) - glVertex3f( 10, 10,-1) - glVertex3f(-10, 10,-1) + glVertex3f(-1000,-1000,-1) + glVertex3f( 1000,-1000,-1) + glVertex3f( 1000, 1000,-1) + glVertex3f(-1000, 1000,-1) glEnd() for i in xrange(1, 20, 2): glStencilFunc(GL_EQUAL, i, 0xFF); glColor(float(i)/10, 0, 0) glBegin(GL_QUADS) - glVertex3f(-10,-10,-1) - glVertex3f( 10,-10,-1) - glVertex3f( 10, 10,-1) - glVertex3f(-10, 10,-1) + glVertex3f(-1000,-1000,-1) + glVertex3f( 1000,-1000,-1) + glVertex3f( 1000, 1000,-1) + glVertex3f(-1000, 1000,-1) glEnd() glPopMatrix() diff --git a/SkeinPyPy_NewUI/newui/validators.py b/SkeinPyPy_NewUI/newui/validators.py index 30c407a4..a8cb4804 100644 --- a/SkeinPyPy_NewUI/newui/validators.py +++ b/SkeinPyPy_NewUI/newui/validators.py @@ -68,7 +68,7 @@ class wallThicknessValidator(): def validate(self): try: wallThickness = float(self.setting.GetValue()) - nozzleSize = float(settings.getSetting('nozzle_size')) + nozzleSize = float(settings.getProfileSetting('nozzle_size')) if wallThickness <= nozzleSize * 0.5: return ERROR, 'Trying to print walls thinner then the half of your nozzle size, this will not produce anything usable' if wallThickness <= nozzleSize * 0.85: