python3: pep8 all files.

e.g. 4 spaces for identation
using black for this:
https://github.com/ambv/black
This commit is contained in:
Nico Tonnhofer 2018-12-28 20:07:51 +01:00
parent 9b49097ac3
commit bf72cb7f10
35 changed files with 6526 additions and 5803 deletions

View File

@ -14,12 +14,15 @@
from __future__ import print_function from __future__ import print_function
import sys import sys
import time import time
if sys.version_info.major >= 3: if sys.version_info.major >= 3:
print("You are currently running Python3. Python3 is not supported.\n" print(
"You are currently running Python3. Python3 is not supported.\n"
"Please try running with Python2.\n\n" "Please try running with Python2.\n\n"
"It often works to type \"python2 configtool.py\" in the command line.") 'It often works to type "python2 configtool.py" in the command line.'
time.sleep(10) )
sys.exit(-1) time.sleep(10)
sys.exit(-1)
import getopt import getopt
import os.path import os.path
@ -29,92 +32,99 @@ from configtool.settings import Settings
from configtool.board import Board from configtool.board import Board
from configtool.printer import Printer from configtool.printer import Printer
cmdFolder = os.path.realpath(os.path.abspath(os.path.split(inspect.getfile( cmdFolder = os.path.realpath(
inspect.currentframe()))[0])) os.path.abspath(os.path.split(inspect.getfile(inspect.currentframe()))[0])
)
verbose = 0 verbose = 0
settings = None settings = None
board = None board = None
printer = None printer = None
def getSettings(arg = None):
global settings def getSettings(arg=None):
if arg or not settings: global settings
settings = Settings(None, cmdFolder, arg) if arg or not settings:
settings.verbose = verbose settings = Settings(None, cmdFolder, arg)
return settings settings.verbose = verbose
return settings
def cmdLoad(arg): def cmdLoad(arg):
xx, ext = os.path.splitext(arg) xx, ext = os.path.splitext(arg)
fn = os.path.basename(arg) fn = os.path.basename(arg)
if ext == ".ini": if ext == ".ini":
settings = getSettings(arg) settings = getSettings(arg)
if not settings.loaded: if not settings.loaded:
print("Failed to load settings file: %s." % arg) print("Failed to load settings file: %s." % arg)
sys.exit(2) sys.exit(2)
return return
if ext == ".h": if ext == ".h":
if fn.startswith("board."): if fn.startswith("board."):
global board global board
board = Board(getSettings()) board = Board(getSettings())
ok, fn = board.loadConfigFile(arg) ok, fn = board.loadConfigFile(arg)
if not ok: if not ok:
print("Failed trying to load board file: %s." % fn) print("Failed trying to load board file: %s." % fn)
sys.exit(2) sys.exit(2)
return return
elif fn.startswith("printer."): elif fn.startswith("printer."):
global printer global printer
printer = Printer(getSettings()) printer = Printer(getSettings())
ok, fn = printer.loadConfigFile(arg) ok, fn = printer.loadConfigFile(arg)
if not ok: if not ok:
print("Failed trying to load printer file: %s" % fn) print("Failed trying to load printer file: %s" % fn)
sys.exit(2) sys.exit(2)
return return
print("Unrecognized file: %s." % arg)
print("Expected one of *.ini, board.*.h or printer.*.h.")
sys.exit(2)
print("Unrecognized file: %s." % arg)
print("Expected one of *.ini, board.*.h or printer.*.h.")
sys.exit(2)
def cmdSave(arg): def cmdSave(arg):
xx, ext = os.path.splitext(arg) xx, ext = os.path.splitext(arg)
fn = os.path.basename(arg) fn = os.path.basename(arg)
if ext == ".ini": if ext == ".ini":
if not getSettings(arg).save(arg): if not getSettings(arg).save(arg):
print("Failed to save settings file: %s." % arg) print("Failed to save settings file: %s." % arg)
sys.exit(2) sys.exit(2)
return return
if ext == ".h": if ext == ".h":
if fn.startswith("board."): if fn.startswith("board."):
global board global board
if not board.saveConfigFile(arg, None): if not board.saveConfigFile(arg, None):
print("Failed trying to save board file: %s." % arg) print("Failed trying to save board file: %s." % arg)
sys.exit(2) sys.exit(2)
return return
elif fn.startswith("printer."): elif fn.startswith("printer."):
global printer global printer
if not printer.saveConfigFile(arg, None): if not printer.saveConfigFile(arg, None):
print("Failed trying to save printer file: %s." % arg) print("Failed trying to save printer file: %s." % arg)
sys.exit(2) sys.exit(2)
return return
print("Unrecognized file: %s." % arg)
print("Expected one of *.ini, board.*.h or printer.*.h.")
sys.exit(2)
print("Unrecognized file: %s." % arg)
print("Expected one of *.ini, board.*.h or printer.*.h.")
sys.exit(2)
def cmdShowAll(): def cmdShowAll():
names = {"configtool": getSettings(), "board": board, "printer": printer} names = {"configtool": getSettings(), "board": board, "printer": printer}
for namespace in names: for namespace in names:
if names[namespace]: if names[namespace]:
values = names[namespace].getValues() values = names[namespace].getValues()
for k in sorted(values): for k in sorted(values):
print("%s.%s: %s" % (namespace, k, str(values[k]))) print("%s.%s: %s" % (namespace, k, str(values[k])))
def cmdHelp(): def cmdHelp():
print("""Usage: %s [options] print(
"""Usage: %s [options]
Running without any options starts the gui (normal operation). Running without any options starts the gui (normal operation).
Following options are available for command line automation: Following options are available for command line automation:
@ -137,50 +147,55 @@ Following options are available for command line automation:
-a, --show-all Show all loaded variables and values. -a, --show-all Show all loaded variables and values.
-q, --quit Quit processing without launching the GUI. -q, --quit Quit processing without launching the GUI.
""" % sys.argv[0]) """
% sys.argv[0]
)
def CommandLine(argv): def CommandLine(argv):
""" Parse and act on command line arguments. All script automation commands """ Parse and act on command line arguments. All script automation commands
result in sys.exit() (i.e. they do not return from this function). Other result in sys.exit() (i.e. they do not return from this function). Other
options like --debug will return to allow the gui to launch. options like --debug will return to allow the gui to launch.
""" """
global settings, verbose global settings, verbose
try: try:
opts, args = getopt.getopt(argv, "hvl:as:q", ["help", "verbose", "load=", opts, args = getopt.getopt(
"show-all", "save=", "quit"]) argv, "hvl:as:q", ["help", "verbose", "load=", "show-all", "save=", "quit"]
except getopt.GetoptError as err: )
print(err) except getopt.GetoptError as err:
print("Use '%s --help' to get help with command line options." % print(err)
sys.argv[0]) print("Use '%s --help' to get help with command line options." % sys.argv[0])
sys.exit(2) sys.exit(2)
# Check for HELP first. # Check for HELP first.
for opt, arg in opts: for opt, arg in opts:
if opt in ("-h", "--help"): if opt in ("-h", "--help"):
cmdHelp() cmdHelp()
sys.exit() sys.exit()
# Now parse other options. # Now parse other options.
for opt, arg in opts: for opt, arg in opts:
if opt in ("-v", "--verbose"): if opt in ("-v", "--verbose"):
verbose += 1 verbose += 1
getSettings() getSettings()
elif opt in ("-l", "--load"): elif opt in ("-l", "--load"):
cmdLoad(arg) cmdLoad(arg)
elif opt in ("-s", "--save"): elif opt in ("-s", "--save"):
cmdSave(arg) cmdSave(arg)
elif opt in ("-a", "--show-all"): elif opt in ("-a", "--show-all"):
cmdShowAll() cmdShowAll()
elif opt in ("-q", "--quit"): elif opt in ("-q", "--quit"):
sys.exit() sys.exit()
if __name__ == '__main__':
CommandLine(sys.argv[1:])
from configtool.gui import StartGui if __name__ == "__main__":
StartGui(getSettings()) CommandLine(sys.argv[1:])
from configtool.gui import StartGui
StartGui(getSettings())

View File

@ -1,120 +1,126 @@
import wx import wx
from configtool.page import Page from configtool.page import Page
class AccelerationPage(wx.Panel, Page): class AccelerationPage(wx.Panel, Page):
def __init__(self, parent, nb, idPg, font): def __init__(self, parent, nb, idPg, font):
wx.Panel.__init__(self, nb, wx.ID_ANY) wx.Panel.__init__(self, nb, wx.ID_ANY)
Page.__init__(self, font) Page.__init__(self, font)
self.parent = parent self.parent = parent
self.id = idPg self.id = idPg
self.accTypeKeys = ['ACCELERATION_REPRAP', 'ACCELERATION_RAMPING', self.accTypeKeys = [
'ACCELERATION_TEMPORAL'] "ACCELERATION_REPRAP",
self.jerkKeys = ['MAX_JERK_X', 'MAX_JERK_Y', 'MAX_JERK_Z', 'MAX_JERK_E'] "ACCELERATION_RAMPING",
"ACCELERATION_TEMPORAL",
]
self.jerkKeys = ["MAX_JERK_X", "MAX_JERK_Y", "MAX_JERK_Z", "MAX_JERK_E"]
self.labels = {'ACCELERATION_REPRAP': "RepRap", self.labels = {
'ACCELERATION_RAMPING': "Ramping", "ACCELERATION_REPRAP": "RepRap",
'ACCELERATION_TEMPORAL': "Temporal", "ACCELERATION_RAMPING": "Ramping",
'ACCELERATION': "Acceleration:", "ACCELERATION_TEMPORAL": "Temporal",
'LOOKAHEAD': "Look Ahead", "ACCELERATION": "Acceleration:",
'MAX_JERK_X': "X:", 'MAX_JERK_Y': "Y:", 'MAX_JERK_Z': "Z:", "LOOKAHEAD": "Look Ahead",
'MAX_JERK_E': "E:"} "MAX_JERK_X": "X:",
"MAX_JERK_Y": "Y:",
"MAX_JERK_Z": "Z:",
"MAX_JERK_E": "E:",
}
sz = wx.GridBagSizer() sz = wx.GridBagSizer()
sz.Add((20, 40), pos = (0, 0)) sz.Add((20, 40), pos=(0, 0))
b = wx.StaticBox(self, wx.ID_ANY, "Acceleration Type") b = wx.StaticBox(self, wx.ID_ANY, "Acceleration Type")
b.SetFont(font) b.SetFont(font)
sbox = wx.StaticBoxSizer(b, wx.VERTICAL) sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.Add((5, 5)) sbox.Add((5, 5))
style = wx.RB_GROUP style = wx.RB_GROUP
for k in self.accTypeKeys: for k in self.accTypeKeys:
rb = self.addRadioButton(k, style, self.onAccTypeSelect, b) rb = self.addRadioButton(k, style, self.onAccTypeSelect, b)
style = 0 style = 0
sbox.Add(rb, 1, wx.LEFT + wx.RIGHT, 16) sbox.Add(rb, 1, wx.LEFT + wx.RIGHT, 16)
sbox.Add((5, 5)) sbox.Add((5, 5))
self.rbNone = wx.RadioButton(self, wx.ID_ANY, "None", style = style) self.rbNone = wx.RadioButton(self, wx.ID_ANY, "None", style=style)
self.rbNone.SetFont(font) self.rbNone.SetFont(font)
self.rbNone.SetValue(True) self.rbNone.SetValue(True)
self.Bind(wx.EVT_RADIOBUTTON, self.onAccTypeSelect, self.rbNone) self.Bind(wx.EVT_RADIOBUTTON, self.onAccTypeSelect, self.rbNone)
sbox.Add(self.rbNone, 1, wx.LEFT + wx.RIGHT, 16) sbox.Add(self.rbNone, 1, wx.LEFT + wx.RIGHT, 16)
sbox.Add((5, 5)) sbox.Add((5, 5))
sz.Add(sbox, pos = (1, 1)) sz.Add(sbox, pos=(1, 1))
b = wx.StaticBox(self, wx.ID_ANY, "Ramping Parameters") b = wx.StaticBox(self, wx.ID_ANY, "Ramping Parameters")
b.SetFont(font) b.SetFont(font)
sbox = wx.StaticBoxSizer(b, wx.VERTICAL) sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.Add((5, 5)) sbox.Add((5, 5))
k = 'ACCELERATION' k = "ACCELERATION"
tc = self.addTextCtrl(k, 80, self.onTextCtrlFloat) tc = self.addTextCtrl(k, 80, self.onTextCtrlFloat)
self.textControls[k].Enable(False) self.textControls[k].Enable(False)
sbox.Add(tc) sbox.Add(tc)
sbox.Add((5, 5)) sbox.Add((5, 5))
k = 'LOOKAHEAD' k = "LOOKAHEAD"
cb = self.addCheckBox(k, self.onCheckBox) cb = self.addCheckBox(k, self.onCheckBox)
self.checkBoxes[k].Enable(False) self.checkBoxes[k].Enable(False)
sbox.Add(cb, 1, wx.ALIGN_CENTER_HORIZONTAL) sbox.Add(cb, 1, wx.ALIGN_CENTER_HORIZONTAL)
sbox.Add((5, 5)) sbox.Add((5, 5))
sz.Add(sbox, pos = (1, 3)) sz.Add(sbox, pos=(1, 3))
b = wx.StaticBox(self, wx.ID_ANY, "Maximum Jerk") b = wx.StaticBox(self, wx.ID_ANY, "Maximum Jerk")
b.SetFont(font) b.SetFont(font)
sbox = wx.StaticBoxSizer(b, wx.VERTICAL) sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.Add((5, 5)) sbox.Add((5, 5))
for k in self.jerkKeys: for k in self.jerkKeys:
tc = self.addTextCtrl(k, 40, self.onTextCtrlInteger) tc = self.addTextCtrl(k, 40, self.onTextCtrlInteger)
sbox.Add(tc) sbox.Add(tc)
sbox.Add((5, 5)) sbox.Add((5, 5))
sz.Add((80, 20), pos = (1, 4)) sz.Add((80, 20), pos=(1, 4))
sz.Add(sbox, pos = (1, 5)) sz.Add(sbox, pos=(1, 5))
self.SetSizer(sz) self.SetSizer(sz)
self.enableAll(False) self.enableAll(False)
def enableAll(self, flag = True): def enableAll(self, flag=True):
self.rbNone.Enable(flag) self.rbNone.Enable(flag)
Page.enableAll(self, flag) Page.enableAll(self, flag)
def onAccTypeSelect(self, evt): def onAccTypeSelect(self, evt):
self.assertModified(True) self.assertModified(True)
rb = evt.GetEventObject() rb = evt.GetEventObject()
label = rb.GetLabel() label = rb.GetLabel()
if label == self.labels['ACCELERATION_RAMPING']: if label == self.labels["ACCELERATION_RAMPING"]:
ena = True ena = True
else: else:
ena = False ena = False
self.checkBoxes['LOOKAHEAD'].Enable(ena) self.checkBoxes["LOOKAHEAD"].Enable(ena)
self.textControls['ACCELERATION'].Enable(ena) self.textControls["ACCELERATION"].Enable(ena)
evt.Skip() evt.Skip()
def insertValues(self, cfgValues): def insertValues(self, cfgValues):
Page.insertValues(self, cfgValues) Page.insertValues(self, cfgValues)
self.checkBoxes['LOOKAHEAD'].Enable(False) self.checkBoxes["LOOKAHEAD"].Enable(False)
self.textControls['ACCELERATION'].Enable(False) self.textControls["ACCELERATION"].Enable(False)
for tag in self.accTypeKeys: for tag in self.accTypeKeys:
if tag in cfgValues.keys() and cfgValues[tag]: if tag in cfgValues.keys() and cfgValues[tag]:
self.radioButtons[tag].SetValue(True) self.radioButtons[tag].SetValue(True)
if tag == 'ACCELERATION_RAMPING': if tag == "ACCELERATION_RAMPING":
self.checkBoxes['LOOKAHEAD'].Enable(True) self.checkBoxes["LOOKAHEAD"].Enable(True)
self.textControls['ACCELERATION'].Enable(True) self.textControls["ACCELERATION"].Enable(True)
def getValues(self): def getValues(self):
result = Page.getValues(self) result = Page.getValues(self)
for tag in self.accTypeKeys: for tag in self.accTypeKeys:
result[tag] = self.radioButtons[tag].GetValue() result[tag] = self.radioButtons[tag].GetValue()
return result return result

View File

@ -1,216 +1,229 @@
import wx import wx
from configtool.data import BSIZESMALL, offsetChLabel, offsetTcLabel from configtool.data import BSIZESMALL, offsetChLabel, offsetTcLabel
class AddHeaterDlg(wx.Dialog): class AddHeaterDlg(wx.Dialog):
def __init__(self, parent, names, pins, font, def __init__(
name = "", pin = "", invert = "0", pwm = "1", max_pwm = "100"): self,
wx.Dialog.__init__(self, parent, wx.ID_ANY, "Add heater", size = (400, 204)) parent,
self.SetFont(font) names,
self.Bind(wx.EVT_CLOSE, self.onCancel) pins,
font,
name="",
pin="",
invert="0",
pwm="1",
max_pwm="100",
):
wx.Dialog.__init__(self, parent, wx.ID_ANY, "Add heater", size=(400, 204))
self.SetFont(font)
self.Bind(wx.EVT_CLOSE, self.onCancel)
self.names = names self.names = names
self.choices = pins self.choices = pins
self.nameValid = (name != "") self.nameValid = name != ""
self.maxPWMValid = (max_pwm != "") self.maxPWMValid = max_pwm != ""
self.pwmValid = (pwm != "") self.pwmValid = pwm != ""
sz = wx.BoxSizer(wx.VERTICAL) sz = wx.BoxSizer(wx.VERTICAL)
gsz = wx.GridBagSizer() gsz = wx.GridBagSizer()
gsz.Add((20, 20), pos = (0, 0)) gsz.Add((20, 20), pos=(0, 0))
lsz = wx.BoxSizer(wx.HORIZONTAL) lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Heater Name:", size = (80, -1), st = wx.StaticText(
style = wx.ALIGN_RIGHT) self, wx.ID_ANY, "Heater Name:", size=(80, -1), style=wx.ALIGN_RIGHT
st.SetFont(font) )
lsz.Add(st, 1, wx.TOP, offsetTcLabel) st.SetFont(font)
lsz.Add(st, 1, wx.TOP, offsetTcLabel)
self.tcName = wx.TextCtrl(self, wx.ID_ANY, name) self.tcName = wx.TextCtrl(self, wx.ID_ANY, name)
self.tcName.SetFont(font) self.tcName.SetFont(font)
if not name: if not name:
self.tcName.SetBackgroundColour("pink") self.tcName.SetBackgroundColour("pink")
self.tcName.Bind(wx.EVT_TEXT, self.onNameEntry) self.tcName.Bind(wx.EVT_TEXT, self.onNameEntry)
lsz.Add(self.tcName) lsz.Add(self.tcName)
self.tcName.SetToolTip("Enter a unique name for this heater.") self.tcName.SetToolTip("Enter a unique name for this heater.")
gsz.Add(lsz, pos = (1, 1)) gsz.Add(lsz, pos=(1, 1))
lsz = wx.BoxSizer(wx.HORIZONTAL) lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Pin:", size = (80, -1), st = wx.StaticText(self, wx.ID_ANY, "Pin:", size=(80, -1), style=wx.ALIGN_RIGHT)
style = wx.ALIGN_RIGHT) st.SetFont(font)
st.SetFont(font) lsz.Add(st, 1, wx.TOP, offsetChLabel)
lsz.Add(st, 1, wx.TOP, offsetChLabel)
self.chPin = wx.Choice(self, wx.ID_ANY, choices = pins) self.chPin = wx.Choice(self, wx.ID_ANY, choices=pins)
self.chPin.SetFont(font) self.chPin.SetFont(font)
self.chPin.Bind(wx.EVT_CHOICE, self.onChoice) self.chPin.Bind(wx.EVT_CHOICE, self.onChoice)
i = self.chPin.FindString(pin) i = self.chPin.FindString(pin)
if i == wx.NOT_FOUND: if i == wx.NOT_FOUND:
self.chPin.SetSelection(0) self.chPin.SetSelection(0)
else: else:
self.chPin.SetSelection(i) self.chPin.SetSelection(i)
lsz.Add(self.chPin) lsz.Add(self.chPin)
self.chPin.SetToolTip("Choose a pin for this heater.") self.chPin.SetToolTip("Choose a pin for this heater.")
gsz.Add(lsz, pos = (3, 1)) gsz.Add(lsz, pos=(3, 1))
lsz = wx.BoxSizer(wx.HORIZONTAL) lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Max PWM:", size = (80, -1), st = wx.StaticText(
style = wx.ALIGN_RIGHT) self, wx.ID_ANY, "Max PWM:", size=(80, -1), style=wx.ALIGN_RIGHT
st.SetFont(font) )
lsz.Add(st, 1, wx.TOP, offsetChLabel) st.SetFont(font)
lsz.Add(st, 1, wx.TOP, offsetChLabel)
self.tcMaxPWM = wx.TextCtrl(self, wx.ID_ANY, max_pwm) self.tcMaxPWM = wx.TextCtrl(self, wx.ID_ANY, max_pwm)
self.tcMaxPWM.SetFont(font) self.tcMaxPWM.SetFont(font)
self.tcMaxPWM.Bind(wx.EVT_TEXT, self.onMaxPWM) self.tcMaxPWM.Bind(wx.EVT_TEXT, self.onMaxPWM)
lsz.Add(self.tcMaxPWM) lsz.Add(self.tcMaxPWM)
self.tcMaxPWM.SetToolTip("Enter max. PWM value in [%]. Typically \n" self.tcMaxPWM.SetToolTip(
"between 40 and 100. Standard is 100.\n" "Enter max. PWM value in [%]. Typically \n"
"Valid values 1 to 100.") "between 40 and 100. Standard is 100.\n"
"Valid values 1 to 100."
)
gsz.Add(lsz, pos = (5, 1)) gsz.Add(lsz, pos=(5, 1))
self.cbInv = wx.CheckBox(self, wx.ID_ANY, "Invert") self.cbInv = wx.CheckBox(self, wx.ID_ANY, "Invert")
self.cbInv.SetFont(font) self.cbInv.SetFont(font)
self.cbInv.SetValue(int(invert) != 0) self.cbInv.SetValue(int(invert) != 0)
self.cbInv.SetToolTip("Invert the pin signal.") self.cbInv.SetToolTip("Invert the pin signal.")
gsz.Add(self.cbInv, pos = (3, 3)) gsz.Add(self.cbInv, pos=(3, 3))
lsz = wx.BoxSizer(wx.HORIZONTAL) lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "PWM:", size = (60, -1), st = wx.StaticText(self, wx.ID_ANY, "PWM:", size=(60, -1), style=wx.ALIGN_RIGHT)
style = wx.ALIGN_RIGHT) st.SetFont(font)
st.SetFont(font) lsz.Add(st, 1, wx.TOP, offsetChLabel)
lsz.Add(st, 1, wx.TOP, offsetChLabel)
self.tcPwm = wx.TextCtrl(self, wx.ID_ANY, pwm, size=(60, -1)) self.tcPwm = wx.TextCtrl(self, wx.ID_ANY, pwm, size=(60, -1))
self.tcPwm.SetFont(font) self.tcPwm.SetFont(font)
self.tcPwm.Bind(wx.EVT_TEXT, self.onPWM) self.tcPwm.Bind(wx.EVT_TEXT, self.onPWM)
lsz.Add(self.tcPwm) lsz.Add(self.tcPwm)
self.tcPwm.SetToolTip("Use Pulse Width Modulation. " self.tcPwm.SetToolTip(
"Hardware PWM if available or " "Use Pulse Width Modulation. "
"Software PWM. When FORCE_SOFTWARE_PWM " "Hardware PWM if available or "
"is set, always software PWM for 1 and " "Software PWM. When FORCE_SOFTWARE_PWM "
"hardware PWM for >= 2.") "is set, always software PWM for 1 and "
"hardware PWM for >= 2."
)
gsz.Add((50, 15), pos = (1, 2)) gsz.Add((50, 15), pos=(1, 2))
gsz.Add(lsz, pos = (1, 3)) gsz.Add(lsz, pos=(1, 3))
gsz.Add((20, 20), pos = (4, 4)) gsz.Add((20, 20), pos=(4, 4))
sz.Add(gsz) sz.Add(gsz)
sz.Add((30, 30)) sz.Add((30, 30))
bsz = wx.BoxSizer(wx.HORIZONTAL) bsz = wx.BoxSizer(wx.HORIZONTAL)
self.bSave = wx.Button(self, wx.ID_ANY, "Save", size = BSIZESMALL) self.bSave = wx.Button(self, wx.ID_ANY, "Save", size=BSIZESMALL)
self.bSave.SetFont(font) self.bSave.SetFont(font)
self.bSave.Bind(wx.EVT_BUTTON, self.onSave) self.bSave.Bind(wx.EVT_BUTTON, self.onSave)
bsz.Add(self.bSave) bsz.Add(self.bSave)
bsz.Add(30, 100) bsz.Add(30, 100)
self.bCancel = wx.Button(self, wx.ID_ANY, "Cancel", size = BSIZESMALL) self.bCancel = wx.Button(self, wx.ID_ANY, "Cancel", size=BSIZESMALL)
self.bCancel.SetFont(font) self.bCancel.SetFont(font)
self.bCancel.Bind(wx.EVT_BUTTON, self.onCancel) self.bCancel.Bind(wx.EVT_BUTTON, self.onCancel)
bsz.Add(self.bCancel) bsz.Add(self.bCancel)
sz.Add(bsz, flag = wx.ALIGN_CENTER_HORIZONTAL) sz.Add(bsz, flag=wx.ALIGN_CENTER_HORIZONTAL)
sz.Add((10, 10)) sz.Add((10, 10))
self.SetSizer(sz) self.SetSizer(sz)
self.checkDlgValidity() self.checkDlgValidity()
self.Fit() self.Fit()
def onNameEntry(self, evt): def onNameEntry(self, evt):
tc = evt.GetEventObject() tc = evt.GetEventObject()
w = tc.GetValue().strip() w = tc.GetValue().strip()
if w == "": if w == "":
self.nameValid = False self.nameValid = False
else: else:
if w in self.names: if w in self.names:
self.nameValid = False self.nameValid = False
else: else:
self.nameValid = True self.nameValid = True
if self.nameValid: if self.nameValid:
tc.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)) tc.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW))
else: else:
tc.SetBackgroundColour("pink") tc.SetBackgroundColour("pink")
tc.Refresh() tc.Refresh()
self.checkDlgValidity() self.checkDlgValidity()
evt.Skip() evt.Skip()
def onChoice(self, evt): def onChoice(self, evt):
pass pass
def onMaxPWM(self, evt): def onMaxPWM(self, evt):
tc = evt.GetEventObject() tc = evt.GetEventObject()
w = tc.GetValue().strip() w = tc.GetValue().strip()
if w == "": if w == "":
self.maxPWMValid = False self.maxPWMValid = False
else: else:
if int(w) > 0 and int(w) <= 100: if int(w) > 0 and int(w) <= 100:
self.maxPWMValid = True self.maxPWMValid = True
else: else:
self.maxPWMValid = False self.maxPWMValid = False
if self.maxPWMValid: if self.maxPWMValid:
tc.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)) tc.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW))
else: else:
tc.SetBackgroundColour("pink") tc.SetBackgroundColour("pink")
tc.Refresh() tc.Refresh()
self.checkDlgValidity() self.checkDlgValidity()
if evt is not None: if evt is not None:
evt.Skip() evt.Skip()
def onPWM(self, evt): def onPWM(self, evt):
tc = evt.GetEventObject() tc = evt.GetEventObject()
w = tc.GetValue().strip() w = tc.GetValue().strip()
if w == "": if w == "":
self.pwmValid = False self.pwmValid = False
else: else:
if int(w) >= 0: if int(w) >= 0:
self.pwmValid = True self.pwmValid = True
else: else:
self.pwmValid = False self.pwmValid = False
if self.pwmValid: if self.pwmValid:
tc.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)) tc.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW))
else: else:
tc.SetBackgroundColour("pink") tc.SetBackgroundColour("pink")
tc.Refresh() tc.Refresh()
self.checkDlgValidity() self.checkDlgValidity()
if evt is not None: if evt is not None:
evt.Skip() evt.Skip()
def checkDlgValidity(self): def checkDlgValidity(self):
if (self.nameValid and self.maxPWMValid and self.pwmValid): if self.nameValid and self.maxPWMValid and self.pwmValid:
self.bSave.Enable(True) self.bSave.Enable(True)
else: else:
self.bSave.Enable(False) self.bSave.Enable(False)
def getValues(self): def getValues(self):
nm = self.tcName.GetValue() nm = self.tcName.GetValue()
pin = self.choices[self.chPin.GetSelection()] pin = self.choices[self.chPin.GetSelection()]
if self.cbInv.IsChecked(): if self.cbInv.IsChecked():
invert = "1" invert = "1"
else: else:
invert = "0" invert = "0"
pwm = self.tcPwm.GetValue() pwm = self.tcPwm.GetValue()
max_pwm = self.tcMaxPWM.GetValue() max_pwm = self.tcMaxPWM.GetValue()
return (nm, pin, invert, pwm, max_pwm) return (nm, pin, invert, pwm, max_pwm)
def onSave(self, evt): def onSave(self, evt):
self.EndModal(wx.ID_OK) self.EndModal(wx.ID_OK)
def onCancel(self, evt): def onCancel(self, evt):
self.EndModal(wx.ID_CANCEL) self.EndModal(wx.ID_CANCEL)

File diff suppressed because it is too large Load Diff

View File

@ -4,488 +4,534 @@ import os
import re import re
from sys import platform from sys import platform
from configtool.data import (defineValueFormat, from configtool.data import (
defineBoolFormat, defineHeaterFormat, defineValueFormat,
reHelpTextStart, reHelpTextEnd, defineBoolFormat,
reStartSensors, reEndSensors, reStartHeaters, defineHeaterFormat,
reEndHeaters, reCandHeatPins, reCandThermPins, reHelpTextStart,
reCandProcessors, reCandCPUClocks, reFloatAttr, reHelpTextEnd,
reDefine, reDefineBL, reDefQS, reDefQSm, reStartSensors,
reDefQSm2, reDefBool, reDefBoolBL, reDefHT, reEndSensors,
reDefTS, reDefTT, reSensor, reHeater3, reHeater4, reStartHeaters,
reHeater5, reTempTable4, reTempTable7) reEndHeaters,
reCandHeatPins,
reCandThermPins,
reCandProcessors,
reCandCPUClocks,
reFloatAttr,
reDefine,
reDefineBL,
reDefQS,
reDefQSm,
reDefQSm2,
reDefBool,
reDefBoolBL,
reDefHT,
reDefTS,
reDefTT,
reSensor,
reHeater3,
reHeater4,
reHeater5,
reTempTable4,
reTempTable7,
)
class Board: class Board:
def __init__(self, settings): def __init__(self, settings):
self.settings = settings self.settings = settings
self.configFile = None self.configFile = None
self.cfgDir = os.path.join(self.settings.folder, "configtool") self.cfgDir = os.path.join(self.settings.folder, "configtool")
self.cfgValues = {} self.cfgValues = {}
self.heaters = [] self.heaters = []
self.sensors = [] self.sensors = []
self.candHeatPins = [] self.candHeatPins = []
self.candThermPins = [] self.candThermPins = []
def getValues(self): def getValues(self):
vars = [("sensor." + x[0], x[1:]) for x in self.sensors] vars = [("sensor." + x[0], x[1:]) for x in self.sensors]
vars += [("heater." + x[0], x[1:]) for x in self.heaters] vars += [("heater." + x[0], x[1:]) for x in self.heaters]
vars += [(x, self.cfgValues[x]) for x in self.cfgValues] vars += [(x, self.cfgValues[x]) for x in self.cfgValues]
return dict(vars) return dict(vars)
def getCPUInfo(self): def getCPUInfo(self):
vF_CPU = None vF_CPU = None
if 'F_CPU' in self.cfgValues.keys(): if "F_CPU" in self.cfgValues.keys():
vF_CPU = self.cfgValues['F_CPU'][0] vF_CPU = self.cfgValues["F_CPU"][0]
vCPU = None vCPU = None
if 'CPU' in self.cfgValues.keys(): if "CPU" in self.cfgValues.keys():
vCPU = self.cfgValues['CPU'][0] vCPU = self.cfgValues["CPU"][0]
return vF_CPU, vCPU return vF_CPU, vCPU
def hasData(self): def hasData(self):
return (self.configFile != None) return self.configFile != None
def getFileName(self): def getFileName(self):
return self.configFile return self.configFile
def loadConfigFile(self, fn): def loadConfigFile(self, fn):
cfgFn = os.path.join(self.cfgDir, "board.generic.h") cfgFn = os.path.join(self.cfgDir, "board.generic.h")
try: try:
self.cfgBuffer = list(open(cfgFn)) self.cfgBuffer = list(open(cfgFn))
except: except:
return False, cfgFn return False, cfgFn
try: try:
self.userBuffer = list(open(fn)) self.userBuffer = list(open(fn))
except: except:
return False, fn return False, fn
self.configFile = fn self.configFile = fn
self.sensors = [] self.sensors = []
self.heaters = [] self.heaters = []
self.candHeatPins = [] self.candHeatPins = []
self.candThermPins = [] self.candThermPins = []
self.candProcessors = [] self.candProcessors = []
self.candClocks = [] self.candClocks = []
self.tempTables = {} self.tempTables = {}
gatheringHelpText = False gatheringHelpText = False
helpTextString = "" helpTextString = ""
helpKey = None helpKey = None
self.cfgValues = {} self.cfgValues = {}
self.cfgNames = [] self.cfgNames = []
self.helpText = {} self.helpText = {}
prevLines = ""
for ln in self.cfgBuffer:
if gatheringHelpText:
if reHelpTextEnd.match(ln):
gatheringHelpText = False
helpTextString = helpTextString.strip()
# Keep paragraphs with double-newline.
helpTextString = helpTextString.replace("\n\n ", "\n\n")
# Keep indented lines, typically a list.
helpTextString = helpTextString.replace("\n\n ", "\n\n ")
helpTextString = helpTextString.replace("\n ", "\n\n ")
# Remove all other newlines and indents.
helpTextString = helpTextString.replace("\n ", " ")
hk = helpKey.split()
for k in hk:
self.helpText[k] = helpTextString
helpTextString = ""
helpKey = None
continue
else:
helpTextString += ln
continue
m = reHelpTextStart.match(ln)
if m:
t = m.groups()
gatheringHelpText = True
helpKey = t[0]
continue
if ln.rstrip().endswith("\\"):
prevLines += ln.rstrip()[:-1]
continue
if prevLines != "":
ln = prevLines + ln
prevLines = "" prevLines = ""
for ln in self.cfgBuffer:
if gatheringHelpText:
if reHelpTextEnd.match(ln):
gatheringHelpText = False
helpTextString = helpTextString.strip()
# Keep paragraphs with double-newline.
helpTextString = helpTextString.replace("\n\n ", "\n\n")
# Keep indented lines, typically a list.
helpTextString = helpTextString.replace("\n\n ", "\n\n ")
helpTextString = helpTextString.replace("\n ", "\n\n ")
# Remove all other newlines and indents.
helpTextString = helpTextString.replace("\n ", " ")
hk = helpKey.split()
for k in hk:
self.helpText[k] = helpTextString
helpTextString = ""
helpKey = None
continue
else:
helpTextString += ln
continue
self.parseDefineName(ln) m = reHelpTextStart.match(ln)
self.parseDefineValue(ln) if m:
t = m.groups()
gatheringHelpText = True
helpKey = t[0]
continue
# Set all boolean generic configuration items to False, so items not yet if ln.rstrip().endswith("\\"):
# existing in the user configuration default to disabled. prevLines += ln.rstrip()[:-1]
# continue
# An alternative would be to allow both, enabled and disabled booleans
# in board.generic.h, which then allows to set an appropriate default for
# each #define. This was tried but conflicted with config file writing code
# below (disabled #defines were reset to the default, even when set
# differently in the GUI), so this would need adjustment, too.
for k in self.cfgValues.keys():
if isinstance(self.cfgValues[k], bool):
self.cfgValues[k] = False
# Read the user configuration. This usually overwrites all of the items if prevLines != "":
# read above, but not those missing in the user configuration, e.g. ln = prevLines + ln
# when reading an older config. prevLines = ""
gatheringHelpText = False
prevLines = ""
for ln in self.userBuffer:
if gatheringHelpText:
if reHelpTextEnd.match(ln):
gatheringHelpText = False
continue
if reHelpTextStart.match(ln): self.parseDefineName(ln)
gatheringHelpText = True self.parseDefineValue(ln)
continue
if ln.rstrip().endswith("\\"): # Set all boolean generic configuration items to False, so items not yet
prevLines += ln.rstrip()[:-1] # existing in the user configuration default to disabled.
continue #
# An alternative would be to allow both, enabled and disabled booleans
# in board.generic.h, which then allows to set an appropriate default for
# each #define. This was tried but conflicted with config file writing code
# below (disabled #defines were reset to the default, even when set
# differently in the GUI), so this would need adjustment, too.
for k in self.cfgValues.keys():
if isinstance(self.cfgValues[k], bool):
self.cfgValues[k] = False
if prevLines != "": # Read the user configuration. This usually overwrites all of the items
ln = prevLines + ln # read above, but not those missing in the user configuration, e.g.
# when reading an older config.
gatheringHelpText = False
prevLines = "" prevLines = ""
for ln in self.userBuffer:
if gatheringHelpText:
if reHelpTextEnd.match(ln):
gatheringHelpText = False
continue
if self.parseCandidateValues(ln): if reHelpTextStart.match(ln):
continue gatheringHelpText = True
continue
if self.parseDefineValue(ln): if ln.rstrip().endswith("\\"):
continue prevLines += ln.rstrip()[:-1]
continue
m = reDefTS.search(ln) if prevLines != "":
if m: ln = prevLines + ln
t = m.groups() prevLines = ""
if len(t) == 1:
s = self.parseSensor(t[0])
if s:
self.sensors.append(s)
continue
m = reDefHT.search(ln) if self.parseCandidateValues(ln):
if m: continue
t = m.groups()
if len(t) == 1:
s = self.parseHeater(t[0])
if s:
self.heaters.append(s)
continue
# Parsing done. All parsed stuff is now in these arrays and dicts. if self.parseDefineValue(ln):
if self.settings.verbose >= 2: continue
print(self.sensors)
print(self.heaters)
print(self.candHeatPins)
print(self.candThermPins)
print(self.candProcessors)
print(self.candClocks)
print(self.tempTables)
print(self.cfgValues) # #defines with a value.
print(self.cfgNames) # Names found in the generic file.
if self.settings.verbose >= 3:
print(self.helpText)
for k in range(len(self.sensors)): m = reDefTS.search(ln)
tn = self.sensors[k][0].upper() if m:
if tn in self.tempTables.keys(): t = m.groups()
self.sensors[k][3] = self.tempTables[tn] if len(t) == 1:
else: s = self.parseSensor(t[0])
self.sensors[k][3] = None if s:
self.sensors.append(s)
continue
return True, None m = reDefHT.search(ln)
if m:
t = m.groups()
if len(t) == 1:
s = self.parseHeater(t[0])
if s:
self.heaters.append(s)
continue
def parseDefineName(self, ln): # Parsing done. All parsed stuff is now in these arrays and dicts.
m = reDefBool.search(ln) if self.settings.verbose >= 2:
if m: print(self.sensors)
t = m.groups() print(self.heaters)
if len(t) == 1: print(self.candHeatPins)
self.cfgNames.append(t[0]) print(self.candThermPins)
return True print(self.candProcessors)
print(self.candClocks)
print(self.tempTables)
print(self.cfgValues) # #defines with a value.
print(self.cfgNames) # Names found in the generic file.
if self.settings.verbose >= 3:
print(self.helpText)
return False for k in range(len(self.sensors)):
tn = self.sensors[k][0].upper()
def parseDefineValue(self, ln): if tn in self.tempTables.keys():
m = reDefQS.search(ln) self.sensors[k][3] = self.tempTables[tn]
if m:
t = m.groups()
if len(t) == 2:
m = reDefQSm.search(ln)
if m:
t = m.groups()
tt = re.findall(reDefQSm2, t[1])
if len(tt) == 1 and (t[0] in self.cfgNames):
self.cfgValues[t[0]] = tt[0], True
return True
elif len(tt) > 1 and (t[0] in self.cfgNames):
self.cfgValues[t[0]] = tt, True
return True
m = reDefine.search(ln)
if m:
t = m.groups()
if len(t) == 2 and (t[0] in self.cfgNames):
if reDefineBL.search(ln):
self.cfgValues[t[0]] = t[1], True
else:
self.cfgValues[t[0]] = t[1], False
return True
m = reDefBool.search(ln)
if m:
t = m.groups()
# Accept booleans, but not those for which a value exists already.
# Booleans already existing as values are most likely misconfigured
# manual edits (or result of a bug).
if len(t) == 1 and t[0] in self.cfgNames \
and not (t[0] in self.cfgValues \
and isinstance(self.cfgValues[t[0]], tuple)):
if reDefBoolBL.search(ln):
self.cfgValues[t[0]] = True
else:
self.cfgValues[t[0]] = False
return True
return False
def parseCandidateValues(self, ln):
m = reCandThermPins.match(ln)
if m:
t = m.groups()
if len(t) == 1:
self.candThermPins.append(t[0])
return True
m = reCandHeatPins.match(ln)
if m:
t = m.groups()
if len(t) == 1:
self.candHeatPins.append(t[0])
return True
m = reCandProcessors.match(ln)
if m:
t = m.groups()
if len(t) == 1:
self.candProcessors.append(t[0])
return True
m = reCandCPUClocks.match(ln)
if m:
t = m.groups()
if len(t) == 1:
self.candClocks.append(t[0])
return True
m = reDefTT.match(ln)
if m:
t = m.groups()
if len(t) == 2:
s = self.parseTempTable(t[1])
if s:
self.tempTables[t[0]] = s
return True
return False
def parseSensor(self, s):
m = reSensor.search(s)
if m:
t = m.groups()
if len(t) == 4:
return list(t)
return None
def parseHeater(self, s):
m = reHeater5.search(s)
if m:
t = m.groups()
if len(t) == 5:
return list(t)
# reHeater4 deprecated, for compatibility with old config files only.
m = reHeater4.search(s)
if m:
t = m.groups()
if len(t) == 4:
t = list(t)
t.insert(5, '100')
return t
# reHeater3 deprecated, for compatibility with old config files only.
m = reHeater3.search(s)
if m:
t = m.groups()
if len(t) == 3:
t = list(t)
t.insert(2, '0')
t.insert(5, '100')
return t
# End of deprecated part.
return None
def parseTempTable(self, s):
m = reTempTable4.search(s)
if m:
t = m.groups()
if len(t) == 4:
return list(t)
m = reTempTable7.search(s)
if m:
t = m.groups()
if len(t) == 7:
return list(t)
return None
def saveConfigFile(self, path, values):
if not values:
values = self.cfgValues
if self.settings.verbose >= 1:
print("Saving board: %s." % path)
if self.settings.verbose >= 2:
print(values)
fp = file(path, 'w')
self.configFile = path
skipToSensorEnd = False
skipToHeaterEnd = False
candThermPinsWritten = False
candHeatPinsWritten = False
candProcessorsWritten = False
candCPUClocksWritten = False
for ln in self.cfgBuffer:
m = reStartSensors.match(ln)
if m:
fp.write(ln)
fp.write("// name type pin "
"additional\n");
ttString = "\n"
ttString += "// Beta algorithm r0 beta r2 vadc\n"
ttString += "// Steinhart-Hart rp t0 r0 t1 "
ttString += "r1 t2 r2\n"
for s in self.sensors:
sstr = "%-10s%-15s%-7s" % ((s[0] + ","), (s[1] + ","), (s[2] + ","))
if s[3] is None:
sstr += "0"
else:
sstr += "THERMISTOR_%s" % s[0].upper()
tt = s[3]
if len(tt) == 4:
ttString += "//TEMP_TABLE %-8s (%-8s%-6s%-6s%s)\n" % \
(s[0].upper(), (tt[0] + ","), (tt[1] + ","),
(tt[2] + ","), tt[3])
else: else:
ttString += "//TEMP_TABLE %-8s (%-8s%-6s%-8s%-6s%-8s%-6s%s)\n" % \ self.sensors[k][3] = None
(s[0].upper(), (tt[0] + ","), (tt[1] + ","),
(tt[2] + ","), (tt[3] + ","), (tt[4] + ","),
(tt[5] + ","), tt[6])
fp.write("DEFINE_TEMP_SENSOR(%s)\n" % sstr)
fp.write(ttString)
skipToSensorEnd = True
continue
if skipToSensorEnd: return True, None
m = reEndSensors.match(ln)
def parseDefineName(self, ln):
m = reDefBool.search(ln)
if m: if m:
fp.write(ln) t = m.groups()
skipToSensorEnd = False if len(t) == 1:
continue self.cfgNames.append(t[0])
return True
m = reStartHeaters.match(ln) return False
if m:
fp.write(ln)
fp.write("// name pin invert pwm max_pwm\n")
for s in self.heaters:
sstr = "%-10s%-9s%-8s%-7s%s" % ((s[0] + ","), (s[1] + ","),
(s[2] + ","), s[3] + ",", s[4])
fp.write("DEFINE_HEATER(%s)\n" % sstr)
fp.write("\n")
for s in self.heaters:
fp.write(defineHeaterFormat % (s[0].upper(), s[0]))
skipToHeaterEnd = True
continue
if skipToHeaterEnd: def parseDefineValue(self, ln):
m = reEndHeaters.match(ln) m = reDefQS.search(ln)
if m: if m:
fp.write(ln) t = m.groups()
skipToHeaterEnd = False if len(t) == 2:
continue m = reDefQSm.search(ln)
if m:
t = m.groups()
tt = re.findall(reDefQSm2, t[1])
if len(tt) == 1 and (t[0] in self.cfgNames):
self.cfgValues[t[0]] = tt[0], True
return True
elif len(tt) > 1 and (t[0] in self.cfgNames):
self.cfgValues[t[0]] = tt, True
return True
if reCandThermPins.match(ln): m = reDefine.search(ln)
if not candThermPinsWritten: if m:
for pin in self.candThermPins: t = m.groups()
fp.write("//#define TEMP_SENSOR_PIN " + pin + "\n") if len(t) == 2 and (t[0] in self.cfgNames):
candThermPinsWritten = True if reDefineBL.search(ln):
continue self.cfgValues[t[0]] = t[1], True
else:
self.cfgValues[t[0]] = t[1], False
return True
if reCandHeatPins.match(ln): m = reDefBool.search(ln)
if not candHeatPinsWritten: if m:
for pin in self.candHeatPins: t = m.groups()
fp.write("//#define HEATER_PIN " + pin + "\n") # Accept booleans, but not those for which a value exists already.
candHeatPinsWritten = True # Booleans already existing as values are most likely misconfigured
continue # manual edits (or result of a bug).
if (
len(t) == 1
and t[0] in self.cfgNames
and not (
t[0] in self.cfgValues and isinstance(self.cfgValues[t[0]], tuple)
)
):
if reDefBoolBL.search(ln):
self.cfgValues[t[0]] = True
else:
self.cfgValues[t[0]] = False
return True
if reCandProcessors.match(ln): return False
if not candProcessorsWritten:
for pin in self.candProcessors:
fp.write("//#define CPU_TYPE " + pin + "\n")
candProcessorsWritten = True
continue
if reCandCPUClocks.match(ln): def parseCandidateValues(self, ln):
if not candCPUClocksWritten: m = reCandThermPins.match(ln)
for pin in self.candClocks: if m:
fp.write("//#define F_CPU_OPT " + pin + "\n") t = m.groups()
candCPUClocksWritten = True if len(t) == 1:
continue self.candThermPins.append(t[0])
return True
m = reCandHeatPins.match(ln)
if m:
t = m.groups()
if len(t) == 1:
self.candHeatPins.append(t[0])
return True
m = reCandProcessors.match(ln)
if m:
t = m.groups()
if len(t) == 1:
self.candProcessors.append(t[0])
return True
m = reCandCPUClocks.match(ln)
if m:
t = m.groups()
if len(t) == 1:
self.candClocks.append(t[0])
return True
m = reDefTT.match(ln)
if m:
t = m.groups()
if len(t) == 2:
s = self.parseTempTable(t[1])
if s:
self.tempTables[t[0]] = s
return True
return False
def parseSensor(self, s):
m = reSensor.search(s)
if m:
t = m.groups()
if len(t) == 4:
return list(t)
return None
def parseHeater(self, s):
m = reHeater5.search(s)
if m:
t = m.groups()
if len(t) == 5:
return list(t)
# reHeater4 deprecated, for compatibility with old config files only.
m = reHeater4.search(s)
if m:
t = m.groups()
if len(t) == 4:
t = list(t)
t.insert(5, "100")
return t
# reHeater3 deprecated, for compatibility with old config files only.
m = reHeater3.search(s)
if m:
t = m.groups()
if len(t) == 3:
t = list(t)
t.insert(2, "0")
t.insert(5, "100")
return t
# End of deprecated part.
return None
def parseTempTable(self, s):
m = reTempTable4.search(s)
if m:
t = m.groups()
if len(t) == 4:
return list(t)
m = reTempTable7.search(s)
if m:
t = m.groups()
if len(t) == 7:
return list(t)
return None
def saveConfigFile(self, path, values):
if not values:
values = self.cfgValues
if self.settings.verbose >= 1:
print("Saving board: %s." % path)
if self.settings.verbose >= 2:
print(values)
fp = file(path, "w")
self.configFile = path
skipToSensorEnd = False
skipToHeaterEnd = False
candThermPinsWritten = False
candHeatPinsWritten = False
candProcessorsWritten = False
candCPUClocksWritten = False
for ln in self.cfgBuffer:
m = reStartSensors.match(ln)
if m:
fp.write(ln)
fp.write(
"// name type pin " "additional\n"
)
ttString = "\n"
ttString += "// Beta algorithm r0 beta r2 vadc\n"
ttString += "// Steinhart-Hart rp t0 r0 t1 "
ttString += "r1 t2 r2\n"
for s in self.sensors:
sstr = "%-10s%-15s%-7s" % ((s[0] + ","), (s[1] + ","), (s[2] + ","))
if s[3] is None:
sstr += "0"
else:
sstr += "THERMISTOR_%s" % s[0].upper()
tt = s[3]
if len(tt) == 4:
ttString += "//TEMP_TABLE %-8s (%-8s%-6s%-6s%s)\n" % (
s[0].upper(),
(tt[0] + ","),
(tt[1] + ","),
(tt[2] + ","),
tt[3],
)
else:
ttString += (
"//TEMP_TABLE %-8s (%-8s%-6s%-8s%-6s%-8s%-6s%s)\n"
% (
s[0].upper(),
(tt[0] + ","),
(tt[1] + ","),
(tt[2] + ","),
(tt[3] + ","),
(tt[4] + ","),
(tt[5] + ","),
tt[6],
)
)
fp.write("DEFINE_TEMP_SENSOR(%s)\n" % sstr)
fp.write(ttString)
skipToSensorEnd = True
continue
if skipToSensorEnd:
m = reEndSensors.match(ln)
if m:
fp.write(ln)
skipToSensorEnd = False
continue
m = reStartHeaters.match(ln)
if m:
fp.write(ln)
fp.write("// name pin invert pwm max_pwm\n")
for s in self.heaters:
sstr = "%-10s%-9s%-8s%-7s%s" % (
(s[0] + ","),
(s[1] + ","),
(s[2] + ","),
s[3] + ",",
s[4],
)
fp.write("DEFINE_HEATER(%s)\n" % sstr)
fp.write("\n")
for s in self.heaters:
fp.write(defineHeaterFormat % (s[0].upper(), s[0]))
skipToHeaterEnd = True
continue
if skipToHeaterEnd:
m = reEndHeaters.match(ln)
if m:
fp.write(ln)
skipToHeaterEnd = False
continue
if reCandThermPins.match(ln):
if not candThermPinsWritten:
for pin in self.candThermPins:
fp.write("//#define TEMP_SENSOR_PIN " + pin + "\n")
candThermPinsWritten = True
continue
if reCandHeatPins.match(ln):
if not candHeatPinsWritten:
for pin in self.candHeatPins:
fp.write("//#define HEATER_PIN " + pin + "\n")
candHeatPinsWritten = True
continue
if reCandProcessors.match(ln):
if not candProcessorsWritten:
for pin in self.candProcessors:
fp.write("//#define CPU_TYPE " + pin + "\n")
candProcessorsWritten = True
continue
if reCandCPUClocks.match(ln):
if not candCPUClocksWritten:
for pin in self.candClocks:
fp.write("//#define F_CPU_OPT " + pin + "\n")
candCPUClocksWritten = True
continue
m = reDefine.match(ln)
if m:
t = m.groups()
if len(t) == 2 and t[0] in values.keys():
v = values[t[0]]
self.cfgValues[t[0]] = v
if v[1] == False:
fp.write("//")
fp.write(defineValueFormat % (t[0], v[0]))
else:
if t[0] == "RX_ENABLE_PIN" or t[0] == "TX_ENABLE_PIN":
# Known to be absent in the GUI, also won't be added anytime soon.
fp.write(ln)
else:
print("Value key " + t[0] + " not found in GUI.")
continue
m = reDefBoolBL.match(ln)
if m:
t = m.groups()
if len(t) == 1 and t[0] in values.keys():
v = values[t[0]]
self.cfgValues[t[0]] = v
if v == "" or v == False:
fp.write("//")
fp.write(defineBoolFormat % t[0])
else:
if t[0] == "MOTHERBOARD":
# Known to be absent in the GUI, also won't be added anytime soon.
fp.write(ln)
else:
print("Boolean key " + t[0] + " not found in GUI.")
continue
m = reDefine.match(ln)
if m:
t = m.groups()
if len(t) == 2 and t[0] in values.keys():
v = values[t[0]]
self.cfgValues[t[0]] = v
if v[1] == False:
fp.write("//")
fp.write(defineValueFormat % (t[0], v[0]))
else:
if t[0] == 'RX_ENABLE_PIN' or t[0] == 'TX_ENABLE_PIN':
# Known to be absent in the GUI, also won't be added anytime soon.
fp.write(ln) fp.write(ln)
else:
print("Value key " + t[0] + " not found in GUI.")
continue fp.close()
m = reDefBoolBL.match(ln) return True
if m:
t = m.groups()
if len(t) == 1 and t[0] in values.keys():
v = values[t[0]]
self.cfgValues[t[0]] = v
if v == "" or v == False:
fp.write("//")
fp.write(defineBoolFormat % t[0])
else:
if t[0] == 'MOTHERBOARD':
# Known to be absent in the GUI, also won't be added anytime soon.
fp.write(ln)
else:
print("Boolean key " + t[0] + " not found in GUI.")
continue
fp.write(ln)
fp.close()
return True

View File

@ -1,20 +1,40 @@
import os import os
import wx import wx
import re import re
from sys import platform from sys import platform
from configtool.decoration import Decoration from configtool.decoration import Decoration
from configtool.data import (defineValueFormat, from configtool.data import (
defineBoolFormat, defineHeaterFormat, defineValueFormat,
reHelpTextStart, reHelpTextEnd, defineBoolFormat,
reStartSensors, reEndSensors, reStartHeaters, defineHeaterFormat,
reEndHeaters, reCandHeatPins, reCandThermPins, reHelpTextStart,
reCandProcessors, reCandCPUClocks, reFloatAttr, reHelpTextEnd,
reDefine, reDefineBL, reDefQS, reDefQSm, reStartSensors,
reDefQSm2, reDefBool, reDefBoolBL, reDefHT, reEndSensors,
reDefTS, reDefTT, reSensor, reHeater3, reHeater4, reStartHeaters,
reTempTable4, reTempTable7) reEndHeaters,
reCandHeatPins,
reCandThermPins,
reCandProcessors,
reCandCPUClocks,
reFloatAttr,
reDefine,
reDefineBL,
reDefQS,
reDefQSm,
reDefQSm2,
reDefBool,
reDefBoolBL,
reDefHT,
reDefTS,
reDefTT,
reSensor,
reHeater3,
reHeater4,
reTempTable4,
reTempTable7,
)
from configtool.pinoutspage import PinoutsPage from configtool.pinoutspage import PinoutsPage
from configtool.displaypage import DisplayPage from configtool.displaypage import DisplayPage
from configtool.sensorpage import SensorsPage from configtool.sensorpage import SensorsPage
@ -26,269 +46,302 @@ from configtool.thermistortablefile import generateTempTables
from configtool.board import Board from configtool.board import Board
class BoardPanel(wx.Panel): class BoardPanel(wx.Panel):
def __init__(self, parent, nb, settings): def __init__(self, parent, nb, settings):
wx.Panel.__init__(self, nb, wx.ID_ANY) wx.Panel.__init__(self, nb, wx.ID_ANY)
self.parent = parent self.parent = parent
self.deco = Decoration() self.deco = Decoration()
self.protFileLoaded = False self.protFileLoaded = False
self.settings = settings self.settings = settings
self.board = Board(self.settings) self.board = Board(self.settings)
self.dir = os.path.join(self.settings.folder, "config") self.dir = os.path.join(self.settings.folder, "config")
self.SetBackgroundColour(self.deco.getBackgroundColour()) self.SetBackgroundColour(self.deco.getBackgroundColour())
self.Bind(wx.EVT_PAINT, self.deco.onPaintBackground) self.Bind(wx.EVT_PAINT, self.deco.onPaintBackground)
sz = wx.BoxSizer(wx.HORIZONTAL) sz = wx.BoxSizer(wx.HORIZONTAL)
self.nb = wx.Notebook(self, wx.ID_ANY, size = (21, 21), self.nb = wx.Notebook(self, wx.ID_ANY, size=(21, 21), style=wx.BK_DEFAULT)
style = wx.BK_DEFAULT) self.nb.SetBackgroundColour(self.deco.getBackgroundColour())
self.nb.SetBackgroundColour(self.deco.getBackgroundColour()) self.nb.SetFont(self.settings.font)
self.nb.SetFont(self.settings.font)
self.pages = [] self.pages = []
self.titles = [] self.titles = []
self.pageModified = [] self.pageModified = []
self.pageValid = [] self.pageValid = []
self.pgCpu = self.registerPage(CpuPage, "CPU") self.pgCpu = self.registerPage(CpuPage, "CPU")
self.pgPins = self.registerPage(PinoutsPage, "Pinouts") self.pgPins = self.registerPage(PinoutsPage, "Pinouts")
self.pgDisplay = self.registerPage(DisplayPage, "Display") self.pgDisplay = self.registerPage(DisplayPage, "Display")
self.pgHeaters = self.registerPage(HeatersPage, "Heaters") self.pgHeaters = self.registerPage(HeatersPage, "Heaters")
self.pgSensors = self.registerPage(SensorsPage, "Temperature Sensors", self.pgSensors = self.registerPage(
heatersPage = self.pgHeaters) SensorsPage, "Temperature Sensors", heatersPage=self.pgHeaters
self.pgCommunications = self.registerPage(CommunicationsPage, )
"Communications") self.pgCommunications = self.registerPage(CommunicationsPage, "Communications")
sz.Add(self.nb, 1, wx.EXPAND + wx.ALL, 5) sz.Add(self.nb, 1, wx.EXPAND + wx.ALL, 5)
self.SetSizer(sz) self.SetSizer(sz)
self.Fit() self.Fit()
def registerPage(self, klass, label, *args, **kwargs): def registerPage(self, klass, label, *args, **kwargs):
page = klass(self, self.nb, len(self.pages), *args, page = klass(
font = self.settings.font, **kwargs) self, self.nb, len(self.pages), *args, font=self.settings.font, **kwargs
self.nb.AddPage(page, label) )
self.pages.append(page) self.nb.AddPage(page, label)
self.titles.append(label) self.pages.append(page)
self.pageModified.append(False) self.titles.append(label)
self.pageValid.append(True) self.pageModified.append(False)
return page self.pageValid.append(True)
return page
def getCPUInfo(self): def getCPUInfo(self):
return self.board.getCPUInfo() return self.board.getCPUInfo()
def assertModified(self, pg, flag = True): def assertModified(self, pg, flag=True):
self.pageModified[pg] = flag self.pageModified[pg] = flag
self.modifyTab(pg) self.modifyTab(pg)
def isModified(self): def isModified(self):
return (True in self.pageModified) return True in self.pageModified
def isValid(self): def isValid(self):
return not (False in self.pageValid) return not (False in self.pageValid)
def hasData(self): def hasData(self):
return self.board.hasData() return self.board.hasData()
def getFileName(self): def getFileName(self):
return self.board.getFileName() return self.board.getFileName()
def assertValid(self, pg, flag = True): def assertValid(self, pg, flag=True):
self.pageValid[pg] = flag self.pageValid[pg] = flag
self.modifyTab(pg) self.modifyTab(pg)
if False in self.pageValid: if False in self.pageValid:
self.parent.enableSaveBoard(False, False) self.parent.enableSaveBoard(False, False)
else: else:
self.parent.enableSaveBoard(not self.protFileLoaded, True) self.parent.enableSaveBoard(not self.protFileLoaded, True)
def modifyTab(self, pg): def modifyTab(self, pg):
if self.pageModified[pg] and not self.pageValid[pg]: if self.pageModified[pg] and not self.pageValid[pg]:
pfx = "?* " pfx = "?* "
elif self.pageModified[pg]: elif self.pageModified[pg]:
pfx = "* " pfx = "* "
elif not self.pageValid[pg]: elif not self.pageValid[pg]:
pfx = "? " pfx = "? "
else: else:
pfx = "" pfx = ""
self.nb.SetPageText(pg, pfx + self.titles[pg]) self.nb.SetPageText(pg, pfx + self.titles[pg])
if True in self.pageModified and False in self.pageValid: if True in self.pageModified and False in self.pageValid:
pfx = "?* " pfx = "?* "
elif True in self.pageModified: elif True in self.pageModified:
pfx = "* " pfx = "* "
elif False in self.pageValid: elif False in self.pageValid:
pfx = "? " pfx = "? "
else: else:
pfx = "" pfx = ""
self.parent.setBoardTabDecor(pfx) self.parent.setBoardTabDecor(pfx)
def setHeaters(self, ht): def setHeaters(self, ht):
self.parent.setHeaters(ht) self.parent.setHeaters(ht)
def onClose(self, evt): def onClose(self, evt):
if not self.confirmLoseChanges("exit"): if not self.confirmLoseChanges("exit"):
return return
self.Destroy() self.Destroy()
def confirmLoseChanges(self, msg): def confirmLoseChanges(self, msg):
if True not in self.pageModified: if True not in self.pageModified:
return True return True
dlg = wx.MessageDialog(self, "Are you sure you want to " + msg + "?\n" dlg = wx.MessageDialog(
"There are changes to your board " self,
"configuration that will be lost.", "Are you sure you want to " + msg + "?\n"
"Changes pending", "There are changes to your board "
wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION) "configuration that will be lost.",
rc = dlg.ShowModal() "Changes pending",
dlg.Destroy() wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION,
)
rc = dlg.ShowModal()
dlg.Destroy()
if rc != wx.ID_YES: if rc != wx.ID_YES:
return False return False
return True return True
def onLoadConfig(self, evt): def onLoadConfig(self, evt):
if not self.confirmLoseChanges("load a new board configuration"): if not self.confirmLoseChanges("load a new board configuration"):
return return
if platform.startswith("darwin"): if platform.startswith("darwin"):
# Mac OS X appears to be a bit limited on wildcards. # Mac OS X appears to be a bit limited on wildcards.
wildcard = "Board configuration (board.*.h)|*.h" wildcard = "Board configuration (board.*.h)|*.h"
else: else:
wildcard = "Board configuration (board.*.h)|board.*.h" wildcard = "Board configuration (board.*.h)|board.*.h"
dlg = wx.FileDialog(self, message = "Choose a board config file", dlg = wx.FileDialog(
defaultDir = self.dir, defaultFile = "", self,
wildcard = wildcard, style = wx.FD_OPEN | wx.FD_CHANGE_DIR) message="Choose a board config file",
defaultDir=self.dir,
defaultFile="",
wildcard=wildcard,
style=wx.FD_OPEN | wx.FD_CHANGE_DIR,
)
path = None path = None
if dlg.ShowModal() == wx.ID_OK: if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath() path = dlg.GetPath()
dlg.Destroy() dlg.Destroy()
if path is None: if path is None:
return return
self.dir = os.path.dirname(path) self.dir = os.path.dirname(path)
rc, efn = self.loadConfigFile(path) rc, efn = self.loadConfigFile(path)
if not rc: if not rc:
dlg = wx.MessageDialog(self, "Unable to process file %s." % efn, dlg = wx.MessageDialog(
"File error", wx.OK + wx.ICON_ERROR) self,
dlg.ShowModal() "Unable to process file %s." % efn,
dlg.Destroy() "File error",
return wx.OK + wx.ICON_ERROR,
)
dlg.ShowModal()
dlg.Destroy()
return
def loadConfigFile(self, fn): def loadConfigFile(self, fn):
ok, file = self.board.loadConfigFile(fn) ok, file = self.board.loadConfigFile(fn)
if not ok: if not ok:
return ok, file return ok, file
if os.path.basename(fn) in protectedFiles: if os.path.basename(fn) in protectedFiles:
self.parent.enableSaveBoard(False, True) self.parent.enableSaveBoard(False, True)
self.protFileLoaded = True self.protFileLoaded = True
else: else:
self.protFileLoaded = False self.protFileLoaded = False
self.parent.enableSaveBoard(True, True) self.parent.enableSaveBoard(True, True)
self.parent.setBoardTabFile(os.path.basename(fn)) self.parent.setBoardTabFile(os.path.basename(fn))
self.pgHeaters.setCandidatePins(self.board.candHeatPins) self.pgHeaters.setCandidatePins(self.board.candHeatPins)
self.pgSensors.setCandidatePins(self.board.candThermPins) self.pgSensors.setCandidatePins(self.board.candThermPins)
self.pgCpu.setCandidateProcessors(self.board.candProcessors) self.pgCpu.setCandidateProcessors(self.board.candProcessors)
self.pgCpu.setCandidateClocks(self.board.candClocks) self.pgCpu.setCandidateClocks(self.board.candClocks)
for pg in self.pages: for pg in self.pages:
pg.insertValues(self.board.cfgValues) pg.insertValues(self.board.cfgValues)
pg.setHelpText(self.board.helpText) pg.setHelpText(self.board.helpText)
self.pgSensors.setSensors(self.board.sensors) self.pgSensors.setSensors(self.board.sensors)
self.pgHeaters.setHeaters(self.board.heaters) self.pgHeaters.setHeaters(self.board.heaters)
return True, None return True, None
def onSaveConfig(self, evt): def onSaveConfig(self, evt):
path = self.getFileName() path = self.getFileName()
return self.saveConfigFile(path) return self.saveConfigFile(path)
def onSaveConfigAs(self, evt): def onSaveConfigAs(self, evt):
if platform.startswith("darwin"): if platform.startswith("darwin"):
# Mac OS X appears to be a bit limited on wildcards. # Mac OS X appears to be a bit limited on wildcards.
wildcard = "Board configuration (board.*.h)|*.h" wildcard = "Board configuration (board.*.h)|*.h"
else: else:
wildcard = "Board configuration (board.*.h)|board.*.h" wildcard = "Board configuration (board.*.h)|board.*.h"
dlg = wx.FileDialog(self, message = "Save as ...", defaultDir = self.dir, dlg = wx.FileDialog(
defaultFile = "", wildcard = wildcard, self,
style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) message="Save as ...",
defaultDir=self.dir,
defaultFile="",
wildcard=wildcard,
style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT,
)
val = dlg.ShowModal() val = dlg.ShowModal()
if val != wx.ID_OK: if val != wx.ID_OK:
dlg.Destroy() dlg.Destroy()
return return
path = dlg.GetPath() path = dlg.GetPath()
dlg.Destroy() dlg.Destroy()
rc = self.saveConfigFile(path) rc = self.saveConfigFile(path)
if rc: if rc:
self.parent.setBoardTabFile(os.path.basename(path)) self.parent.setBoardTabFile(os.path.basename(path))
self.protFileLoaded = False self.protFileLoaded = False
self.parent.enableSaveBoard(True, True) self.parent.enableSaveBoard(True, True)
return rc return rc
def saveConfigFile(self, path): def saveConfigFile(self, path):
if os.path.basename(path) in protectedFiles: if os.path.basename(path) in protectedFiles:
dlg = wx.MessageDialog(self, "It's not allowed to overwrite files " dlg = wx.MessageDialog(
"distributed by Teacup. Choose another name.", self,
"Protected file error", wx.OK + wx.ICON_ERROR) "It's not allowed to overwrite files "
dlg.ShowModal() "distributed by Teacup. Choose another name.",
dlg.Destroy() "Protected file error",
return False wx.OK + wx.ICON_ERROR,
)
dlg.ShowModal()
dlg.Destroy()
return False
if not os.path.basename(path).startswith("board."): if not os.path.basename(path).startswith("board."):
dlg = wx.MessageDialog(self, "Illegal file name: %s.\n" dlg = wx.MessageDialog(
"File name must begin with \"board.\"" % path, self,
"Illegal file name", wx.OK + wx.ICON_ERROR) "Illegal file name: %s.\n" 'File name must begin with "board."' % path,
dlg.ShowModal() "Illegal file name",
dlg.Destroy() wx.OK + wx.ICON_ERROR,
return False )
dlg.ShowModal()
dlg.Destroy()
return False
values = {} values = {}
for pg in self.pages: for pg in self.pages:
v1 = pg.getValues() v1 = pg.getValues()
for k in v1.keys(): for k in v1.keys():
values[k] = v1[k] values[k] = v1[k]
ext = os.path.splitext(os.path.basename(path))[1] ext = os.path.splitext(os.path.basename(path))[1]
self.dir = os.path.dirname(path) self.dir = os.path.dirname(path)
if ext == "": if ext == "":
path += ".h" path += ".h"
try: try:
self.board.saveConfigFile(path, values) self.board.saveConfigFile(path, values)
except: except:
dlg = wx.MessageDialog(self, "Unable to write to file %s." % path, dlg = wx.MessageDialog(
"File error", wx.OK + wx.ICON_ERROR) self,
dlg.ShowModal() "Unable to write to file %s." % path,
dlg.Destroy() "File error",
return False wx.OK + wx.ICON_ERROR,
)
dlg.ShowModal()
dlg.Destroy()
return False
return self.generateTempTables() return self.generateTempTables()
def generateTempTables(self): def generateTempTables(self):
if not generateTempTables(self.board.sensors, self.settings): if not generateTempTables(self.board.sensors, self.settings):
dlg = wx.MessageDialog(self, "Error writing to file thermistortable.h.", dlg = wx.MessageDialog(
"File error", wx.OK + wx.ICON_ERROR) self,
dlg.ShowModal() "Error writing to file thermistortable.h.",
dlg.Destroy() "File error",
return False wx.OK + wx.ICON_ERROR,
)
dlg.ShowModal()
dlg.Destroy()
return False
return True return True

View File

@ -1,4 +1,3 @@
import wx.lib.newevent import wx.lib.newevent
import thread, shlex, subprocess import thread, shlex, subprocess
import os, re import os, re
@ -6,7 +5,7 @@ from os.path import isfile, join
from sys import platform from sys import platform
if platform.startswith("win"): if platform.startswith("win"):
from _subprocess import STARTF_USESHOWWINDOW from _subprocess import STARTF_USESHOWWINDOW
(scriptEvent, EVT_SCRIPT_UPDATE) = wx.lib.newevent.NewEvent() (scriptEvent, EVT_SCRIPT_UPDATE) = wx.lib.newevent.NewEvent()
SCRIPT_RUNNING = 1 SCRIPT_RUNNING = 1
@ -14,447 +13,495 @@ SCRIPT_FINISHED = 2
SCRIPT_CANCELLED = 3 SCRIPT_CANCELLED = 3
TOOLPATHS_INSIDE_ARDUINO = [ TOOLPATHS_INSIDE_ARDUINO = [
"hardware/tools/avr/bin/", "hardware/tools/avr/bin/",
"hardware/tools/" # avrdude in Arduino 1.0.x "hardware/tools/",
] ] # avrdude in Arduino 1.0.x
if platform.startswith("darwin"): if platform.startswith("darwin"):
# That's an OS property, the Applicaton Bundle hierarchy. # That's an OS property, the Applicaton Bundle hierarchy.
pathsCopy = TOOLPATHS_INSIDE_ARDUINO pathsCopy = TOOLPATHS_INSIDE_ARDUINO
TOOLPATHS_INSIDE_ARDUINO = [] TOOLPATHS_INSIDE_ARDUINO = []
for path in pathsCopy: for path in pathsCopy:
TOOLPATHS_INSIDE_ARDUINO.append("Contents/Resources/Java/" + path) TOOLPATHS_INSIDE_ARDUINO.append("Contents/Resources/Java/" + path)
TOOLPATHS_INSIDE_ARDUINO.append("Contents/Java/" + path) TOOLPATHS_INSIDE_ARDUINO.append("Contents/Java/" + path)
class ScriptTools: class ScriptTools:
def __init__(self, settings): def __init__(self, settings):
self.settings = settings self.settings = settings
def figureCommandPath(self, baseCommand): def figureCommandPath(self, baseCommand):
findConf = False findConf = False
if baseCommand == "avrdude": if baseCommand == "avrdude":
findConf = True findConf = True
if platform.startswith("win"): if platform.startswith("win"):
baseCommand += ".exe" baseCommand += ".exe"
if self.settings.arduinodir: if self.settings.arduinodir:
cmdpath = self.settings.arduinodir cmdpath = self.settings.arduinodir
for pathOption in TOOLPATHS_INSIDE_ARDUINO: for pathOption in TOOLPATHS_INSIDE_ARDUINO:
cmdpathTry = cmdpath cmdpathTry = cmdpath
for dir in pathOption.split("/"): for dir in pathOption.split("/"):
cmdpathTry = os.path.join(cmdpathTry, dir) cmdpathTry = os.path.join(cmdpathTry, dir)
cmdpathTry = os.path.join(cmdpathTry, baseCommand) cmdpathTry = os.path.join(cmdpathTry, baseCommand)
if os.path.exists(cmdpathTry): if os.path.exists(cmdpathTry):
cmdpath = "\"" + cmdpathTry + "\"" cmdpath = '"' + cmdpathTry + '"'
break break
if findConf: if findConf:
confpath = cmdpath.strip("\"") confpath = cmdpath.strip('"')
exepos = confpath.rfind(".exe") exepos = confpath.rfind(".exe")
if exepos >= 0: if exepos >= 0:
confpath = confpath[0:exepos] confpath = confpath[0:exepos]
confpath += ".conf" confpath += ".conf"
if not os.path.exists(confpath): if not os.path.exists(confpath):
confpath = os.path.split(confpath)[0] confpath = os.path.split(confpath)[0]
confpath = os.path.split(confpath)[0] confpath = os.path.split(confpath)[0]
confpath = os.path.join(confpath, "etc") confpath = os.path.join(confpath, "etc")
confpath = os.path.join(confpath, "avrdude.conf") confpath = os.path.join(confpath, "avrdude.conf")
if os.path.exists(confpath): if os.path.exists(confpath):
cmdpath += " -C \"" + confpath + "\"" cmdpath += ' -C "' + confpath + '"'
else: else:
cmdpath = baseCommand cmdpath = baseCommand
# No need to search avrdude.conf in this case. # No need to search avrdude.conf in this case.
return cmdpath return cmdpath
class ScriptThread: class ScriptThread:
def __init__(self, win, script): def __init__(self, win, script):
self.win = win self.win = win
self.running = False self.running = False
self.cancelled = False self.cancelled = False
self.script = script self.script = script
def Start(self): def Start(self):
self.running = True self.running = True
self.cancelled = False self.cancelled = False
thread.start_new_thread(self.Run, ()) thread.start_new_thread(self.Run, ())
def Stop(self): def Stop(self):
self.cancelled = True self.cancelled = True
def IsRunning(self): def IsRunning(self):
return self.running return self.running
def Run(self): def Run(self):
if platform.startswith("win"):
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= STARTF_USESHOWWINDOW
for cmd in self.script:
evt = scriptEvent(msg = cmd, state = SCRIPT_RUNNING)
wx.PostEvent(self.win, evt)
args = shlex.split(str(cmd))
try:
if platform.startswith("win"): if platform.startswith("win"):
p = subprocess.Popen(args, stderr = subprocess.STDOUT, startupinfo = subprocess.STARTUPINFO()
stdout = subprocess.PIPE, startupinfo.dwFlags |= STARTF_USESHOWWINDOW
startupinfo = startupinfo)
else: for cmd in self.script:
p = subprocess.Popen(args, stderr = subprocess.STDOUT, evt = scriptEvent(msg=cmd, state=SCRIPT_RUNNING)
stdout = subprocess.PIPE)
except:
evt = scriptEvent(msg = "Exception occurred trying to run\n\n%s" % cmd,
state = SCRIPT_CANCELLED)
wx.PostEvent(self.win, evt)
self.running = False
return
obuf = ''
while not self.cancelled:
o = p.stdout.read(1)
if o == '': break
if o == '\r' or o == '\n':
if obuf.strip() != "":
evt = scriptEvent(msg = obuf, state = SCRIPT_RUNNING)
wx.PostEvent(self.win, evt) wx.PostEvent(self.win, evt)
obuf = '' args = shlex.split(str(cmd))
elif ord(o) < 32: try:
pass if platform.startswith("win"):
else: p = subprocess.Popen(
obuf += o args,
stderr=subprocess.STDOUT,
stdout=subprocess.PIPE,
startupinfo=startupinfo,
)
else:
p = subprocess.Popen(
args, stderr=subprocess.STDOUT, stdout=subprocess.PIPE
)
except:
evt = scriptEvent(
msg="Exception occurred trying to run\n\n%s" % cmd,
state=SCRIPT_CANCELLED,
)
wx.PostEvent(self.win, evt)
self.running = False
return
obuf = ""
while not self.cancelled:
o = p.stdout.read(1)
if o == "":
break
if o == "\r" or o == "\n":
if obuf.strip() != "":
evt = scriptEvent(msg=obuf, state=SCRIPT_RUNNING)
wx.PostEvent(self.win, evt)
obuf = ""
elif ord(o) < 32:
pass
else:
obuf += o
if self.cancelled: if self.cancelled:
evt = scriptEvent(msg = None, state = SCRIPT_CANCELLED) evt = scriptEvent(msg=None, state=SCRIPT_CANCELLED)
wx.PostEvent(self.win, evt)
p.kill()
self.running = False
p.wait()
return
rc = p.wait()
if rc != 0:
msg = "RC = " + str(rc) + " - Build terminated"
evt = scriptEvent(msg=msg, state=SCRIPT_CANCELLED)
wx.PostEvent(self.win, evt)
self.running = False
return
evt = scriptEvent(msg="", state=SCRIPT_RUNNING)
wx.PostEvent(self.win, evt)
evt = scriptEvent(msg=None, state=SCRIPT_FINISHED)
wx.PostEvent(self.win, evt) wx.PostEvent(self.win, evt)
p.kill()
self.running = False self.running = False
p.wait()
return
rc = p.wait()
if rc != 0:
msg = "RC = " + str(rc) + " - Build terminated"
evt = scriptEvent(msg = msg, state = SCRIPT_CANCELLED)
wx.PostEvent(self.win, evt)
self.running = False
return
evt = scriptEvent(msg = "", state = SCRIPT_RUNNING)
wx.PostEvent(self.win, evt)
evt = scriptEvent(msg = None, state = SCRIPT_FINISHED)
wx.PostEvent(self.win, evt)
self.running = False
class Build(wx.Dialog): class Build(wx.Dialog):
def __init__(self, parent, settings, f_cpu, cpu): def __init__(self, parent, settings, f_cpu, cpu):
wx.Dialog.__init__(self, parent, wx.ID_ANY, "Build teacup", wx.Dialog.__init__(
style = wx.RESIZE_BORDER + wx.DEFAULT_DIALOG_STYLE) self,
self.settings = settings parent,
self.SetFont(self.settings.font) wx.ID_ANY,
self.root = self.settings.folder "Build teacup",
self.f_cpu = f_cpu style=wx.RESIZE_BORDER + wx.DEFAULT_DIALOG_STYLE,
self.cpu = cpu )
self.Bind(wx.EVT_CLOSE, self.onExit) self.settings = settings
self.cancelPending = False self.SetFont(self.settings.font)
self.root = self.settings.folder
self.f_cpu = f_cpu
self.cpu = cpu
self.Bind(wx.EVT_CLOSE, self.onExit)
self.cancelPending = False
hsz = wx.BoxSizer(wx.HORIZONTAL) hsz = wx.BoxSizer(wx.HORIZONTAL)
hsz.Add((10, 10)) hsz.Add((10, 10))
sz = wx.BoxSizer(wx.VERTICAL) sz = wx.BoxSizer(wx.VERTICAL)
sz.Add((10, 10)) sz.Add((10, 10))
tc = wx.TextCtrl(self, wx.ID_ANY, size = (900, 300), tc = wx.TextCtrl(
style = wx.TE_READONLY + wx.TE_MULTILINE) self, wx.ID_ANY, size=(900, 300), style=wx.TE_READONLY + wx.TE_MULTILINE
sz.Add(tc, 1, wx.EXPAND) )
f = wx.Font(8, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, sz.Add(tc, 1, wx.EXPAND)
wx.FONTWEIGHT_BOLD) f = wx.Font(8, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)
tc.SetFont(f) tc.SetFont(f)
self.log = tc self.log = tc
sz.Add((10, 10)) sz.Add((10, 10))
hsz.Add(sz, 1, wx.EXPAND) hsz.Add(sz, 1, wx.EXPAND)
hsz.Add((10, 10)) hsz.Add((10, 10))
self.SetSizer(hsz) self.SetSizer(hsz)
self.Fit() self.Fit()
builddir = join(self.root, "build") builddir = join(self.root, "build")
if not os.path.exists(builddir): if not os.path.exists(builddir):
os.makedirs(builddir) os.makedirs(builddir)
self.log.AppendText("Directory %s created.\n\n" % builddir) self.log.AppendText("Directory %s created.\n\n" % builddir)
self.compile() self.compile()
def compile(self): def compile(self):
self.generateCompileScript() self.generateCompileScript()
if len(self.script) == 0: if len(self.script) == 0:
self.log.AppendText("Nothing to compile!\n") self.log.AppendText("Nothing to compile!\n")
self.active = False self.active = False
else:
self.Bind(EVT_SCRIPT_UPDATE, self.compileUpdate)
self.t = ScriptThread(self, self.script)
self.active = True
self.t.Start()
def link(self):
self.generateLinkScript()
if len(self.script) == 0:
self.log.AppendText("Nothing to link!\n")
self.active = False
else:
self.Bind(EVT_SCRIPT_UPDATE, self.linkUpdate)
t = ScriptThread(self, self.script)
self.active = True
t.Start()
def report(self):
self.script = []
self.reportLines = []
cmdpath = ScriptTools(self.settings).figureCommandPath("avr-objdump")
elfpath = "\"" + join(self.root, "build", "teacup.elf") + "\""
cmd = cmdpath + " -h " + elfpath
self.script.append(cmd)
self.Bind(EVT_SCRIPT_UPDATE, self.reportUpdate)
t = ScriptThread(self, self.script)
self.active = True
t.Start()
def generateCompileScript(self):
self.script = []
cmdpath = ScriptTools(self.settings).figureCommandPath("avr-gcc")
cfiles = [f for f in os.listdir(self.root)
if isfile(join(self.root,f)) and f.endswith(".c")]
for f in cfiles:
basename = f[:-2]
ofile = basename + ".o"
alfile = basename + ".al"
opath = "\"" + join(self.root, "build", ofile) + "\""
alpath = "\"" + join(self.root, "build", alfile) + "\""
cpath = "\"" + join(self.root, f) + "\""
opts = self.settings.cflags
opts = opts.replace("%ALNAME%", alpath)
opts = opts.replace("%F_CPU%", self.f_cpu)
opts = opts.replace("%CPU%", self.cpu)
cmd = cmdpath + " -c " + opts + " -o " + opath + " " + cpath
self.script.append(cmd)
def generateLinkScript(self):
self.script = []
cmdpath = ScriptTools(self.settings).figureCommandPath("avr-gcc")
# This is ugly:
# Work around a problem of avr-ld.exe coming with Arduino 1.6.4 for
# Windows. Without this it always drops this error message:
# collect2.exe: error: ld returned 5 exit status 255
# Just enabling verbose messages allows ld.exe to complete without failure.
if platform.startswith("win"):
cmdpath += " -Wl,-V"
ofiles = ["\"" + join(self.root, "build", f) + "\""
for f in os.listdir(join(self.root, "build"))
if isfile(join(self.root, "build", f)) and f.endswith(".o")]
opath = " ".join(ofiles)
elfpath = "\"" + join(self.root, "build", "teacup.elf") + "\""
hexpath = "\"" + join(self.root, "teacup.hex") + "\""
opts = self.settings.cflags
opts = opts.replace("%ALNAME%", "teacup.elf")
opts = opts.replace("%F_CPU%", self.f_cpu)
opts = opts.replace("%CPU%", self.cpu)
cmd = cmdpath + " " + self.settings.ldflags + " " + opts + " -o " + \
elfpath + " " + opath + " -lm"
self.script.append(cmd)
cmdpath = ScriptTools(self.settings).figureCommandPath("avr-objcopy")
cmd = cmdpath + " " + self.settings.objcopyflags + " " + elfpath + \
" " + hexpath
self.script.append(cmd)
def compileUpdate(self, evt):
if evt.msg is not None:
self.log.AppendText(evt.msg + "\n")
if evt.state == SCRIPT_RUNNING:
pass
if evt.state == SCRIPT_CANCELLED:
self.active = False
if self.cancelPending:
self.EndModal(wx.ID_OK)
self.log.AppendText("Build terminated abnormally.\n")
if evt.state == SCRIPT_FINISHED:
self.log.AppendText("Compile completed normally.\n\n")
self.link()
def linkUpdate(self, evt):
if evt.msg is not None:
self.log.AppendText(evt.msg + "\n")
if evt.state == SCRIPT_RUNNING:
pass
if evt.state == SCRIPT_CANCELLED:
self.log.AppendText("Link terminated abnormally.\n")
self.active = False
if evt.state == SCRIPT_FINISHED:
self.log.AppendText("Link completed normally.\n")
self.report()
def reportUpdate(self, evt):
if evt.state == SCRIPT_RUNNING:
if evt.msg is not None:
self.reportLines.append(evt.msg)
if evt.state == SCRIPT_CANCELLED:
self.log.AppendText(evt.msg + "\n")
self.log.AppendText("Report terminated abnormally.\n")
self.active = False
if evt.state == SCRIPT_FINISHED:
self.formatReport()
self.log.AppendText("\nBuild completed normally.\n")
self.active = False
def formatReportLine(self, m, name, v168, v328, v644, v1280):
t = m.groups()
v = int(t[0], 16)
self.log.AppendText(("%12s: %6d bytes %6.2f%% %6.2f%%"
" %6.2f%% %6.2f%%\n") %
(name, v, v / float(v168 * 1024) * 100.0,
v / float(v328 * 1024) * 100.0,
v / float(v644 * 1024) * 100.0,
v / float(v1280 * 1024) * 100.0))
def formatReport(self):
reText = re.compile("\.text\s+([0-9a-f]+)")
reBss = re.compile("\.bss\s+([0-9a-f]+)")
reEEProm = re.compile("\.eeprom\s+([0-9a-f]+)")
self.log.AppendText("\n ATmega... '168 '328(P)"
" '644(P) '1280\n")
for l in self.reportLines:
m = reText.search(l)
if m:
self.formatReportLine(m, "FLASH", 14, 30, 62, 126)
else:
m = reBss.search(l)
if m:
self.formatReportLine(m, "RAM", 1, 2, 4, 8)
else: else:
m = reEEProm.search(l) self.Bind(EVT_SCRIPT_UPDATE, self.compileUpdate)
if m: self.t = ScriptThread(self, self.script)
self.formatReportLine(m, "EEPROM", 1, 2, 2, 4) self.active = True
self.t.Start()
def onExit(self, evt): def link(self):
if self.active: self.generateLinkScript()
dlg = wx.MessageDialog(self, "Are you sure you want to cancel building?", if len(self.script) == 0:
"Build active", self.log.AppendText("Nothing to link!\n")
wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION) self.active = False
rc = dlg.ShowModal() else:
dlg.Destroy() self.Bind(EVT_SCRIPT_UPDATE, self.linkUpdate)
t = ScriptThread(self, self.script)
self.active = True
t.Start()
if rc == wx.ID_YES: def report(self):
self.cancelPending = True self.script = []
self.log.AppendText("Cancelling...\n") self.reportLines = []
self.t.Stop() cmdpath = ScriptTools(self.settings).figureCommandPath("avr-objdump")
return elfpath = '"' + join(self.root, "build", "teacup.elf") + '"'
cmd = cmdpath + " -h " + elfpath
self.script.append(cmd)
self.Bind(EVT_SCRIPT_UPDATE, self.reportUpdate)
t = ScriptThread(self, self.script)
self.active = True
t.Start()
self.EndModal(wx.ID_OK) def generateCompileScript(self):
self.script = []
cmdpath = ScriptTools(self.settings).figureCommandPath("avr-gcc")
cfiles = [
f
for f in os.listdir(self.root)
if isfile(join(self.root, f)) and f.endswith(".c")
]
for f in cfiles:
basename = f[:-2]
ofile = basename + ".o"
alfile = basename + ".al"
opath = '"' + join(self.root, "build", ofile) + '"'
alpath = '"' + join(self.root, "build", alfile) + '"'
cpath = '"' + join(self.root, f) + '"'
opts = self.settings.cflags
opts = opts.replace("%ALNAME%", alpath)
opts = opts.replace("%F_CPU%", self.f_cpu)
opts = opts.replace("%CPU%", self.cpu)
cmd = cmdpath + " -c " + opts + " -o " + opath + " " + cpath
self.script.append(cmd)
def generateLinkScript(self):
self.script = []
cmdpath = ScriptTools(self.settings).figureCommandPath("avr-gcc")
# This is ugly:
# Work around a problem of avr-ld.exe coming with Arduino 1.6.4 for
# Windows. Without this it always drops this error message:
# collect2.exe: error: ld returned 5 exit status 255
# Just enabling verbose messages allows ld.exe to complete without failure.
if platform.startswith("win"):
cmdpath += " -Wl,-V"
ofiles = [
'"' + join(self.root, "build", f) + '"'
for f in os.listdir(join(self.root, "build"))
if isfile(join(self.root, "build", f)) and f.endswith(".o")
]
opath = " ".join(ofiles)
elfpath = '"' + join(self.root, "build", "teacup.elf") + '"'
hexpath = '"' + join(self.root, "teacup.hex") + '"'
opts = self.settings.cflags
opts = opts.replace("%ALNAME%", "teacup.elf")
opts = opts.replace("%F_CPU%", self.f_cpu)
opts = opts.replace("%CPU%", self.cpu)
cmd = (
cmdpath
+ " "
+ self.settings.ldflags
+ " "
+ opts
+ " -o "
+ elfpath
+ " "
+ opath
+ " -lm"
)
self.script.append(cmd)
cmdpath = ScriptTools(self.settings).figureCommandPath("avr-objcopy")
cmd = cmdpath + " " + self.settings.objcopyflags + " " + elfpath + " " + hexpath
self.script.append(cmd)
def compileUpdate(self, evt):
if evt.msg is not None:
self.log.AppendText(evt.msg + "\n")
if evt.state == SCRIPT_RUNNING:
pass
if evt.state == SCRIPT_CANCELLED:
self.active = False
if self.cancelPending:
self.EndModal(wx.ID_OK)
self.log.AppendText("Build terminated abnormally.\n")
if evt.state == SCRIPT_FINISHED:
self.log.AppendText("Compile completed normally.\n\n")
self.link()
def linkUpdate(self, evt):
if evt.msg is not None:
self.log.AppendText(evt.msg + "\n")
if evt.state == SCRIPT_RUNNING:
pass
if evt.state == SCRIPT_CANCELLED:
self.log.AppendText("Link terminated abnormally.\n")
self.active = False
if evt.state == SCRIPT_FINISHED:
self.log.AppendText("Link completed normally.\n")
self.report()
def reportUpdate(self, evt):
if evt.state == SCRIPT_RUNNING:
if evt.msg is not None:
self.reportLines.append(evt.msg)
if evt.state == SCRIPT_CANCELLED:
self.log.AppendText(evt.msg + "\n")
self.log.AppendText("Report terminated abnormally.\n")
self.active = False
if evt.state == SCRIPT_FINISHED:
self.formatReport()
self.log.AppendText("\nBuild completed normally.\n")
self.active = False
def formatReportLine(self, m, name, v168, v328, v644, v1280):
t = m.groups()
v = int(t[0], 16)
self.log.AppendText(
("%12s: %6d bytes %6.2f%% %6.2f%%" " %6.2f%% %6.2f%%\n")
% (
name,
v,
v / float(v168 * 1024) * 100.0,
v / float(v328 * 1024) * 100.0,
v / float(v644 * 1024) * 100.0,
v / float(v1280 * 1024) * 100.0,
)
)
def formatReport(self):
reText = re.compile("\.text\s+([0-9a-f]+)")
reBss = re.compile("\.bss\s+([0-9a-f]+)")
reEEProm = re.compile("\.eeprom\s+([0-9a-f]+)")
self.log.AppendText(
"\n ATmega... '168 '328(P)" " '644(P) '1280\n"
)
for l in self.reportLines:
m = reText.search(l)
if m:
self.formatReportLine(m, "FLASH", 14, 30, 62, 126)
else:
m = reBss.search(l)
if m:
self.formatReportLine(m, "RAM", 1, 2, 4, 8)
else:
m = reEEProm.search(l)
if m:
self.formatReportLine(m, "EEPROM", 1, 2, 2, 4)
def onExit(self, evt):
if self.active:
dlg = wx.MessageDialog(
self,
"Are you sure you want to cancel building?",
"Build active",
wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION,
)
rc = dlg.ShowModal()
dlg.Destroy()
if rc == wx.ID_YES:
self.cancelPending = True
self.log.AppendText("Cancelling...\n")
self.t.Stop()
return
self.EndModal(wx.ID_OK)
class Upload(wx.Dialog): class Upload(wx.Dialog):
def __init__(self, parent, settings, f_cpu, cpu): def __init__(self, parent, settings, f_cpu, cpu):
wx.Dialog.__init__(self, parent, wx.ID_ANY, "Upload teacup", wx.Dialog.__init__(
style = wx.RESIZE_BORDER + wx.DEFAULT_DIALOG_STYLE) self,
self.settings = settings parent,
self.SetFont(self.settings.font) wx.ID_ANY,
self.root = self.settings.folder "Upload teacup",
self.f_cpu = f_cpu style=wx.RESIZE_BORDER + wx.DEFAULT_DIALOG_STYLE,
self.cpu = cpu )
self.baud = self.settings.uploadspeed self.settings = settings
self.Bind(wx.EVT_CLOSE, self.onExit) self.SetFont(self.settings.font)
self.cancelPending = False self.root = self.settings.folder
self.f_cpu = f_cpu
self.cpu = cpu
self.baud = self.settings.uploadspeed
self.Bind(wx.EVT_CLOSE, self.onExit)
self.cancelPending = False
hsz = wx.BoxSizer(wx.HORIZONTAL) hsz = wx.BoxSizer(wx.HORIZONTAL)
hsz.Add((10, 10)) hsz.Add((10, 10))
sz = wx.BoxSizer(wx.VERTICAL) sz = wx.BoxSizer(wx.VERTICAL)
sz.Add((10, 10)) sz.Add((10, 10))
tc = wx.TextCtrl(self, wx.ID_ANY, size = (900, 300), tc = wx.TextCtrl(
style = wx.TE_READONLY + wx.TE_MULTILINE) self, wx.ID_ANY, size=(900, 300), style=wx.TE_READONLY + wx.TE_MULTILINE
sz.Add(tc, 1, wx.EXPAND) )
f = wx.Font(8, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, sz.Add(tc, 1, wx.EXPAND)
wx.FONTWEIGHT_BOLD) f = wx.Font(8, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)
tc.SetFont(f) tc.SetFont(f)
self.log = tc self.log = tc
sz.Add((10, 10)) sz.Add((10, 10))
hsz.Add(sz, 1, wx.EXPAND) hsz.Add(sz, 1, wx.EXPAND)
hsz.Add((10, 10)) hsz.Add((10, 10))
self.SetSizer(hsz) self.SetSizer(hsz)
self.Fit() self.Fit()
self.generateUploadScript() self.generateUploadScript()
if len(self.script) == 0: if len(self.script) == 0:
self.log.AppendText("Nothing to upload!\n") self.log.AppendText("Nothing to upload!\n")
self.active = False self.active = False
else: else:
self.Bind(EVT_SCRIPT_UPDATE, self.uploadUpdate) self.Bind(EVT_SCRIPT_UPDATE, self.uploadUpdate)
self.t = ScriptThread(self, self.script) self.t = ScriptThread(self, self.script)
self.active = True self.active = True
self.t.Start() self.t.Start()
def generateUploadScript(self): def generateUploadScript(self):
self.script = [] self.script = []
cmdpath = ScriptTools(self.settings).figureCommandPath("avrdude") cmdpath = ScriptTools(self.settings).figureCommandPath("avrdude")
hexpath = "\"" + join(self.root, "teacup.hex") + "\"" hexpath = '"' + join(self.root, "teacup.hex") + '"'
cmd = cmdpath + " -c %s %s -b %s -p %s -P %s -U flash:w:%s:i" % \ cmd = cmdpath + " -c %s %s -b %s -p %s -P %s -U flash:w:%s:i" % (
(self.settings.programmer, self.settings.programflags, self.baud, self.settings.programmer,
self.cpu, self.settings.port, hexpath) self.settings.programflags,
self.script.append(cmd) self.baud,
self.cpu,
self.settings.port,
hexpath,
)
self.script.append(cmd)
def uploadUpdate(self, evt): def uploadUpdate(self, evt):
if evt.msg is not None: if evt.msg is not None:
self.log.AppendText(evt.msg + "\n") self.log.AppendText(evt.msg + "\n")
if evt.state == SCRIPT_RUNNING: if evt.state == SCRIPT_RUNNING:
pass pass
if evt.state == SCRIPT_CANCELLED: if evt.state == SCRIPT_CANCELLED:
self.active = False self.active = False
if self.cancelPending:
self.EndModal(wx.ID_OK)
self.log.AppendText("Upload terminated abnormally.\n")
if evt.state == SCRIPT_FINISHED:
self.log.AppendText("Upload completed normally.\n")
self.active = False
def onExit(self, evt):
if self.active:
dlg = wx.MessageDialog(
self,
"Are you sure you want to cancel upload?",
"Upload active",
wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION,
)
rc = dlg.ShowModal()
dlg.Destroy()
if rc == wx.ID_YES:
self.cancelPending = True
self.log.AppendText("Cancelling...\n")
self.t.Stop()
return
if self.cancelPending:
self.EndModal(wx.ID_OK) self.EndModal(wx.ID_OK)
self.log.AppendText("Upload terminated abnormally.\n")
if evt.state == SCRIPT_FINISHED:
self.log.AppendText("Upload completed normally.\n")
self.active = False
def onExit(self, evt):
if self.active:
dlg = wx.MessageDialog(self, "Are you sure you want to cancel upload?",
"Upload active",
wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION)
rc = dlg.ShowModal()
dlg.Destroy()
if rc == wx.ID_YES:
self.cancelPending = True
self.log.AppendText("Cancelling...\n")
self.t.Stop()
return
self.EndModal(wx.ID_OK)

View File

@ -1,296 +1,331 @@
import wx import wx
from configtool.data import (BSIZESMALL, reFloat, reInteger, offsetChLabel, from configtool.data import BSIZESMALL, reFloat, reInteger, offsetChLabel, offsetTcLabel
offsetTcLabel)
class CalcBelt(wx.Dialog): class CalcBelt(wx.Dialog):
def __init__(self, parent, font, cbUse): def __init__(self, parent, font, cbUse):
wx.Dialog.__init__(self, parent, wx.ID_ANY, wx.Dialog.__init__(
"Steps calculator for belt driven axes", self,
size = (360, 300)) parent,
self.SetFont(font) wx.ID_ANY,
self.Bind(wx.EVT_CLOSE, self.onExit) "Steps calculator for belt driven axes",
size=(360, 300),
)
self.SetFont(font)
self.Bind(wx.EVT_CLOSE, self.onExit)
self.use = cbUse self.use = cbUse
labelWidth = 130 labelWidth = 130
hsz = wx.BoxSizer(wx.HORIZONTAL) hsz = wx.BoxSizer(wx.HORIZONTAL)
hsz.Add((10, 10)) hsz.Add((10, 10))
sz = wx.BoxSizer(wx.VERTICAL) sz = wx.BoxSizer(wx.VERTICAL)
sz.Add((10, 10)) sz.Add((10, 10))
lsz = wx.BoxSizer(wx.HORIZONTAL) lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Step Angle:", size = (labelWidth, -1), st = wx.StaticText(
style = wx.ALIGN_RIGHT) self, wx.ID_ANY, "Step Angle:", size=(labelWidth, -1), style=wx.ALIGN_RIGHT
st.SetFont(font) )
lsz.Add(st, 1, wx.TOP, offsetChLabel) st.SetFont(font)
lsz.Add((5, 5)) lsz.Add(st, 1, wx.TOP, offsetChLabel)
lsz.Add((5, 5))
stepAngles = ["1.8 (200 per revolution)", "0.9 (400 per revolution)", stepAngles = [
"7.5 (48 per revolution)"] "1.8 (200 per revolution)",
self.stepAngleValues = [200, 400, 48] "0.9 (400 per revolution)",
tc = wx.Choice(self, wx.ID_ANY, choices = stepAngles) "7.5 (48 per revolution)",
tc.SetFont(font) ]
tc.SetSelection(0) self.stepAngleValues = [200, 400, 48]
tc.Bind(wx.EVT_CHOICE, self.onChoice) tc = wx.Choice(self, wx.ID_ANY, choices=stepAngles)
lsz.Add(tc) tc.SetFont(font)
tc.SetToolTip("Step angle. Depends on your type of stepper motor.") tc.SetSelection(0)
self.tcStep = tc tc.Bind(wx.EVT_CHOICE, self.onChoice)
lsz.Add(tc)
tc.SetToolTip("Step angle. Depends on your type of stepper motor.")
self.tcStep = tc
sz.Add(lsz) sz.Add(lsz)
sz.Add((10, 10)) sz.Add((10, 10))
lsz = wx.BoxSizer(wx.HORIZONTAL) lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Microstepping:", st = wx.StaticText(
size = (labelWidth, -1), style = wx.ALIGN_RIGHT) self,
st.SetFont(font) wx.ID_ANY,
lsz.Add(st, 1, wx.TOP, offsetChLabel) "Microstepping:",
lsz.Add((5, 5)) size=(labelWidth, -1),
style=wx.ALIGN_RIGHT,
)
st.SetFont(font)
lsz.Add(st, 1, wx.TOP, offsetChLabel)
lsz.Add((5, 5))
microStepping = ["1 - full step", "1/2 - half step", "1/4 - quarter step", microStepping = [
"1/8", "1/16", "1/32", "1/64", "1/128"] "1 - full step",
self.microSteppingValues = [1, 2, 4, 8, 16, 32, 64, 128] "1/2 - half step",
tc = wx.Choice(self, wx.ID_ANY, choices = microStepping) "1/4 - quarter step",
tc.SetFont(font) "1/8",
tc.Bind(wx.EVT_CHOICE, self.onChoice) "1/16",
tc.SetSelection(4) "1/32",
lsz.Add(tc) "1/64",
tc.SetToolTip("Microstepping. Most boards allow to change this by " "1/128",
"setting jumpers. The value here must match the " ]
"setting on the board in conjunction with the type " self.microSteppingValues = [1, 2, 4, 8, 16, 32, 64, 128]
"of stepper driver chip.") tc = wx.Choice(self, wx.ID_ANY, choices=microStepping)
self.tcMicroStepping = tc tc.SetFont(font)
tc.Bind(wx.EVT_CHOICE, self.onChoice)
tc.SetSelection(4)
lsz.Add(tc)
tc.SetToolTip(
"Microstepping. Most boards allow to change this by "
"setting jumpers. The value here must match the "
"setting on the board in conjunction with the type "
"of stepper driver chip."
)
self.tcMicroStepping = tc
sz.Add(lsz) sz.Add(lsz)
sz.Add((10, 10)) sz.Add((10, 10))
lsz = wx.BoxSizer(wx.HORIZONTAL) lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Belt Pitch (in mm):", st = wx.StaticText(
size = (labelWidth, -1), style = wx.ALIGN_RIGHT) self,
st.SetFont(font) wx.ID_ANY,
lsz.Add(st, 1, wx.TOP, offsetTcLabel) "Belt Pitch (in mm):",
lsz.Add((5, 5)) size=(labelWidth, -1),
style=wx.ALIGN_RIGHT,
)
st.SetFont(font)
lsz.Add(st, 1, wx.TOP, offsetTcLabel)
lsz.Add((5, 5))
tc = wx.TextCtrl(self, wx.ID_ANY, "2", style = wx.TE_RIGHT) tc = wx.TextCtrl(self, wx.ID_ANY, "2", style=wx.TE_RIGHT)
tc.SetFont(font) tc.SetFont(font)
tc.Bind(wx.EVT_TEXT, self.onTextCtrlFloat) tc.Bind(wx.EVT_TEXT, self.onTextCtrlFloat)
lsz.Add(tc) lsz.Add(tc)
tc.SetToolTip("Belt pitch. Distance between two teeth on the belt.") tc.SetToolTip("Belt pitch. Distance between two teeth on the belt.")
self.tcBeltPitch = tc self.tcBeltPitch = tc
lsz.Add((5, 5)) lsz.Add((5, 5))
beltPresets = ["-", "2mm Pitch (GT2)", "MXL Pitch (2.03mm)", beltPresets = [
"T2.5 (2.5mm)", "3mm Pitch (GT2, HTD)", "-",
"5mm Pitch (T5, GTD, HTD)", "0.2\" XL belt (5.08mm)"] "2mm Pitch (GT2)",
self.beltPresetValues = [-1, 2.0, 2.03, 2.5, 3.0, 5.0, 5.08] "MXL Pitch (2.03mm)",
tc = wx.Choice(self, wx.ID_ANY, choices = beltPresets) "T2.5 (2.5mm)",
tc.SetFont(font) "3mm Pitch (GT2, HTD)",
tc.SetSelection(0) "5mm Pitch (T5, GTD, HTD)",
tc.Bind(wx.EVT_CHOICE, self.onPresetChoice) '0.2" XL belt (5.08mm)',
lsz.Add(tc) ]
tc.SetToolTip("Belt pitch presets.") self.beltPresetValues = [-1, 2.0, 2.03, 2.5, 3.0, 5.0, 5.08]
self.tcPresets = tc tc = wx.Choice(self, wx.ID_ANY, choices=beltPresets)
tc.SetFont(font)
tc.SetSelection(0)
tc.Bind(wx.EVT_CHOICE, self.onPresetChoice)
lsz.Add(tc)
tc.SetToolTip("Belt pitch presets.")
self.tcPresets = tc
sz.Add(lsz) sz.Add(lsz)
sz.Add((10, 10)) sz.Add((10, 10))
lsz = wx.BoxSizer(wx.HORIZONTAL) lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Pulley Teeth Count:", st = wx.StaticText(
size = (labelWidth, -1), style = wx.ALIGN_RIGHT) self,
st.SetFont(font) wx.ID_ANY,
lsz.Add(st, 1, wx.TOP, offsetTcLabel) "Pulley Teeth Count:",
lsz.Add((5, 5)) size=(labelWidth, -1),
style=wx.ALIGN_RIGHT,
)
st.SetFont(font)
lsz.Add(st, 1, wx.TOP, offsetTcLabel)
lsz.Add((5, 5))
tc = wx.TextCtrl(self, wx.ID_ANY, "8", style = wx.TE_RIGHT) tc = wx.TextCtrl(self, wx.ID_ANY, "8", style=wx.TE_RIGHT)
tc.SetFont(font) tc.SetFont(font)
tc.Bind(wx.EVT_TEXT, self.onTextCtrlInteger) tc.Bind(wx.EVT_TEXT, self.onTextCtrlInteger)
lsz.Add(tc) lsz.Add(tc)
tc.SetToolTip("Pulley teeth count. Count them!") tc.SetToolTip("Pulley teeth count. Count them!")
self.tcPulleyTeeth = tc self.tcPulleyTeeth = tc
sz.Add(lsz) sz.Add(lsz)
sz.Add((30, 30)) sz.Add((30, 30))
lsz = wx.BoxSizer(wx.HORIZONTAL) lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Result:", size = (labelWidth, -1), st = wx.StaticText(
style = wx.ALIGN_RIGHT) self, wx.ID_ANY, "Result:", size=(labelWidth, -1), style=wx.ALIGN_RIGHT
st.SetFont(font) )
lsz.Add(st) st.SetFont(font)
lsz.Add((5, 5)) lsz.Add(st)
lsz.Add((5, 5))
tc = wx.StaticText(self, wx.ID_ANY, "", size = (260, -1), tc = wx.StaticText(self, wx.ID_ANY, "", size=(260, -1), style=wx.ALIGN_LEFT)
style = wx.ALIGN_LEFT) tc.SetFont(font)
tc.SetFont(font) lsz.Add(tc)
lsz.Add(tc) self.tcResult = tc
self.tcResult = tc
sz.Add(lsz) sz.Add(lsz)
lsz = wx.BoxSizer(wx.HORIZONTAL) lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Resolution:", size = (labelWidth, -1), st = wx.StaticText(
style = wx.ALIGN_RIGHT) self, wx.ID_ANY, "Resolution:", size=(labelWidth, -1), style=wx.ALIGN_RIGHT
st.SetFont(font) )
lsz.Add(st) st.SetFont(font)
lsz.Add((5, 5)) lsz.Add(st)
lsz.Add((5, 5))
tc = wx.StaticText(self, wx.ID_ANY, "", size = (260, -1), tc = wx.StaticText(self, wx.ID_ANY, "", size=(260, -1), style=wx.ALIGN_LEFT)
style = wx.ALIGN_LEFT) tc.SetFont(font)
tc.SetFont(font) lsz.Add(tc)
lsz.Add(tc) self.tcResolution = tc
self.tcResolution = tc
sz.Add(lsz) sz.Add(lsz)
sz.Add((20, 20)) sz.Add((20, 20))
bsz = wx.BoxSizer(wx.HORIZONTAL) bsz = wx.BoxSizer(wx.HORIZONTAL)
b = wx.Button(self, wx.ID_ANY, "Use for X", size = BSIZESMALL) b = wx.Button(self, wx.ID_ANY, "Use for X", size=BSIZESMALL)
b.SetFont(font) b.SetFont(font)
self.Bind(wx.EVT_BUTTON, self.onUseForX, b) self.Bind(wx.EVT_BUTTON, self.onUseForX, b)
bsz.Add(b) bsz.Add(b)
self.bUseForX = b self.bUseForX = b
bsz.Add((5, 5)) bsz.Add((5, 5))
b = wx.Button(self, wx.ID_ANY, "Use for Y", size = BSIZESMALL) b = wx.Button(self, wx.ID_ANY, "Use for Y", size=BSIZESMALL)
b.SetFont(font) b.SetFont(font)
self.Bind(wx.EVT_BUTTON, self.onUseForY, b) self.Bind(wx.EVT_BUTTON, self.onUseForY, b)
bsz.Add(b) bsz.Add(b)
self.bUseForY = b self.bUseForY = b
bsz.Add((5, 5)) bsz.Add((5, 5))
b = wx.Button(self, wx.ID_ANY, "Use for Z", size = BSIZESMALL) b = wx.Button(self, wx.ID_ANY, "Use for Z", size=BSIZESMALL)
b.SetFont(font) b.SetFont(font)
self.Bind(wx.EVT_BUTTON, self.onUseForZ, b) self.Bind(wx.EVT_BUTTON, self.onUseForZ, b)
bsz.Add(b) bsz.Add(b)
self.bUseForZ = b self.bUseForZ = b
bsz.Add((5, 5)) bsz.Add((5, 5))
b = wx.Button(self, wx.ID_ANY, "Use for E", size = BSIZESMALL) b = wx.Button(self, wx.ID_ANY, "Use for E", size=BSIZESMALL)
b.SetFont(font) b.SetFont(font)
self.Bind(wx.EVT_BUTTON, self.onUseForE, b) self.Bind(wx.EVT_BUTTON, self.onUseForE, b)
bsz.Add(b) bsz.Add(b)
self.bUseForE = b self.bUseForE = b
sz.Add(bsz) sz.Add(bsz)
sz.Add((10, 10)) sz.Add((10, 10))
hsz.Add(sz) hsz.Add(sz)
hsz.Add((10, 10)) hsz.Add((10, 10))
self.enableUseButtons(False) self.enableUseButtons(False)
self.SetSizer(hsz) self.SetSizer(hsz)
self.Fit() self.Fit()
self.calculate() self.calculate()
def calculate(self):
self.tcResult.SetLabel("")
self.tcResolution.SetLabel("")
self.enableUseButtons(False)
s = self.tcStep.GetSelection()
sv = self.stepAngleValues[s]
def calculate(self): try:
self.tcResult.SetLabel("") bp = float(self.tcBeltPitch.GetValue())
self.tcResolution.SetLabel("") except:
self.enableUseButtons(False) return
s = self.tcStep.GetSelection()
sv = self.stepAngleValues[s]
try: try:
bp = float(self.tcBeltPitch.GetValue()) pt = int(self.tcPulleyTeeth.GetValue())
except: except:
return return
try: s = self.tcMicroStepping.GetSelection()
pt = int(self.tcPulleyTeeth.GetValue()) msv = self.microSteppingValues[s]
except:
return
s = self.tcMicroStepping.GetSelection() length = pt * bp
msv = self.microSteppingValues[s] steps = sv * msv
length = pt * bp resultmm = steps / length
steps = sv * msv self.result = int(resultmm * 1000.0)
resultmm = steps / length self.tcResult.SetLabel("%d steps/m (%.3f steps/mm)" % (self.result, resultmm))
self.result = int(resultmm * 1000.0) self.tcResolution.SetLabel("%.3f micrometers" % (length / steps * 1000.0))
self.enableUseButtons(True)
self.tcResult.SetLabel("%d steps/m (%.3f steps/mm)" % def enableUseButtons(self, flag):
(self.result, resultmm)) self.bUseForX.Enable(flag)
self.tcResolution.SetLabel("%.3f micrometers" % (length / steps * 1000.0)) self.bUseForY.Enable(flag)
self.enableUseButtons(True) self.bUseForZ.Enable(flag)
self.bUseForE.Enable(flag)
def enableUseButtons(self, flag): def onUseForX(self, evt):
self.bUseForX.Enable(flag) self.use("STEPS_PER_M_X", self.result)
self.bUseForY.Enable(flag)
self.bUseForZ.Enable(flag)
self.bUseForE.Enable(flag)
def onUseForX(self, evt): def onUseForY(self, evt):
self.use('STEPS_PER_M_X', self.result) self.use("STEPS_PER_M_Y", self.result)
def onUseForY(self, evt): def onUseForZ(self, evt):
self.use('STEPS_PER_M_Y', self.result) self.use("STEPS_PER_M_Z", self.result)
def onUseForZ(self, evt): def onUseForE(self, evt):
self.use('STEPS_PER_M_Z', self.result) self.use("STEPS_PER_M_E", self.result)
def onUseForE(self, evt): def onPresetChoice(self, evt):
self.use('STEPS_PER_M_E', self.result) s = self.tcPresets.GetSelection()
sv = self.beltPresetValues[s]
if sv < 0:
return
def onPresetChoice(self, evt): s = "%f" % sv
s = self.tcPresets.GetSelection() s = s.rstrip("0")
sv = self.beltPresetValues[s] if s[-1] == ".":
if sv < 0: s += "0"
return self.tcBeltPitch.SetValue(s)
s = "%f" % sv def onChoice(self, evt):
s = s.rstrip("0") self.calculate()
if s[-1] == ".":
s += "0"
self.tcBeltPitch.SetValue(s)
def onChoice(self, evt): def onTextCtrlInteger(self, evt):
self.calculate() tc = evt.GetEventObject()
w = tc.GetValue().strip()
if w == "":
valid = False
else:
m = reInteger.match(w)
if m:
valid = True
else:
valid = False
def onTextCtrlInteger(self, evt): if valid:
tc = evt.GetEventObject() tc.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW))
w = tc.GetValue().strip() else:
if w == "": tc.SetBackgroundColour("pink")
valid = False tc.Refresh()
else: self.calculate()
m = reInteger.match(w) evt.Skip()
if m:
valid = True
else:
valid = False
if valid: def onTextCtrlFloat(self, evt):
tc.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)) tc = evt.GetEventObject()
else: w = tc.GetValue().strip()
tc.SetBackgroundColour("pink") if w == "":
tc.Refresh() valid = False
self.calculate() else:
evt.Skip() m = reFloat.match(w)
if m:
valid = True
else:
valid = False
def onTextCtrlFloat(self, evt): if valid:
tc = evt.GetEventObject() tc.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW))
w = tc.GetValue().strip() else:
if w == "": tc.SetBackgroundColour("pink")
valid = False tc.Refresh()
else: self.calculate()
m = reFloat.match(w) evt.Skip()
if m:
valid = True
else:
valid = False
if valid: def onExit(self, evt):
tc.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)) self.EndModal(wx.ID_OK)
else:
tc.SetBackgroundColour("pink")
tc.Refresh()
self.calculate()
evt.Skip()
def onExit(self, evt):
self.EndModal(wx.ID_OK)

View File

@ -1,296 +1,339 @@
import wx import wx
from configtool.data import BSIZESMALL, reFloat, offsetChLabel, offsetTcLabel from configtool.data import BSIZESMALL, reFloat, offsetChLabel, offsetTcLabel
class CalcScrew(wx.Dialog): class CalcScrew(wx.Dialog):
def __init__(self, parent, font, cbUse): def __init__(self, parent, font, cbUse):
wx.Dialog.__init__(self, parent, wx.ID_ANY, wx.Dialog.__init__(
"Steps calculator for screw driven axes", self,
size = (400, 204)) parent,
self.SetFont(font) wx.ID_ANY,
self.Bind(wx.EVT_CLOSE, self.onExit) "Steps calculator for screw driven axes",
size=(400, 204),
)
self.SetFont(font)
self.Bind(wx.EVT_CLOSE, self.onExit)
self.use = cbUse self.use = cbUse
labelWidth = 150 labelWidth = 150
hsz = wx.BoxSizer(wx.HORIZONTAL) hsz = wx.BoxSizer(wx.HORIZONTAL)
hsz.Add((10, 10)) hsz.Add((10, 10))
sz = wx.BoxSizer(wx.VERTICAL) sz = wx.BoxSizer(wx.VERTICAL)
sz.Add((10, 10)) sz.Add((10, 10))
lsz = wx.BoxSizer(wx.HORIZONTAL) lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Step Angle:", size = (labelWidth, -1), st = wx.StaticText(
style = wx.ALIGN_RIGHT) self, wx.ID_ANY, "Step Angle:", size=(labelWidth, -1), style=wx.ALIGN_RIGHT
st.SetFont(font) )
lsz.Add(st, 1, wx.TOP, offsetChLabel) st.SetFont(font)
lsz.Add((5, 5)) lsz.Add(st, 1, wx.TOP, offsetChLabel)
lsz.Add((5, 5))
stepAngles = ["1.8 (200 per revolution)", "0.9 (400 per revolution)", stepAngles = [
"7.5 (48 per revolution)"] "1.8 (200 per revolution)",
self.stepAngleValues = [200, 400, 48] "0.9 (400 per revolution)",
tc = wx.Choice(self, wx.ID_ANY, choices = stepAngles) "7.5 (48 per revolution)",
tc.SetFont(font) ]
tc.SetSelection(0) self.stepAngleValues = [200, 400, 48]
tc.Bind(wx.EVT_CHOICE, self.onChoice) tc = wx.Choice(self, wx.ID_ANY, choices=stepAngles)
lsz.Add(tc) tc.SetFont(font)
tc.SetToolTip("Step angle. Depends on your type of stepper motor.") tc.SetSelection(0)
self.tcStep = tc tc.Bind(wx.EVT_CHOICE, self.onChoice)
lsz.Add(tc)
tc.SetToolTip("Step angle. Depends on your type of stepper motor.")
self.tcStep = tc
sz.Add(lsz) sz.Add(lsz)
sz.Add((10, 10)) sz.Add((10, 10))
lsz = wx.BoxSizer(wx.HORIZONTAL) lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Microstepping:", st = wx.StaticText(
size = (labelWidth, -1), style = wx.ALIGN_RIGHT) self,
st.SetFont(font) wx.ID_ANY,
lsz.Add(st, 1, wx.TOP, offsetChLabel) "Microstepping:",
lsz.Add((5, 5)) size=(labelWidth, -1),
style=wx.ALIGN_RIGHT,
)
st.SetFont(font)
lsz.Add(st, 1, wx.TOP, offsetChLabel)
lsz.Add((5, 5))
microStepping = ["1 - full step", "1/2 - half step", "1/4 - quarter step", microStepping = [
"1/8", "1/16", "1/32", "1/64", "1/128"] "1 - full step",
self.microSteppingValues = [1, 2, 4, 8, 16, 32, 64, 128] "1/2 - half step",
tc = wx.Choice(self, wx.ID_ANY, choices = microStepping) "1/4 - quarter step",
tc.SetFont(font) "1/8",
tc.Bind(wx.EVT_CHOICE, self.onChoice) "1/16",
tc.SetSelection(4) "1/32",
lsz.Add(tc) "1/64",
tc.SetToolTip("Microstepping. Most boards allow to change this by " "1/128",
"setting jumpers. The value here must match the " ]
"setting on the board in conjunction with the type " self.microSteppingValues = [1, 2, 4, 8, 16, 32, 64, 128]
"of stepper driver chip.") tc = wx.Choice(self, wx.ID_ANY, choices=microStepping)
self.tcMicroStepping = tc tc.SetFont(font)
tc.Bind(wx.EVT_CHOICE, self.onChoice)
tc.SetSelection(4)
lsz.Add(tc)
tc.SetToolTip(
"Microstepping. Most boards allow to change this by "
"setting jumpers. The value here must match the "
"setting on the board in conjunction with the type "
"of stepper driver chip."
)
self.tcMicroStepping = tc
sz.Add(lsz) sz.Add(lsz)
sz.Add((10, 10)) sz.Add((10, 10))
lsz = wx.BoxSizer(wx.HORIZONTAL) lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Screw Pitch (mm/rev):", st = wx.StaticText(
size = (labelWidth, -1), style = wx.ALIGN_RIGHT) self,
st.SetFont(font) wx.ID_ANY,
lsz.Add(st, 1, wx.TOP, offsetTcLabel) "Screw Pitch (mm/rev):",
lsz.Add((5, 5)) size=(labelWidth, -1),
style=wx.ALIGN_RIGHT,
)
st.SetFont(font)
lsz.Add(st, 1, wx.TOP, offsetTcLabel)
lsz.Add((5, 5))
tc = wx.TextCtrl(self, wx.ID_ANY, "2", style = wx.TE_RIGHT) tc = wx.TextCtrl(self, wx.ID_ANY, "2", style=wx.TE_RIGHT)
tc.SetFont(font) tc.SetFont(font)
tc.Bind(wx.EVT_TEXT, self.onTextCtrlFloat) tc.Bind(wx.EVT_TEXT, self.onTextCtrlFloat)
lsz.Add(tc) lsz.Add(tc)
tc.SetToolTip("Screw pitch. Defined by the pitch of the screw.") tc.SetToolTip("Screw pitch. Defined by the pitch of the screw.")
self.tcScrewPitch = tc self.tcScrewPitch = tc
lsz.Add((5, 5)) lsz.Add((5, 5))
screwPresets = ["-", "M8 - metric (1.25 mm/rev)", "M6 - metric (1 mm/rev)", screwPresets = [
"M5 - metric (0.8 mm/rev)", "12 (12 mm/rev)", "-",
"16 (16 mm/rev)", "25 (25 mm/rev)", "M8 - metric (1.25 mm/rev)",
"5/15\"-18 imperial coarse (1.41111 mm/rev)", "M6 - metric (1 mm/rev)",
"3/16\"-20 imperial (1.270 mm/rev)", "M5 - metric (0.8 mm/rev)",
"1/4\"-16 ACME (1.5875 mm/rev)"] "12 (12 mm/rev)",
self.screwPresetValues = [-1, 1.25, 1.00, 0.8, 12.0, 16.0, 25.0, 1.41111, "16 (16 mm/rev)",
1.270, 1.5875] "25 (25 mm/rev)",
tc = wx.Choice(self, wx.ID_ANY, choices = screwPresets) '5/15"-18 imperial coarse (1.41111 mm/rev)',
tc.SetFont(font) '3/16"-20 imperial (1.270 mm/rev)',
tc.SetSelection(0) '1/4"-16 ACME (1.5875 mm/rev)',
tc.Bind(wx.EVT_CHOICE, self.onPresetChoice) ]
lsz.Add(tc) self.screwPresetValues = [
tc.SetToolTip("Screw pitch presets.") -1,
self.tcPresets = tc 1.25,
1.00,
0.8,
12.0,
16.0,
25.0,
1.41111,
1.270,
1.5875,
]
tc = wx.Choice(self, wx.ID_ANY, choices=screwPresets)
tc.SetFont(font)
tc.SetSelection(0)
tc.Bind(wx.EVT_CHOICE, self.onPresetChoice)
lsz.Add(tc)
tc.SetToolTip("Screw pitch presets.")
self.tcPresets = tc
sz.Add(lsz) sz.Add(lsz)
sz.Add((10, 10)) sz.Add((10, 10))
lsz = wx.BoxSizer(wx.HORIZONTAL) lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Gear Ratio:", size = (labelWidth, -1), st = wx.StaticText(
style = wx.ALIGN_RIGHT) self, wx.ID_ANY, "Gear Ratio:", size=(labelWidth, -1), style=wx.ALIGN_RIGHT
st.SetFont(font) )
lsz.Add(st, 1, wx.TOP, offsetTcLabel) st.SetFont(font)
lsz.Add((5, 5)) lsz.Add(st, 1, wx.TOP, offsetTcLabel)
lsz.Add((5, 5))
tc = wx.TextCtrl(self, wx.ID_ANY, "1", size = (40, -1), style = wx.TE_RIGHT) tc = wx.TextCtrl(self, wx.ID_ANY, "1", size=(40, -1), style=wx.TE_RIGHT)
tc.SetFont(font) tc.SetFont(font)
tc.Bind(wx.EVT_TEXT, self.onTextCtrlFloat) tc.Bind(wx.EVT_TEXT, self.onTextCtrlFloat)
lsz.Add(tc) lsz.Add(tc)
tc.SetToolTip("Gear ratio. 1:1 if there is no gear.") tc.SetToolTip("Gear ratio. 1:1 if there is no gear.")
self.tcRatioTop = tc self.tcRatioTop = tc
lsz.Add((5, 5)) lsz.Add((5, 5))
st = wx.StaticText(self, wx.ID_ANY, ":") st = wx.StaticText(self, wx.ID_ANY, ":")
st.SetFont(font) st.SetFont(font)
lsz.Add(st) lsz.Add(st)
lsz.Add((5, 5)) lsz.Add((5, 5))
tc = wx.TextCtrl(self, wx.ID_ANY, "1", size = (40, -1), style = wx.TE_RIGHT) tc = wx.TextCtrl(self, wx.ID_ANY, "1", size=(40, -1), style=wx.TE_RIGHT)
tc.SetFont(font) tc.SetFont(font)
tc.Bind(wx.EVT_TEXT, self.onTextCtrlFloat) tc.Bind(wx.EVT_TEXT, self.onTextCtrlFloat)
lsz.Add(tc) lsz.Add(tc)
tc.SetToolTip("Gear ratio. 1:1 if there is no gear.") tc.SetToolTip("Gear ratio. 1:1 if there is no gear.")
self.tcRatioBottom = tc self.tcRatioBottom = tc
sz.Add(lsz) sz.Add(lsz)
sz.Add((30, 30)) sz.Add((30, 30))
lsz = wx.BoxSizer(wx.HORIZONTAL) lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Result:", size = (labelWidth, -1), st = wx.StaticText(
style = wx.ALIGN_RIGHT) self, wx.ID_ANY, "Result:", size=(labelWidth, -1), style=wx.ALIGN_RIGHT
st.SetFont(font) )
lsz.Add(st) st.SetFont(font)
lsz.Add((5, 5)) lsz.Add(st)
lsz.Add((5, 5))
tc = wx.StaticText(self, wx.ID_ANY, "", size = (300, -1), tc = wx.StaticText(self, wx.ID_ANY, "", size=(300, -1), style=wx.ALIGN_LEFT)
style = wx.ALIGN_LEFT) tc.SetFont(font)
tc.SetFont(font) lsz.Add(tc)
lsz.Add(tc) self.tcResult = tc
self.tcResult = tc
sz.Add(lsz) sz.Add(lsz)
lsz = wx.BoxSizer(wx.HORIZONTAL) lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Resolution:", size = (labelWidth, -1), st = wx.StaticText(
style = wx.ALIGN_RIGHT) self, wx.ID_ANY, "Resolution:", size=(labelWidth, -1), style=wx.ALIGN_RIGHT
st.SetFont(font) )
lsz.Add(st) st.SetFont(font)
lsz.Add((5, 5)) lsz.Add(st)
lsz.Add((5, 5))
tc = wx.StaticText(self, wx.ID_ANY, "", size = (300, -1), tc = wx.StaticText(self, wx.ID_ANY, "", size=(300, -1), style=wx.ALIGN_LEFT)
style = wx.ALIGN_LEFT) tc.SetFont(font)
tc.SetFont(font) lsz.Add(tc)
lsz.Add(tc) self.tcResolution = tc
self.tcResolution = tc
sz.Add(lsz) sz.Add(lsz)
sz.Add((20, 20)) sz.Add((20, 20))
bsz = wx.BoxSizer(wx.HORIZONTAL) bsz = wx.BoxSizer(wx.HORIZONTAL)
b = wx.Button(self, wx.ID_ANY, "Use for X", size = BSIZESMALL) b = wx.Button(self, wx.ID_ANY, "Use for X", size=BSIZESMALL)
b.SetFont(font) b.SetFont(font)
self.Bind(wx.EVT_BUTTON, self.onUseForX, b) self.Bind(wx.EVT_BUTTON, self.onUseForX, b)
bsz.Add(b) bsz.Add(b)
self.bUseForX = b self.bUseForX = b
bsz.Add((5, 5)) bsz.Add((5, 5))
b = wx.Button(self, wx.ID_ANY, "Use for Y", size = BSIZESMALL) b = wx.Button(self, wx.ID_ANY, "Use for Y", size=BSIZESMALL)
b.SetFont(font) b.SetFont(font)
self.Bind(wx.EVT_BUTTON, self.onUseForY, b) self.Bind(wx.EVT_BUTTON, self.onUseForY, b)
bsz.Add(b) bsz.Add(b)
self.bUseForY = b self.bUseForY = b
bsz.Add((5, 5)) bsz.Add((5, 5))
b = wx.Button(self, wx.ID_ANY, "Use for Z", size = BSIZESMALL) b = wx.Button(self, wx.ID_ANY, "Use for Z", size=BSIZESMALL)
b.SetFont(font) b.SetFont(font)
self.Bind(wx.EVT_BUTTON, self.onUseForZ, b) self.Bind(wx.EVT_BUTTON, self.onUseForZ, b)
bsz.Add(b) bsz.Add(b)
self.bUseForZ = b self.bUseForZ = b
bsz.Add((5, 5)) bsz.Add((5, 5))
b = wx.Button(self, wx.ID_ANY, "Use for E", size = BSIZESMALL) b = wx.Button(self, wx.ID_ANY, "Use for E", size=BSIZESMALL)
b.SetFont(font) b.SetFont(font)
self.Bind(wx.EVT_BUTTON, self.onUseForE, b) self.Bind(wx.EVT_BUTTON, self.onUseForE, b)
bsz.Add(b) bsz.Add(b)
self.bUseForE = b self.bUseForE = b
sz.Add(bsz, flag = wx.ALIGN_CENTER_HORIZONTAL) sz.Add(bsz, flag=wx.ALIGN_CENTER_HORIZONTAL)
sz.Add((10, 10)) sz.Add((10, 10))
hsz.Add(sz) hsz.Add(sz)
hsz.Add((10, 10)) hsz.Add((10, 10))
self.enableUseButtons(False) self.enableUseButtons(False)
self.SetSizer(hsz) self.SetSizer(hsz)
self.Fit() self.Fit()
self.calculate() self.calculate()
def calculate(self): def calculate(self):
self.tcResult.SetLabel("") self.tcResult.SetLabel("")
self.tcResolution.SetLabel("") self.tcResolution.SetLabel("")
self.enableUseButtons(False) self.enableUseButtons(False)
s = self.tcStep.GetSelection() s = self.tcStep.GetSelection()
sv = self.stepAngleValues[s] sv = self.stepAngleValues[s]
try: try:
sp = float(self.tcScrewPitch.GetValue()) sp = float(self.tcScrewPitch.GetValue())
except: except:
return return
try: try:
ratioA = float(self.tcRatioTop.GetValue()) ratioA = float(self.tcRatioTop.GetValue())
except: except:
return return
try: try:
ratioB = float(self.tcRatioBottom.GetValue()) ratioB = float(self.tcRatioBottom.GetValue())
except: except:
return return
s = self.tcMicroStepping.GetSelection() s = self.tcMicroStepping.GetSelection()
msv = self.microSteppingValues[s] msv = self.microSteppingValues[s]
ratio = ratioA / ratioB ratio = ratioA / ratioB
steps = sv * msv steps = sv * msv
resultmm = steps / sp / ratio resultmm = steps / sp / ratio
self.result = int(resultmm * 1000.0) self.result = int(resultmm * 1000.0)
self.tcResult.SetLabel("%d steps/m (%.3f steps/mm)" % self.tcResult.SetLabel("%d steps/m (%.3f steps/mm)" % (self.result, resultmm))
(self.result, resultmm)) self.tcResolution.SetLabel("%.3f micrometers" % (1.0 / resultmm * 1000.0))
self.tcResolution.SetLabel("%.3f micrometers" % (1.0 / resultmm * 1000.0)) self.enableUseButtons(True)
self.enableUseButtons(True)
def enableUseButtons(self, flag): def enableUseButtons(self, flag):
self.bUseForX.Enable(flag) self.bUseForX.Enable(flag)
self.bUseForY.Enable(flag) self.bUseForY.Enable(flag)
self.bUseForZ.Enable(flag) self.bUseForZ.Enable(flag)
self.bUseForE.Enable(flag) self.bUseForE.Enable(flag)
def onUseForX(self, evt): def onUseForX(self, evt):
self.use('STEPS_PER_M_X', self.result) self.use("STEPS_PER_M_X", self.result)
def onUseForY(self, evt): def onUseForY(self, evt):
self.use('STEPS_PER_M_Y', self.result) self.use("STEPS_PER_M_Y", self.result)
def onUseForZ(self, evt): def onUseForZ(self, evt):
self.use('STEPS_PER_M_Z', self.result) self.use("STEPS_PER_M_Z", self.result)
def onUseForE(self, evt): def onUseForE(self, evt):
self.use('STEPS_PER_M_E', self.result) self.use("STEPS_PER_M_E", self.result)
def onPresetChoice(self, evt): def onPresetChoice(self, evt):
s = self.tcPresets.GetSelection() s = self.tcPresets.GetSelection()
sv = self.screwPresetValues[s] sv = self.screwPresetValues[s]
if sv < 0: if sv < 0:
return return
s = "%f" % sv s = "%f" % sv
s = s.rstrip("0") s = s.rstrip("0")
if s[-1] == ".": if s[-1] == ".":
s += "0" s += "0"
self.tcScrewPitch.SetValue(s) self.tcScrewPitch.SetValue(s)
def onChoice(self, evt): def onChoice(self, evt):
self.calculate() self.calculate()
def onTextCtrlFloat(self, evt): def onTextCtrlFloat(self, evt):
tc = evt.GetEventObject() tc = evt.GetEventObject()
w = tc.GetValue().strip() w = tc.GetValue().strip()
if w == "": if w == "":
valid = False valid = False
else: else:
m = reFloat.match(w) m = reFloat.match(w)
if m: if m:
valid = True valid = True
else: else:
valid = False valid = False
if valid: if valid:
tc.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)) tc.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW))
else: else:
tc.SetBackgroundColour("pink") tc.SetBackgroundColour("pink")
tc.Refresh() tc.Refresh()
self.calculate() self.calculate()
evt.Skip() evt.Skip()
def onExit(self, evt): def onExit(self, evt):
self.EndModal(wx.ID_OK) self.EndModal(wx.ID_OK)

View File

@ -1,62 +1,65 @@
import wx import wx
from configtool.page import Page from configtool.page import Page
class CommunicationsPage(wx.Panel, Page): class CommunicationsPage(wx.Panel, Page):
def __init__(self, parent, nb, idPg, font): def __init__(self, parent, nb, idPg, font):
wx.Panel.__init__(self, nb, wx.ID_ANY) wx.Panel.__init__(self, nb, wx.ID_ANY)
Page.__init__(self, font) Page.__init__(self, font)
self.parent = parent self.parent = parent
self.id = idPg self.id = idPg
self.defaultBaud = '115200' self.defaultBaud = "115200"
self.bauds = ['19200', '38400', '57600', '115200', '230400', '250000'] self.bauds = ["19200", "38400", "57600", "115200", "230400", "250000"]
self.labels = {'XONXOFF': "XON/XOFF Flow Control", 'BAUD': "Baud Rate:", self.labels = {
'USB_SERIAL': "USB Serial"} "XONXOFF": "XON/XOFF Flow Control",
"BAUD": "Baud Rate:",
"USB_SERIAL": "USB Serial",
}
sz = wx.GridBagSizer() sz = wx.GridBagSizer()
sz.Add((20, 40), pos = (0, 0)) sz.Add((20, 40), pos=(0, 0))
k = 'USB_SERIAL' k = "USB_SERIAL"
cb = self.addCheckBox(k, self.onUSBCheckBox) cb = self.addCheckBox(k, self.onUSBCheckBox)
sz.Add(cb, pos = (1, 1)) sz.Add(cb, pos=(1, 1))
ch = self.addChoice('BAUD', self.bauds, self.bauds.index(self.defaultBaud), ch = self.addChoice(
80, self.onChoice) "BAUD", self.bauds, self.bauds.index(self.defaultBaud), 80, self.onChoice
sz.Add(ch, pos = (1, 3)) )
sz.Add(ch, pos=(1, 3))
cb = self.addCheckBox('XONXOFF', self.onCheckBox) cb = self.addCheckBox("XONXOFF", self.onCheckBox)
sz.Add(cb, pos = (3, 3)) sz.Add(cb, pos=(3, 3))
sz.Add((100, 10), pos = (2, 2)) sz.Add((100, 10), pos=(2, 2))
self.SetSizer(sz) self.SetSizer(sz)
self.enableAll(False) self.enableAll(False)
def onUSBCheckBox(self, evt): def onUSBCheckBox(self, evt):
self.assertModified(True) self.assertModified(True)
f = not self.checkBoxes['USB_SERIAL'].IsChecked() f = not self.checkBoxes["USB_SERIAL"].IsChecked()
self.checkBoxes['XONXOFF'].Enable(f) self.checkBoxes["XONXOFF"].Enable(f)
self.choices['BAUD'].Enable(f) self.choices["BAUD"].Enable(f)
evt.Skip() evt.Skip()
def insertValues(self, cfgValues): def insertValues(self, cfgValues):
Page.insertValues(self, cfgValues) Page.insertValues(self, cfgValues)
k = 'BAUD' k = "BAUD"
self.setChoice(k, cfgValues, self.defaultBaud) self.setChoice(k, cfgValues, self.defaultBaud)
if self.checkBoxes['USB_SERIAL'].IsChecked(): if self.checkBoxes["USB_SERIAL"].IsChecked():
self.checkBoxes['XONXOFF'].Enable(False) self.checkBoxes["XONXOFF"].Enable(False)
self.choices['BAUD'].Enable(False) self.choices["BAUD"].Enable(False)
def getValues(self): def getValues(self):
result = Page.getValues(self) result = Page.getValues(self)
if result['USB_SERIAL']: if result["USB_SERIAL"]:
result['BAUD'] = result['BAUD'][0], False result["BAUD"] = result["BAUD"][0], False
result['XONXOFF'] = False result["XONXOFF"] = False
return result return result

View File

@ -1,53 +1,51 @@
import wx import wx
from configtool.page import Page from configtool.page import Page
class CpuPage(wx.Panel, Page): class CpuPage(wx.Panel, Page):
def __init__(self, parent, nb, idPg, font): def __init__(self, parent, nb, idPg, font):
wx.Panel.__init__(self, nb, wx.ID_ANY) wx.Panel.__init__(self, nb, wx.ID_ANY)
Page.__init__(self, font) Page.__init__(self, font)
self.parent = parent self.parent = parent
self.id = idPg self.id = idPg
self.labels = {'F_CPU': "CPU Clock Rate:", 'CPU': "Processor Type:"} self.labels = {"F_CPU": "CPU Clock Rate:", "CPU": "Processor Type:"}
self.clocks = [] self.clocks = []
self.processors = [] self.processors = []
sz = wx.GridBagSizer() sz = wx.GridBagSizer()
sz.Add((20, 40), pos = (0, 0)) sz.Add((20, 40), pos=(0, 0))
k = 'F_CPU' k = "F_CPU"
ch = self.addChoice(k, self.clocks, 0, 100, self.onChoice, size = (140, -1)) ch = self.addChoice(k, self.clocks, 0, 100, self.onChoice, size=(140, -1))
sz.Add(ch, pos = (1, 1)) sz.Add(ch, pos=(1, 1))
sz.Add((100, 10), pos = (1, 2)) sz.Add((100, 10), pos=(1, 2))
k = 'CPU' k = "CPU"
ch = self.addChoice(k, self.processors, 0, 100, self.onChoice, ch = self.addChoice(k, self.processors, 0, 100, self.onChoice, size=(140, -1))
size = (140, -1)) sz.Add(ch, pos=(1, 3))
sz.Add(ch, pos = (1, 3))
self.SetSizer(sz) self.SetSizer(sz)
self.enableAll(False) self.enableAll(False)
def setCandidateProcessors(self, plist): def setCandidateProcessors(self, plist):
k = 'CPU' k = "CPU"
self.choices[k].Clear() self.choices[k].Clear()
for p in plist: for p in plist:
self.choices[k].Append(p) self.choices[k].Append(p)
self.processors = plist self.processors = plist
def setCandidateClocks(self, clist): def setCandidateClocks(self, clist):
k = 'F_CPU' k = "F_CPU"
self.choices[k].Clear() self.choices[k].Clear()
for c in clist: for c in clist:
self.choices[k].Append(c) self.choices[k].Append(c)
self.clocks = clist self.clocks = clist
def insertValues(self, cfgValues): def insertValues(self, cfgValues):
Page.insertValues(self, cfgValues) Page.insertValues(self, cfgValues)
if len(self.clocks) > 0: if len(self.clocks) > 0:
self.setChoice('F_CPU', cfgValues, self.clocks[0]) self.setChoice("F_CPU", cfgValues, self.clocks[0])
if len(self.processors) > 0: if len(self.processors) > 0:
self.setChoice('CPU', cfgValues, self.processors[0]) self.setChoice("CPU", cfgValues, self.processors[0])

View File

@ -1,47 +1,61 @@
import re import re
from sys import platform from sys import platform
supportedCPUs = ['ATmega168', 'ATmega328P', 'ATmega644', 'ATmega644P', supportedCPUs = [
'ATmega644PA', 'ATmega1280', 'ATmega1284', 'ATmega1284P', "ATmega168",
'ATmega2560', 'AT90USB1286'] "ATmega328P",
"ATmega644",
"ATmega644P",
"ATmega644PA",
"ATmega1280",
"ATmega1284",
"ATmega1284P",
"ATmega2560",
"AT90USB1286",
]
# Note: this is a kludge and works for ATmegas, only. Smaller ATmegas have a # Note: this is a kludge and works for ATmegas, only. Smaller ATmegas have a
# lot fewer pins, so this list provides a lot of clutter for them. ARMs # lot fewer pins, so this list provides a lot of clutter for them. ARMs
# have entirely different names. The right way would be to fetch pin # have entirely different names. The right way would be to fetch pin
# names from the compiler environment and/or header files. # names from the compiler environment and/or header files.
pinNames = ["AIO%d" % x for x in range(16)] + \ pinNames = (
["DIO%d" % x for x in range(64)] + \ ["AIO%d" % x for x in range(16)]
["P%c%d" % (c, x) for c in range(ord('A'), ord('L') + 1) \ + ["DIO%d" % x for x in range(64)]
for x in range(8)] + ["P%c%d" % (c, x) for c in range(ord("A"), ord("L") + 1) for x in range(8)]
)
sensorTypes = {'MAX6675': "TT_MAX6675", 'Thermistor': "TT_THERMISTOR", sensorTypes = {
'AD595': "TT_AD595", 'PT100': "TT_PT100", "MAX6675": "TT_MAX6675",
'Intercom': "TT_INTERCOM", 'MCP3008': "TT_MCP3008"} "Thermistor": "TT_THERMISTOR",
"AD595": "TT_AD595",
"PT100": "TT_PT100",
"Intercom": "TT_INTERCOM",
"MCP3008": "TT_MCP3008",
}
BSIZE = (100, 60) BSIZE = (100, 60)
BSIZESMALL = (90, 30) BSIZESMALL = (90, 30)
if platform.startswith("win"): if platform.startswith("win"):
offsetTcLabel = 4 offsetTcLabel = 4
offsetChLabel = 4 offsetChLabel = 4
else: else:
offsetTcLabel = 6 offsetTcLabel = 6
offsetChLabel = 8 offsetChLabel = 8
TYPE_GENERAL = 0 TYPE_GENERAL = 0
TYPE_FLOAT = 1 TYPE_FLOAT = 1
reDefQSm = re.compile("\s*#define\s+(\S+)\s+(.*)") reDefQSm = re.compile("\s*#define\s+(\S+)\s+(.*)")
reDefQSm2 = re.compile("\s*(\"[^\"]*\")") reDefQSm2 = re.compile('\s*("[^"]*")')
reInclude = re.compile("^\s*#include\s+\"([^\"]*)") reInclude = re.compile('^\s*#include\s+"([^"]*)')
reFloatAttr = re.compile("/\*\s*float\s*\*/") reFloatAttr = re.compile("/\*\s*float\s*\*/")
reDefine = re.compile("\s*#define\s+(\w+)\s+(\S+)") reDefine = re.compile("\s*#define\s+(\w+)\s+(\S+)")
reDefineBL = re.compile("^\s*#define\s+(\w+)\s+(\S+)") reDefineBL = re.compile("^\s*#define\s+(\w+)\s+(\S+)")
reDefQS = re.compile("\s*#define\s+(\w+)\s+(\"[^\"]*\")") reDefQS = re.compile('\s*#define\s+(\w+)\s+("[^"]*")')
reDefTS = re.compile("\s*(DEFINE_TEMP_SENSOR\\([^)]*\\))") reDefTS = re.compile("\s*(DEFINE_TEMP_SENSOR\\([^)]*\\))")
reDefHT = re.compile("\s*(DEFINE_HEATER\\([^)]*\\))") reDefHT = re.compile("\s*(DEFINE_HEATER\\([^)]*\\))")
reDefTT = re.compile("^\s*//\s*TEMP_TABLE\s+(\S+)\s+(\\(.*\\))") reDefTT = re.compile("^\s*//\s*TEMP_TABLE\s+(\S+)\s+(\\(.*\\))")
@ -64,16 +78,22 @@ reSensor = re.compile(".*\\(\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*\\)")
# reHeater3 and reHeater4 deprecated, for compatibility with old config files only. # reHeater3 and reHeater4 deprecated, for compatibility with old config files only.
reHeater3 = re.compile(".*\\(\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*\\)") reHeater3 = re.compile(".*\\(\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*\\)")
reHeater4 = re.compile(".*\\(\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*\\)") reHeater4 = re.compile(".*\\(\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*\\)")
reHeater5 = re.compile(".*\\(\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*\\)") reHeater5 = re.compile(
reTempTable4 = re.compile(".*\\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d*.?\d*)\s*\\)") ".*\\(\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*\\)"
reTempTable7 = re.compile(".*\\(\s*(\d+)\s*,\s*(\d*.?\d*)\s*,\s*(\d+)\s*,\s*(\d*.?\d*)\s*,\s*(\d+)\s*,\s*(\d*.?\d*)\s*,\s*(\d+)\s*\\)") )
reTempTable4 = re.compile(
".*\\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d*.?\d*)\s*\\)"
)
reTempTable7 = re.compile(
".*\\(\s*(\d+)\s*,\s*(\d*.?\d*)\s*,\s*(\d+)\s*,\s*(\d*.?\d*)\s*,\s*(\d+)\s*,\s*(\d*.?\d*)\s*,\s*(\d+)\s*\\)"
)
reInteger = re.compile("^\d+U?L?$") reInteger = re.compile("^\d+U?L?$")
reFloat = re.compile("^\d+(\.\d*)?$") reFloat = re.compile("^\d+(\.\d*)?$")
defineValueFormat = "#define %-24s %s\n" defineValueFormat = "#define %-24s %s\n"
defineBoolFormat = "#define %s\n" defineBoolFormat = "#define %s\n"
defineHeaterFormat = "#define HEATER_%s HEATER_%s\n" defineHeaterFormat = "#define HEATER_%s HEATER_%s\n"
defineDCExtruderFormat = "#define %-24s HEATER_%s\n" defineDCExtruderFormat = "#define %-24s HEATER_%s\n"
reHomingOpts = re.compile("^\s*//\s*#define\s+HOMING_OPT\s+(\w+)") reHomingOpts = re.compile("^\s*//\s*#define\s+HOMING_OPT\s+(\w+)")

View File

@ -5,64 +5,66 @@ import os.path
class Decoration(object): class Decoration(object):
def __new__(type, *args): def __new__(type, *args):
# Make it a Singleton. # Make it a Singleton.
if not '_the_instance' in type.__dict__: if not "_the_instance" in type.__dict__:
type._the_instance = object.__new__(type) type._the_instance = object.__new__(type)
return type._the_instance return type._the_instance
def __init__(self): def __init__(self):
if not '_ready' in dir(self): if not "_ready" in dir(self):
self._ready = True self._ready = True
# It's a Singleton. Initialisations go in here. # It's a Singleton. Initialisations go in here.
self.backPic = None self.backPic = None
self.backPicOffset = (0, -25) self.backPicOffset = (0, -25)
if not self.backPic: if not self.backPic:
backPicPath = os.path.join("configtool", "background.png") backPicPath = os.path.join("configtool", "background.png")
if os.path.exists(backPicPath): if os.path.exists(backPicPath):
backPic = wx.Bitmap(backPicPath) backPic = wx.Bitmap(backPicPath)
if backPic.IsOk(): if backPic.IsOk():
self.backPic = backPic self.backPic = backPic
else: else:
print("Background picture %s damaged." % backPicPath) print("Background picture %s damaged." % backPicPath)
else: else:
print("Background picture %s doesn't exist." % backPicPath) print("Background picture %s doesn't exist." % backPicPath)
def getBackgroundColour(self): def getBackgroundColour(self):
return wx.Colour(237, 237, 237) return wx.Colour(237, 237, 237)
# On wxFrames, bind this to wx.EVT_ERASE_BACKGROUND # On wxFrames, bind this to wx.EVT_ERASE_BACKGROUND
# On wxPanels, bind this to wx.EVT_PAINT # On wxPanels, bind this to wx.EVT_PAINT
def onPaintBackground(self, evt): def onPaintBackground(self, evt):
client = evt.GetEventObject() client = evt.GetEventObject()
topLevel = client.GetTopLevelParent() topLevel = client.GetTopLevelParent()
try: try:
dc = evt.GetDC() dc = evt.GetDC()
except: except:
dc = wx.PaintDC(client) dc = wx.PaintDC(client)
if dc: if dc:
# Now draw the background picture with pseudo-transparency. This is, # Now draw the background picture with pseudo-transparency. This is,
# each background is drawn with the same picture, without transparency, # each background is drawn with the same picture, without transparency,
# and offsetted just right to have all backgrounds in the same position # and offsetted just right to have all backgrounds in the same position
# relative to the *toplevel* window, not relative to the current # relative to the *toplevel* window, not relative to the current
# subwindow as usual. # subwindow as usual.
# Align bottom right. # Align bottom right.
offX, offY = topLevel.GetClientSize() - self.backPic.GetSize() + \ offX, offY = (
self.backPicOffset topLevel.GetClientSize() - self.backPic.GetSize() + self.backPicOffset
)
if client != topLevel: if client != topLevel:
# Note: trying to figure this additional offset via various # Note: trying to figure this additional offset via various
# .GetScreenPosition() or .GetPosition() or whatever is hopeless. # .GetScreenPosition() or .GetPosition() or whatever is hopeless.
# Of many many tries only this worked on Linux. # Of many many tries only this worked on Linux.
offX, offY = \ offX, offY = client.ScreenToClient(
client.ScreenToClient(topLevel.ClientToScreen((offX, offY))) topLevel.ClientToScreen((offX, offY))
)
if self.backPic: if self.backPic:
dc.DrawBitmap(self.backPic, offX, offY) dc.DrawBitmap(self.backPic, offX, offY)
evt.Skip() evt.Skip()

View File

@ -1,4 +1,3 @@
# coding=utf-8 # coding=utf-8
import wx import wx
@ -6,76 +5,86 @@ from configtool.page import Page
class DisplayPage(wx.Panel, Page): class DisplayPage(wx.Panel, Page):
def __init__(self, parent, nb, idPg, font): def __init__(self, parent, nb, idPg, font):
wx.Panel.__init__(self, nb, wx.ID_ANY) wx.Panel.__init__(self, nb, wx.ID_ANY)
Page.__init__(self, font) Page.__init__(self, font)
self.parent = parent self.parent = parent
self.id = idPg self.id = idPg
self.labels = {'DISPLAY_BUS': "Display Bus:", self.labels = {
'DISPLAY_TYPE': "Display Type:", "DISPLAY_BUS": "Display Bus:",
'DISPLAY_BUS_4BIT': "Direct with 4 pins", "DISPLAY_TYPE": "Display Type:",
'DISPLAY_BUS_8BIT': "Direct with 8 pins", "DISPLAY_BUS_4BIT": "Direct with 4 pins",
'DISPLAY_BUS_I2C': "I²C ( = TWI)", "DISPLAY_BUS_8BIT": "Direct with 8 pins",
'DISPLAY_BUS_SPI': "SPI", "DISPLAY_BUS_I2C": "I²C ( = TWI)",
'DISPLAY_TYPE_SSD1306': "SSD1306 O-LED, 128x32 pixels", "DISPLAY_BUS_SPI": "SPI",
'DISPLAY_TYPE_HD44780': "HD44780 or 1602A, 16x2 characters", "DISPLAY_TYPE_SSD1306": "SSD1306 O-LED, 128x32 pixels",
'DISPLAY_RS_PIN': "RS pin", "DISPLAY_TYPE_HD44780": "HD44780 or 1602A, 16x2 characters",
'DISPLAY_RW_PIN': "R/W pin", "DISPLAY_RS_PIN": "RS pin",
'DISPLAY_E_PIN': "E pin", "DISPLAY_RW_PIN": "R/W pin",
'DISPLAY_D4_PIN': "D4 pin", "DISPLAY_E_PIN": "E pin",
'DISPLAY_D5_PIN': "D5 pin", "DISPLAY_D4_PIN": "D4 pin",
'DISPLAY_D6_PIN': "D6 pin", "DISPLAY_D5_PIN": "D5 pin",
'DISPLAY_D7_PIN': "D7 pin"} "DISPLAY_D6_PIN": "D6 pin",
"DISPLAY_D7_PIN": "D7 pin",
}
sz = wx.GridBagSizer() sz = wx.GridBagSizer()
sz.Add((20, 40), pos = (0, 0)) sz.Add((20, 40), pos=(0, 0))
ch = self.addBoolChoice('DISPLAY_BUS', True, 100, self.onBusChoice, ch = self.addBoolChoice(
size = (160, -1)) "DISPLAY_BUS", True, 100, self.onBusChoice, size=(160, -1)
sz.Add(ch, pos = (1, 1)) )
sz.Add((100, 10), pos = (1, 2)) sz.Add(ch, pos=(1, 1))
sz.Add((100, 10), pos=(1, 2))
ch = self.addBoolChoice('DISPLAY_TYPE', False, 100, self.onChoice, ch = self.addBoolChoice(
size = (240, -1)) "DISPLAY_TYPE", False, 100, self.onChoice, size=(240, -1)
sz.Add(ch, pos = (1, 3)) )
sz.Add(ch, pos=(1, 3))
b = wx.StaticBox(self, wx.ID_ANY, "Direct 4-bit Bus Pins:") b = wx.StaticBox(self, wx.ID_ANY, "Direct 4-bit Bus Pins:")
b.SetFont(font) b.SetFont(font)
self.pinbox = wx.StaticBoxSizer(b, wx.VERTICAL) self.pinbox = wx.StaticBoxSizer(b, wx.VERTICAL)
self.pinbox.Add((5, 5)) self.pinbox.Add((5, 5))
for k in ('DISPLAY_RS_PIN', 'DISPLAY_RW_PIN', 'DISPLAY_E_PIN', for k in (
'DISPLAY_D4_PIN', 'DISPLAY_D5_PIN', 'DISPLAY_D6_PIN', "DISPLAY_RS_PIN",
'DISPLAY_D7_PIN'): "DISPLAY_RW_PIN",
tc = self.addPinChoice(k, 200) "DISPLAY_E_PIN",
self.pinbox.Add(tc) "DISPLAY_D4_PIN",
self.pinbox.Add((5, 5)) "DISPLAY_D5_PIN",
sz.Add(self.pinbox, pos = (3, 1)) "DISPLAY_D6_PIN",
"DISPLAY_D7_PIN",
):
tc = self.addPinChoice(k, 200)
self.pinbox.Add(tc)
self.pinbox.Add((5, 5))
sz.Add(self.pinbox, pos=(3, 1))
self.SetSizer(sz) self.SetSizer(sz)
self.enableAll(False) self.enableAll(False)
def onBusChoice(self, evt): def onBusChoice(self, evt):
choice = self.boolChoices['DISPLAY_BUS'] choice = self.boolChoices["DISPLAY_BUS"]
if choice.GetClientData(choice.GetSelection()): if choice.GetClientData(choice.GetSelection()):
self.boolChoices['DISPLAY_TYPE'].Enable(True) self.boolChoices["DISPLAY_TYPE"].Enable(True)
else: else:
self.boolChoices['DISPLAY_TYPE'].Enable(False) self.boolChoices["DISPLAY_TYPE"].Enable(False)
self.adjustPinVisibility() self.adjustPinVisibility()
Page.onChoice(self, evt) Page.onChoice(self, evt)
def adjustPinVisibility(self): def adjustPinVisibility(self):
visible = False visible = False
choice = self.boolChoices['DISPLAY_BUS'] choice = self.boolChoices["DISPLAY_BUS"]
if choice.GetSelection() >= 0: if choice.GetSelection() >= 0:
selection = choice.GetClientData(choice.GetSelection()) selection = choice.GetClientData(choice.GetSelection())
if selection == 'DISPLAY_BUS_4BIT': if selection == "DISPLAY_BUS_4BIT":
visible = True visible = True
self.pinbox.ShowItems(visible) self.pinbox.ShowItems(visible)
def insertValues(self, cfgValues): def insertValues(self, cfgValues):
Page.insertValues(self, cfgValues) Page.insertValues(self, cfgValues)
self.adjustPinVisibility() self.adjustPinVisibility()

File diff suppressed because it is too large Load Diff

View File

@ -1,87 +1,89 @@
import wx import wx
class HeaterList(wx.ListCtrl): class HeaterList(wx.ListCtrl):
def __init__(self, parent, font): def __init__(self, parent, font):
self.parent = parent self.parent = parent
self.currentItem = None self.currentItem = None
wx.ListCtrl.__init__(self, parent, wx.ID_ANY, wx.ListCtrl.__init__(
size = (95 + 75 + 55 + 55 + 95 + 4, 100), self,
style = wx.LC_REPORT | wx.LC_VIRTUAL | wx.LC_HRULES | parent,
wx.LC_VRULES) wx.ID_ANY,
self.SetFont(font) size=(95 + 75 + 55 + 55 + 95 + 4, 100),
style=wx.LC_REPORT | wx.LC_VIRTUAL | wx.LC_HRULES | wx.LC_VRULES,
)
self.SetFont(font)
self.valid = [] self.valid = []
self.heaterList = [] self.heaterList = []
self.InsertColumn(0, "Name") self.InsertColumn(0, "Name")
self.InsertColumn(1, "Pin") self.InsertColumn(1, "Pin")
self.InsertColumn(2, "Invert") self.InsertColumn(2, "Invert")
self.InsertColumn(3, "PWM") self.InsertColumn(3, "PWM")
self.InsertColumn(4, "Max PWM [%]") self.InsertColumn(4, "Max PWM [%]")
self.SetColumnWidth(0, 95) self.SetColumnWidth(0, 95)
self.SetColumnWidth(1, 75) self.SetColumnWidth(1, 75)
self.SetColumnWidth(2, 55) self.SetColumnWidth(2, 55)
self.SetColumnWidth(3, 55) self.SetColumnWidth(3, 55)
self.SetColumnWidth(4, 95) self.SetColumnWidth(4, 95)
self.SetItemCount(0) self.SetItemCount(0)
self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected) self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnItemDeselected) self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnItemDeselected)
def updateList(self, heaterList): def updateList(self, heaterList):
self.heaterList = heaterList self.heaterList = heaterList
self.valid = [True] * len(heaterList) self.valid = [True] * len(heaterList)
self.currentItem = None self.currentItem = None
self.parent.setItemSelected(None) self.parent.setItemSelected(None)
i = self.GetFirstSelected() i = self.GetFirstSelected()
while i != -1: while i != -1:
self.Select(i, False) self.Select(i, False)
i = self.GetFirstSelected() i = self.GetFirstSelected()
self.SetItemCount(len(heaterList)) self.SetItemCount(len(heaterList))
def setRowValidity(self, i, flag = False): def setRowValidity(self, i, flag=False):
if i < 0 or i >= len(self.heaterList): if i < 0 or i >= len(self.heaterList):
return return
self.valid[i] = flag self.valid[i] = flag
self.Refresh() self.Refresh()
def setTableValidity(self, flag = False): def setTableValidity(self, flag=False):
for i in range(len(self.heaterList)): for i in range(len(self.heaterList)):
self.setRowValidity(i, flag) self.setRowValidity(i, flag)
def OnItemSelected(self, event): def OnItemSelected(self, event):
self.currentItem = event.m_itemIndex self.currentItem = event.m_itemIndex
self.parent.setItemSelected(self.currentItem) self.parent.setItemSelected(self.currentItem)
def OnItemDeselected(self, event): def OnItemDeselected(self, event):
self.currentItem = None self.currentItem = None
self.parent.setItemSelected(None) self.parent.setItemSelected(None)
def getColumnText(self, index, col): def getColumnText(self, index, col):
item = self.GetItem(index, col) item = self.GetItem(index, col)
return item.GetText() return item.GetText()
def OnGetItemText(self, item, col): def OnGetItemText(self, item, col):
if item < 0 or item >= len(self.heaterList): if item < 0 or item >= len(self.heaterList):
return "Error - no heaters." return "Error - no heaters."
s = self.heaterList[item] s = self.heaterList[item]
if col == 0: if col == 0:
return s[0] return s[0]
elif col == 1: elif col == 1:
return s[1] return s[1]
elif col == 2: elif col == 2:
if s[2] == "1": if s[2] == "1":
return "True" return "True"
else: else:
return "False" return "False"
elif col == 3: elif col == 3:
return s[3] return s[3]
elif col == 4: elif col == 4:
return s[4] return s[4]

View File

@ -1,4 +1,3 @@
import wx import wx
from configtool.page import Page from configtool.page import Page
from configtool.data import pinNames, BSIZESMALL from configtool.data import pinNames, BSIZESMALL
@ -7,183 +6,191 @@ from configtool.addheaterdlg import AddHeaterDlg
class HeatersPage(wx.Panel, Page): class HeatersPage(wx.Panel, Page):
def __init__(self, parent, nb, idPg, font): def __init__(self, parent, nb, idPg, font):
wx.Panel.__init__(self, nb, wx.ID_ANY) wx.Panel.__init__(self, nb, wx.ID_ANY)
Page.__init__(self, font) Page.__init__(self, font)
self.parent = parent self.parent = parent
self.font = font self.font = font
self.id = idPg self.id = idPg
self.labels = {'FORCE_SOFTWARE_PWM':"Force software PWM"} self.labels = {"FORCE_SOFTWARE_PWM": "Force software PWM"}
sz = wx.GridBagSizer() sz = wx.GridBagSizer()
sz.Add((30, 30), pos = (0, 0)) sz.Add((30, 30), pos=(0, 0))
self.heaters = [] self.heaters = []
self.validPins = pinNames self.validPins = pinNames
self.lb = HeaterList(self, font) self.lb = HeaterList(self, font)
sz.Add(self.lb, pos = (1, 1)) sz.Add(self.lb, pos=(1, 1))
sz.Add((20, 20), pos = (1, 2)) sz.Add((20, 20), pos=(1, 2))
k = 'FORCE_SOFTWARE_PWM' k = "FORCE_SOFTWARE_PWM"
cb = self.addCheckBox(k, self.onCheckBox) cb = self.addCheckBox(k, self.onCheckBox)
sz.Add(cb, pos = (2, 1)) sz.Add(cb, pos=(2, 1))
sz.Add((20, 20), pos = (2, 2)) sz.Add((20, 20), pos=(2, 2))
bsz = wx.BoxSizer(wx.VERTICAL) bsz = wx.BoxSizer(wx.VERTICAL)
self.bAdd = wx.Button(self, wx.ID_ANY, "Add", size = BSIZESMALL) self.bAdd = wx.Button(self, wx.ID_ANY, "Add", size=BSIZESMALL)
self.bAdd.SetBackgroundColour(self.deco.getBackgroundColour()) self.bAdd.SetBackgroundColour(self.deco.getBackgroundColour())
self.bAdd.SetFont(font) self.bAdd.SetFont(font)
self.Bind(wx.EVT_BUTTON, self.doAdd, self.bAdd) self.Bind(wx.EVT_BUTTON, self.doAdd, self.bAdd)
self.bAdd.SetToolTip("Add a heater to the configuration.") self.bAdd.SetToolTip("Add a heater to the configuration.")
bsz.Add(self.bAdd) bsz.Add(self.bAdd)
bsz.Add((10, 10)) bsz.Add((10, 10))
self.bModify = wx.Button(self, wx.ID_ANY, "Modify", size = BSIZESMALL) self.bModify = wx.Button(self, wx.ID_ANY, "Modify", size=BSIZESMALL)
self.bModify.SetBackgroundColour(self.deco.getBackgroundColour()) self.bModify.SetBackgroundColour(self.deco.getBackgroundColour())
self.bModify.SetFont(font) self.bModify.SetFont(font)
self.bModify.Enable(False) self.bModify.Enable(False)
self.Bind(wx.EVT_BUTTON, self.doModify, self.bModify) self.Bind(wx.EVT_BUTTON, self.doModify, self.bModify)
self.bModify.SetToolTip("Modify the selected heater.") self.bModify.SetToolTip("Modify the selected heater.")
bsz.Add(self.bModify) bsz.Add(self.bModify)
bsz.Add((10, 10)) bsz.Add((10, 10))
self.bDelete = wx.Button(self, wx.ID_ANY, "Delete", size = BSIZESMALL) self.bDelete = wx.Button(self, wx.ID_ANY, "Delete", size=BSIZESMALL)
self.bDelete.SetBackgroundColour(self.deco.getBackgroundColour()) self.bDelete.SetBackgroundColour(self.deco.getBackgroundColour())
self.bDelete.SetFont(font) self.bDelete.SetFont(font)
self.bDelete.Enable(False) self.bDelete.Enable(False)
self.Bind(wx.EVT_BUTTON, self.doDelete, self.bDelete) self.Bind(wx.EVT_BUTTON, self.doDelete, self.bDelete)
self.bDelete.SetToolTip("Remove the selected heater from the " self.bDelete.SetToolTip("Remove the selected heater from the " "configuration.")
"configuration.") bsz.Add(self.bDelete)
bsz.Add(self.bDelete)
sz.Add(bsz, pos = (1, 3)) sz.Add(bsz, pos=(1, 3))
self.SetSizer(sz) self.SetSizer(sz)
self.enableAll(False) self.enableAll(False)
def enableAll(self, flag = True): def enableAll(self, flag=True):
self.bAdd.Enable(flag) self.bAdd.Enable(flag)
Page.enableAll(self, flag) Page.enableAll(self, flag)
def setItemSelected(self, n): def setItemSelected(self, n):
self.selection = n self.selection = n
if n is None: if n is None:
self.bDelete.Enable(False) self.bDelete.Enable(False)
self.bModify.Enable(False) self.bModify.Enable(False)
else: else:
self.bDelete.Enable(True) self.bDelete.Enable(True)
self.bModify.Enable(True) self.bModify.Enable(True)
def getFreePins(self): def getFreePins(self):
freePins = [] + self.validPins freePins = [] + self.validPins
usedPins = [] usedPins = []
for s in self.heaters: for s in self.heaters:
usedPins.append(s[1]) usedPins.append(s[1])
for p in usedPins: for p in usedPins:
if p in freePins: if p in freePins:
freePins.remove(p) freePins.remove(p)
return freePins return freePins
def doAdd(self, evt): def doAdd(self, evt):
nm = [] nm = []
for s in self.heaters: for s in self.heaters:
nm.append(s[0]) nm.append(s[0])
dlg = AddHeaterDlg(self, nm, self.getFreePins(), self.font) dlg = AddHeaterDlg(self, nm, self.getFreePins(), self.font)
rc = dlg.ShowModal() rc = dlg.ShowModal()
if rc == wx.ID_OK: if rc == wx.ID_OK:
ht = dlg.getValues() ht = dlg.getValues()
dlg.Destroy() dlg.Destroy()
if rc != wx.ID_OK: if rc != wx.ID_OK:
return return
self.heaters.append(ht) self.heaters.append(ht)
self.lb.updateList(self.heaters) self.lb.updateList(self.heaters)
self.validateTable() self.validateTable()
self.parent.setHeaters(self.heaters) self.parent.setHeaters(self.heaters)
self.assertModified(True) self.assertModified(True)
def doModify(self, evt): def doModify(self, evt):
if self.selection is None: if self.selection is None:
return return
nm = [] nm = []
for s in self.heaters: for s in self.heaters:
nm.append(s[0]) nm.append(s[0])
h = self.heaters[self.selection] h = self.heaters[self.selection]
dlg = AddHeaterDlg(self, nm, [h[1]] + self.getFreePins(), self.font, dlg = AddHeaterDlg(
name = h[0], pin = h[1], invert = h[2], pwm = h[3], max_pwm = h[4]) self,
rc = dlg.ShowModal() nm,
if rc == wx.ID_OK: [h[1]] + self.getFreePins(),
ht = dlg.getValues() self.font,
name=h[0],
pin=h[1],
invert=h[2],
pwm=h[3],
max_pwm=h[4],
)
rc = dlg.ShowModal()
if rc == wx.ID_OK:
ht = dlg.getValues()
dlg.Destroy() dlg.Destroy()
if rc != wx.ID_OK: if rc != wx.ID_OK:
return return
self.heaters[self.selection] = ht self.heaters[self.selection] = ht
self.lb.updateList(self.heaters) self.lb.updateList(self.heaters)
self.validateTable() self.validateTable()
self.parent.setHeaters(self.heaters) self.parent.setHeaters(self.heaters)
self.assertModified(True) self.assertModified(True)
def doDelete(self, evt): def doDelete(self, evt):
if self.selection is None: if self.selection is None:
return return
self.assertModified(True) self.assertModified(True)
del self.heaters[self.selection] del self.heaters[self.selection]
self.lb.updateList(self.heaters) self.lb.updateList(self.heaters)
self.validateTable() self.validateTable()
self.parent.setHeaters(self.heaters) self.parent.setHeaters(self.heaters)
self.assertModified(True) self.assertModified(True)
def setHeaters(self, heaters): def setHeaters(self, heaters):
self.heaters = heaters self.heaters = heaters
self.lb.updateList(self.heaters) self.lb.updateList(self.heaters)
self.validateTable() self.validateTable()
self.parent.setHeaters(self.heaters) self.parent.setHeaters(self.heaters)
def setCandidatePins(self, plist): def setCandidatePins(self, plist):
if not plist or len(plist) == 0: if not plist or len(plist) == 0:
self.validPins = pinNames self.validPins = pinNames
else: else:
self.validPins = plist self.validPins = plist
self.validateTable() self.validateTable()
def heaterNames(self): def heaterNames(self):
heaterNames = [] heaterNames = []
for heater in self.heaters: for heater in self.heaters:
heaterNames.append(heater[0]) heaterNames.append(heater[0])
return heaterNames return heaterNames
def validateTable(self): def validateTable(self):
self.lb.setTableValidity(True) self.lb.setTableValidity(True)
self.setFieldValidity('HEATERLIST', True) self.setFieldValidity("HEATERLIST", True)
for i in range(len(self.heaters)): for i in range(len(self.heaters)):
if self.heaters[i][1] not in self.validPins: if self.heaters[i][1] not in self.validPins:
self.lb.setRowValidity(i, False) self.lb.setRowValidity(i, False)
self.setFieldValidity('HEATERLIST', False) self.setFieldValidity("HEATERLIST", False)
def setHelpText(self, ht): def setHelpText(self, ht):
Page.setHelpText(self, ht) Page.setHelpText(self, ht)
k = 'DEFINE_HEATER' k = "DEFINE_HEATER"
if k in ht.keys(): if k in ht.keys():
self.bAdd.SetToolTip(ht[k]) self.bAdd.SetToolTip(ht[k])

View File

@ -1,4 +1,3 @@
import wx import wx
from configtool.data import BSIZE from configtool.data import BSIZE
from configtool.page import Page from configtool.page import Page
@ -7,238 +6,264 @@ from configtool.calcscrew import CalcScrew
class MechanicalPage(wx.Panel, Page): class MechanicalPage(wx.Panel, Page):
def __init__(self, parent, nb, idPg, font): def __init__(self, parent, nb, idPg, font):
wx.Panel.__init__(self, nb, wx.ID_ANY) wx.Panel.__init__(self, nb, wx.ID_ANY)
Page.__init__(self, font) Page.__init__(self, font)
self.id = idPg self.id = idPg
self.parent = parent self.parent = parent
self.font = font self.font = font
self.spmKeys = ['STEPS_PER_M_X', 'STEPS_PER_M_Y', 'STEPS_PER_M_Z', self.spmKeys = [
'STEPS_PER_M_E'] "STEPS_PER_M_X",
"STEPS_PER_M_Y",
"STEPS_PER_M_Z",
"STEPS_PER_M_E",
]
self.mfrKeys = ['MAXIMUM_FEEDRATE_X', 'MAXIMUM_FEEDRATE_Y', self.mfrKeys = [
'MAXIMUM_FEEDRATE_Z', 'MAXIMUM_FEEDRATE_E'] "MAXIMUM_FEEDRATE_X",
"MAXIMUM_FEEDRATE_Y",
"MAXIMUM_FEEDRATE_Z",
"MAXIMUM_FEEDRATE_E",
]
self.msrKeys = ['SEARCH_FEEDRATE_X', 'SEARCH_FEEDRATE_Y', self.msrKeys = ["SEARCH_FEEDRATE_X", "SEARCH_FEEDRATE_Y", "SEARCH_FEEDRATE_Z"]
'SEARCH_FEEDRATE_Z']
self.eclKeys = ['ENDSTOP_CLEARANCE_X', 'ENDSTOP_CLEARANCE_Y', self.eclKeys = [
'ENDSTOP_CLEARANCE_Z'] "ENDSTOP_CLEARANCE_X",
"ENDSTOP_CLEARANCE_Y",
"ENDSTOP_CLEARANCE_Z",
]
self.minmaxKeys = ['X_MIN', 'X_MAX', 'Y_MIN', 'Y_MAX', 'Z_MIN', 'Z_MAX'] self.minmaxKeys = ["X_MIN", "X_MAX", "Y_MIN", "Y_MAX", "Z_MIN", "Z_MAX"]
self.kinematicsKeys = ['KINEMATICS_STRAIGHT', 'KINEMATICS_COREXY'] self.kinematicsKeys = ["KINEMATICS_STRAIGHT", "KINEMATICS_COREXY"]
self.homingKeys = [('HOMING_STEP1', 2), ('HOMING_STEP2', 2), self.homingKeys = [
('HOMING_STEP3', 2), ('HOMING_STEP4', 2)] ("HOMING_STEP1", 2),
("HOMING_STEP2", 2),
("HOMING_STEP3", 2),
("HOMING_STEP4", 2),
]
self.labels = {'STEPS_PER_M_X': "X:", 'STEPS_PER_M_Y': "Y:", self.labels = {
'STEPS_PER_M_Z': "Z:", 'STEPS_PER_M_E' : "E:", "STEPS_PER_M_X": "X:",
'MAXIMUM_FEEDRATE_X': "X:", 'MAXIMUM_FEEDRATE_Y': "Y:", "STEPS_PER_M_Y": "Y:",
'MAXIMUM_FEEDRATE_Z': "Z:", 'MAXIMUM_FEEDRATE_E': "E:", "STEPS_PER_M_Z": "Z:",
'SEARCH_FEEDRATE_X': "X:", 'SEARCH_FEEDRATE_Y': "Y:", "STEPS_PER_M_E": "E:",
'SEARCH_FEEDRATE_Z': "Z:", "MAXIMUM_FEEDRATE_X": "X:",
'ENDSTOP_CLEARANCE_X': "X:", 'ENDSTOP_CLEARANCE_Y': "Y:", "MAXIMUM_FEEDRATE_Y": "Y:",
'ENDSTOP_CLEARANCE_Z': "Z:", "MAXIMUM_FEEDRATE_Z": "Z:",
'X_MIN': "Min X:", 'X_MAX': "Max X:", 'Y_MIN': "Min Y:", "MAXIMUM_FEEDRATE_E": "E:",
'Y_MAX': "Max Y:", 'Z_MIN': "Min Z:", 'Z_MAX': "Max Z:", "SEARCH_FEEDRATE_X": "X:",
'E_ABSOLUTE': "Absolute E Coordinates", "SEARCH_FEEDRATE_Y": "Y:",
'KINEMATICS_STRAIGHT': "Straight", "SEARCH_FEEDRATE_Z": "Z:",
'KINEMATICS_COREXY': "CoreXY", "ENDSTOP_CLEARANCE_X": "X:",
'HOMING_STEP1': "Step 1:", "ENDSTOP_CLEARANCE_Y": "Y:",
'HOMING_STEP2': "Step 2:", "ENDSTOP_CLEARANCE_Z": "Z:",
'HOMING_STEP3': "Step 3:", "X_MIN": "Min X:",
'HOMING_STEP4': "Step 4:", "X_MAX": "Max X:",
'none': "-", "Y_MIN": "Min Y:",
'x_negative': "X min", "Y_MAX": "Max Y:",
'x_positive': "X max", "Z_MIN": "Min Z:",
'y_negative': "Y min", "Z_MAX": "Max Z:",
'y_positive': "Y max", "E_ABSOLUTE": "Absolute E Coordinates",
'z_negative': "Z min", "KINEMATICS_STRAIGHT": "Straight",
'z_positive': "Z max"} "KINEMATICS_COREXY": "CoreXY",
"HOMING_STEP1": "Step 1:",
"HOMING_STEP2": "Step 2:",
"HOMING_STEP3": "Step 3:",
"HOMING_STEP4": "Step 4:",
"none": "-",
"x_negative": "X min",
"x_positive": "X max",
"y_negative": "Y min",
"y_positive": "Y max",
"z_negative": "Z min",
"z_positive": "Z max",
}
labelWidth = 40; labelWidth = 40
labelWidthHoming = 60; labelWidthHoming = 60
sz = wx.GridBagSizer() sz = wx.GridBagSizer()
sz.Add((10, 10), pos = (0, 0)) sz.Add((10, 10), pos=(0, 0))
sz.Add((90, 10), pos = (0, 4)) sz.Add((90, 10), pos=(0, 4))
b = wx.StaticBox(self, wx.ID_ANY, "Steps Per Meter") b = wx.StaticBox(self, wx.ID_ANY, "Steps Per Meter")
b.SetFont(font) b.SetFont(font)
sbox = wx.StaticBoxSizer(b, wx.VERTICAL) sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.Add((5, 5)) sbox.Add((5, 5))
for k in self.spmKeys: for k in self.spmKeys:
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger) tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger)
sbox.Add(tc) sbox.Add(tc)
sbox.Add((5, 5)) sbox.Add((5, 5))
sz.Add(sbox, pos = (1, 1)) sz.Add(sbox, pos=(1, 1))
b = wx.StaticBox(self, wx.ID_ANY, "Maximum Feedrate") b = wx.StaticBox(self, wx.ID_ANY, "Maximum Feedrate")
b.SetFont(font) b.SetFont(font)
sbox = wx.StaticBoxSizer(b, wx.VERTICAL) sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.Add((5, 5)) sbox.Add((5, 5))
for k in self.mfrKeys: for k in self.mfrKeys:
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger) tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger)
sbox.Add(tc) sbox.Add(tc)
sbox.Add((5, 5)) sbox.Add((5, 5))
sz.Add(sbox, pos = (1, 5)) sz.Add(sbox, pos=(1, 5))
b = wx.StaticBox(self, wx.ID_ANY, "Search Feedrate") b = wx.StaticBox(self, wx.ID_ANY, "Search Feedrate")
b.SetFont(font) b.SetFont(font)
sbox = wx.StaticBoxSizer(b, wx.VERTICAL) sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.Add((5, 5)) sbox.Add((5, 5))
for k in self.msrKeys: for k in self.msrKeys:
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger) tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger)
sbox.Add(tc) sbox.Add(tc)
sbox.Add((5, 5)) sbox.Add((5, 5))
sz.Add(sbox, pos = (1, 7)) sz.Add(sbox, pos=(1, 7))
b = wx.StaticBox(self, wx.ID_ANY, "Endstop Clearance") b = wx.StaticBox(self, wx.ID_ANY, "Endstop Clearance")
b.SetFont(font) b.SetFont(font)
sbox = wx.StaticBoxSizer(b, wx.VERTICAL) sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.Add((5, 5)) sbox.Add((5, 5))
for k in self.eclKeys: for k in self.eclKeys:
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger) tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger)
sbox.Add(tc) sbox.Add(tc)
sbox.Add((5, 5)) sbox.Add((5, 5))
sz.Add(sbox, pos = (3, 5)) sz.Add(sbox, pos=(3, 5))
b = wx.StaticBox(self, wx.ID_ANY, "Travel Limits") b = wx.StaticBox(self, wx.ID_ANY, "Travel Limits")
b.SetFont(font) b.SetFont(font)
sbox = wx.StaticBoxSizer(b, wx.VERTICAL) sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.Add((5, 5)) sbox.Add((5, 5))
for k in self.minmaxKeys: for k in self.minmaxKeys:
tc = self.addTextCtrl(k, labelWidth + 20, self.onTextCtrlFloat) tc = self.addTextCtrl(k, labelWidth + 20, self.onTextCtrlFloat)
sbox.Add(tc) sbox.Add(tc)
sbox.Add((5, 5)) sbox.Add((5, 5))
sz.Add(sbox, pos = (3, 7)) sz.Add(sbox, pos=(3, 7))
vsz = wx.BoxSizer(wx.VERTICAL) vsz = wx.BoxSizer(wx.VERTICAL)
b = wx.StaticBox(self, wx.ID_ANY, "Kinematics") b = wx.StaticBox(self, wx.ID_ANY, "Kinematics")
b.SetFont(font) b.SetFont(font)
sbox = wx.StaticBoxSizer(b, wx.VERTICAL) sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.Add((5, 5)) sbox.Add((5, 5))
style = wx.RB_GROUP style = wx.RB_GROUP
for k in self.kinematicsKeys: for k in self.kinematicsKeys:
rb = self.addRadioButton(k, style, self.onKinematicsSelect, b) rb = self.addRadioButton(k, style, self.onKinematicsSelect, b)
style = 0 style = 0
sbox.Add(rb, 1, wx.LEFT + wx.RIGHT, 16) sbox.Add(rb, 1, wx.LEFT + wx.RIGHT, 16)
sbox.Add((5, 5)) sbox.Add((5, 5))
vsz.Add(sbox, 1, wx.LEFT, 10) vsz.Add(sbox, 1, wx.LEFT, 10)
cb = self.addCheckBox('E_ABSOLUTE', self.onCheckBox) cb = self.addCheckBox("E_ABSOLUTE", self.onCheckBox)
vsz.Add(cb, 1, wx.LEFT, 10) vsz.Add(cb, 1, wx.LEFT, 10)
sz.Add(vsz, pos = (3, 1)) sz.Add(vsz, pos=(3, 1))
bsz = wx.BoxSizer(wx.VERTICAL) bsz = wx.BoxSizer(wx.VERTICAL)
b = wx.Button(self, wx.ID_ANY, "Calculate\nBelt Driven", size = BSIZE) b = wx.Button(self, wx.ID_ANY, "Calculate\nBelt Driven", size=BSIZE)
b.SetBackgroundColour(self.deco.getBackgroundColour()) b.SetBackgroundColour(self.deco.getBackgroundColour())
b.SetFont(font) b.SetFont(font)
b.SetToolTip("Open the calculator for axes that are belt-driven.") b.SetToolTip("Open the calculator for axes that are belt-driven.")
self.Bind(wx.EVT_BUTTON, self.onCalcBelt, b) self.Bind(wx.EVT_BUTTON, self.onCalcBelt, b)
self.bCalcBelt = b self.bCalcBelt = b
bsz.Add(b, 1, wx.ALL, 5) bsz.Add(b, 1, wx.ALL, 5)
b = wx.Button(self, wx.ID_ANY, "Calculate\nScrew Driven", size = BSIZE) b = wx.Button(self, wx.ID_ANY, "Calculate\nScrew Driven", size=BSIZE)
b.SetBackgroundColour(self.deco.getBackgroundColour()) b.SetBackgroundColour(self.deco.getBackgroundColour())
b.SetFont(font) b.SetFont(font)
bsz.Add(b, 1, wx.ALL, 5) bsz.Add(b, 1, wx.ALL, 5)
b.SetToolTip("Open the calculator for axes that are screw-driven.") b.SetToolTip("Open the calculator for axes that are screw-driven.")
self.Bind(wx.EVT_BUTTON, self.onCalcScrew, b) self.Bind(wx.EVT_BUTTON, self.onCalcScrew, b)
self.bCalcScrew = b self.bCalcScrew = b
sz.Add(bsz, pos = (1, 3)) sz.Add(bsz, pos=(1, 3))
b = wx.StaticBox(self, wx.ID_ANY, "Homing Order") b = wx.StaticBox(self, wx.ID_ANY, "Homing Order")
b.SetFont(font) b.SetFont(font)
sbox = wx.StaticBoxSizer(b, wx.VERTICAL) sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.Add((5, 5)) sbox.Add((5, 5))
for k, ctype in self.homingKeys: for k, ctype in self.homingKeys:
if ctype == 2: if ctype == 2:
tc = self.addChoice(k, [], 0, labelWidthHoming, self.onChoice) tc = self.addChoice(k, [], 0, labelWidthHoming, self.onChoice)
sbox.Add(tc) sbox.Add(tc)
sbox.Add((5, 5)) sbox.Add((5, 5))
sz.Add(sbox, pos = (3, 3)) sz.Add(sbox, pos=(3, 3))
self.enableAll(False) self.enableAll(False)
self.SetSizer(sz) self.SetSizer(sz)
def enableAll(self, flag = True): def enableAll(self, flag=True):
self.bCalcBelt.Enable(flag) self.bCalcBelt.Enable(flag)
self.bCalcScrew.Enable(flag) self.bCalcScrew.Enable(flag)
Page.enableAll(self, flag) Page.enableAll(self, flag)
def onKinematicsSelect(self, evt): def onKinematicsSelect(self, evt):
self.assertModified(True) self.assertModified(True)
evt.Skip() evt.Skip()
def onCalcBelt(self, evt): def onCalcBelt(self, evt):
dlg = CalcBelt(self, self.font, self.cbCalcBelt) dlg = CalcBelt(self, self.font, self.cbCalcBelt)
dlg.ShowModal() dlg.ShowModal()
dlg.Destroy() dlg.Destroy()
def cbCalcBelt(self, field, value): def cbCalcBelt(self, field, value):
s = "%d" % value s = "%d" % value
self.textControls[field].SetValue(s) self.textControls[field].SetValue(s)
def onCalcScrew(self, evt): def onCalcScrew(self, evt):
dlg = CalcScrew(self, self.font, self.cbCalcScrew) dlg = CalcScrew(self, self.font, self.cbCalcScrew)
dlg.ShowModal() dlg.ShowModal()
dlg.Destroy() dlg.Destroy()
def cbCalcScrew(self, field, value): def cbCalcScrew(self, field, value):
s = "%d" % value s = "%d" % value
self.textControls[field].SetValue(s) self.textControls[field].SetValue(s)
def setHelpText(self, ht): def setHelpText(self, ht):
Page.setHelpText(self, ht) Page.setHelpText(self, ht)
if 'KINEMATICS' in ht.keys(): if "KINEMATICS" in ht.keys():
for k in self.kinematicsKeys: for k in self.kinematicsKeys:
self.radioButtons[k].SetToolTip(ht['KINEMATICS']) self.radioButtons[k].SetToolTip(ht["KINEMATICS"])
def prepareHomingValues(self, name, i, cfgValues): def prepareHomingValues(self, name, i, cfgValues):
self.choices[name].Clear() self.choices[name].Clear()
for p in cfgValues['HOMING_OPTS']: for p in cfgValues["HOMING_OPTS"]:
self.choices[name].Append(self.labels[p]) self.choices[name].Append(self.labels[p])
i = cfgValues['HOMING_OPTS'].index(cfgValues[name]) i = cfgValues["HOMING_OPTS"].index(cfgValues[name])
self.choices[name].SetSelection(i) self.choices[name].SetSelection(i)
def insertValues(self, cfgValues): def insertValues(self, cfgValues):
Page.insertValues(self, cfgValues) Page.insertValues(self, cfgValues)
self.prepareHomingValues('HOMING_STEP1', 0, cfgValues) self.prepareHomingValues("HOMING_STEP1", 0, cfgValues)
self.prepareHomingValues('HOMING_STEP2', 1, cfgValues) self.prepareHomingValues("HOMING_STEP2", 1, cfgValues)
self.prepareHomingValues('HOMING_STEP3', 2, cfgValues) self.prepareHomingValues("HOMING_STEP3", 2, cfgValues)
self.prepareHomingValues('HOMING_STEP4', 3, cfgValues) self.prepareHomingValues("HOMING_STEP4", 3, cfgValues)
for tag in self.kinematicsKeys: for tag in self.kinematicsKeys:
if tag in cfgValues.keys() and cfgValues[tag]: if tag in cfgValues.keys() and cfgValues[tag]:
self.radioButtons[tag].SetValue(True) self.radioButtons[tag].SetValue(True)
def getHomingValue(self, name, result): def getHomingValue(self, name, result):
return (self.labels.keys()[self.labels.values().index(result[name][0])], True) return (self.labels.keys()[self.labels.values().index(result[name][0])], True)
def getValues(self): def getValues(self):
result = Page.getValues(self) result = Page.getValues(self)
for tag in self.kinematicsKeys: for tag in self.kinematicsKeys:
result[tag] = self.radioButtons[tag].GetValue() result[tag] = self.radioButtons[tag].GetValue()
result['HOMING_STEP1'] = self.getHomingValue('HOMING_STEP1', result) result["HOMING_STEP1"] = self.getHomingValue("HOMING_STEP1", result)
result['HOMING_STEP2'] = self.getHomingValue('HOMING_STEP2', result) result["HOMING_STEP2"] = self.getHomingValue("HOMING_STEP2", result)
result['HOMING_STEP3'] = self.getHomingValue('HOMING_STEP3', result) result["HOMING_STEP3"] = self.getHomingValue("HOMING_STEP3", result)
result['HOMING_STEP4'] = self.getHomingValue('HOMING_STEP4', result) result["HOMING_STEP4"] = self.getHomingValue("HOMING_STEP4", result)
return result return result

View File

@ -1,233 +1,240 @@
import wx import wx
from configtool.page import Page from configtool.page import Page
from configtool.data import reFloat from configtool.data import reFloat
class MiscellaneousPage(wx.Panel, Page): class MiscellaneousPage(wx.Panel, Page):
def __init__(self, parent, nb, idPg, font): def __init__(self, parent, nb, idPg, font):
wx.Panel.__init__(self, nb, wx.ID_ANY) wx.Panel.__init__(self, nb, wx.ID_ANY)
Page.__init__(self, font) Page.__init__(self, font)
self.parent = parent self.parent = parent
self.id = idPg self.id = idPg
self.font = font self.font = font
self.labels = {'USE_INTERNAL_PULLUPS': "Use Internal Pullups", self.labels = {
'BED_LEVELING': "Enable dynamic bed leveling", "USE_INTERNAL_PULLUPS": "Use Internal Pullups",
'Z_AUTODISABLE': "Z Autodisable", "BED_LEVELING": "Enable dynamic bed leveling",
'EECONFIG': "Enable EEPROM Storage", "Z_AUTODISABLE": "Z Autodisable",
'BANG_BANG': "Enable", "EECONFIG": "Enable EEPROM Storage",
'BANG_BANG_ON': "On PWM Level:", "BANG_BANG": "Enable",
'BANG_BANG_OFF': "Off PWM Level:", "BANG_BANG_ON": "On PWM Level:",
'REPORT_TARGET_TEMPS': "Report Target Temperatures", "BANG_BANG_OFF": "Off PWM Level:",
'MOVEBUFFER_SIZE': "Movebuffer Size:", "REPORT_TARGET_TEMPS": "Report Target Temperatures",
'DC_EXTRUDER': "Heater:", 'DC_EXTRUDER_PWM': "PWM:", "MOVEBUFFER_SIZE": "Movebuffer Size:",
'USE_WATCHDOG': "Use the Watchdog Timer", "DC_EXTRUDER": "Heater:",
'TH_COUNT': "Temperature History Size:", "DC_EXTRUDER_PWM": "PWM:",
'FAST_PWM': "Fast PWM", "USE_WATCHDOG": "Use the Watchdog Timer",
'ENDSTOP_STEPS': "Endstop Steps:", "TH_COUNT": "Temperature History Size:",
'PID_SCALE': "PID Scaling Factor:", "FAST_PWM": "Fast PWM",
'TEMP_HYSTERESIS': "Temperature Hysteresis:", "ENDSTOP_STEPS": "Endstop Steps:",
'TEMP_RESIDENCY_TIME': "Temperature Residency Time:", "PID_SCALE": "PID Scaling Factor:",
'TEMP_EWMA': "Temperature EWMA:", "TEMP_HYSTERESIS": "Temperature Hysteresis:",
'HEATER_SANITY_CHECK': "Heater Sanity Check"} "TEMP_RESIDENCY_TIME": "Temperature Residency Time:",
"TEMP_EWMA": "Temperature EWMA:",
"HEATER_SANITY_CHECK": "Heater Sanity Check",
}
self.heaterNameNone = "<none>" self.heaterNameNone = "<none>"
self.heaterNames = [self.heaterNameNone] self.heaterNames = [self.heaterNameNone]
self.boardHeaters = [] self.boardHeaters = []
self.processors = [] self.processors = []
sz = wx.GridBagSizer() sz = wx.GridBagSizer()
sz.Add((20, 40), pos = (0, 0)) sz.Add((20, 40), pos=(0, 0))
sz.Add((40, 40), pos = (0, 2)) sz.Add((40, 40), pos=(0, 2))
sz.Add((40, 40), pos = (0, 4)) sz.Add((40, 40), pos=(0, 4))
sz.Add((20, 30), pos = (1, 0)) sz.Add((20, 30), pos=(1, 0))
sz.Add((20, 30), pos = (2, 0)) sz.Add((20, 30), pos=(2, 0))
sz.Add((20, 30), pos = (3, 0)) sz.Add((20, 30), pos=(3, 0))
sz.Add((20, 30), pos = (4, 0)) sz.Add((20, 30), pos=(4, 0))
sz.Add((20, 30), pos = (5, 0)) sz.Add((20, 30), pos=(5, 0))
sz.Add((20, 30), pos = (6, 0)) sz.Add((20, 30), pos=(6, 0))
sz.Add((20, 30), pos = (7, 0)) sz.Add((20, 30), pos=(7, 0))
sz.Add((20, 30), pos = (8, 0)) sz.Add((20, 30), pos=(8, 0))
labelWidth = 140 labelWidth = 140
k = 'EECONFIG' k = "EECONFIG"
cb = self.addCheckBox(k, self.onCheckBox) cb = self.addCheckBox(k, self.onCheckBox)
sz.Add(cb, pos = (1, 1)) sz.Add(cb, pos=(1, 1))
k = 'USE_INTERNAL_PULLUPS' k = "USE_INTERNAL_PULLUPS"
cb = self.addCheckBox(k, self.onCheckBox) cb = self.addCheckBox(k, self.onCheckBox)
sz.Add(cb, pos = (2, 1)) sz.Add(cb, pos=(2, 1))
k = 'USE_WATCHDOG' k = "USE_WATCHDOG"
cb = self.addCheckBox(k, self.onCheckBox) cb = self.addCheckBox(k, self.onCheckBox)
sz.Add(cb, pos = (3, 1)) sz.Add(cb, pos=(3, 1))
k = 'FAST_PWM' k = "FAST_PWM"
cb = self.addCheckBox(k, self.onCheckBox) cb = self.addCheckBox(k, self.onCheckBox)
sz.Add(cb, pos = (4, 1)) sz.Add(cb, pos=(4, 1))
k = 'HEATER_SANITY_CHECK' k = "HEATER_SANITY_CHECK"
cb = self.addCheckBox(k, self.onCheckBox) cb = self.addCheckBox(k, self.onCheckBox)
sz.Add(cb, pos = (5, 1)) sz.Add(cb, pos=(5, 1))
k = 'REPORT_TARGET_TEMPS' k = "REPORT_TARGET_TEMPS"
cb = self.addCheckBox(k, self.onCheckBox) cb = self.addCheckBox(k, self.onCheckBox)
sz.Add(cb, pos = (6, 1)) sz.Add(cb, pos=(6, 1))
k = 'Z_AUTODISABLE' k = "Z_AUTODISABLE"
cb = self.addCheckBox(k, self.onCheckBox) cb = self.addCheckBox(k, self.onCheckBox)
sz.Add(cb, pos = (7, 1)) sz.Add(cb, pos=(7, 1))
k = 'BED_LEVELING' k = "BED_LEVELING"
cb = self.addCheckBox(k, self.onCheckBox) cb = self.addCheckBox(k, self.onCheckBox)
sz.Add(cb, pos = (8, 1)) sz.Add(cb, pos=(8, 1))
b = wx.StaticBox(self, wx.ID_ANY, "BANG BANG Bed Control") b = wx.StaticBox(self, wx.ID_ANY, "BANG BANG Bed Control")
b.SetFont(font) b.SetFont(font)
sbox = wx.StaticBoxSizer(b, wx.VERTICAL) sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.Add((5, 5)) sbox.Add((5, 5))
k = 'BANG_BANG' k = "BANG_BANG"
cb = self.addCheckBox(k, self.onCheckBox) cb = self.addCheckBox(k, self.onCheckBox)
sbox.Add(cb, 1, wx.LEFT, 60) sbox.Add(cb, 1, wx.LEFT, 60)
sbox.Add((5, 20)) sbox.Add((5, 20))
k = 'BANG_BANG_ON' k = "BANG_BANG_ON"
tc = self.addTextCtrl(k, 100, self.onTextCtrlInteger) tc = self.addTextCtrl(k, 100, self.onTextCtrlInteger)
sbox.Add(tc) sbox.Add(tc)
sbox.Add((5, 5)) sbox.Add((5, 5))
k = 'BANG_BANG_OFF' k = "BANG_BANG_OFF"
tc = self.addTextCtrl(k, 100, self.onTextCtrlInteger) tc = self.addTextCtrl(k, 100, self.onTextCtrlInteger)
sbox.Add(tc) sbox.Add(tc)
sbox.Add((5, 5)) sbox.Add((5, 5))
sz.Add(sbox, pos = (1, 3), span = (5, 1), flag = wx.ALIGN_CENTER_HORIZONTAL) sz.Add(sbox, pos=(1, 3), span=(5, 1), flag=wx.ALIGN_CENTER_HORIZONTAL)
b = wx.StaticBox(self, wx.ID_ANY, "DC Motor Extruder") b = wx.StaticBox(self, wx.ID_ANY, "DC Motor Extruder")
b.SetFont(font) b.SetFont(font)
sbox = wx.StaticBoxSizer(b, wx.VERTICAL) sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.Add((5, 5)) sbox.Add((5, 5))
k = 'DC_EXTRUDER' k = "DC_EXTRUDER"
ch = self.addChoice(k, self.heaterNames, 0, 60, self.onChoice) ch = self.addChoice(k, self.heaterNames, 0, 60, self.onChoice)
sbox.Add(ch) sbox.Add(ch)
sbox.Add((5, 5)) sbox.Add((5, 5))
k = 'DC_EXTRUDER_PWM' k = "DC_EXTRUDER_PWM"
tc = self.addTextCtrl(k, 60, self.onTextCtrlInteger) tc = self.addTextCtrl(k, 60, self.onTextCtrlInteger)
sbox.Add(tc) sbox.Add(tc)
sbox.Add((5, 5)) sbox.Add((5, 5))
sz.Add(sbox, pos = (6, 3), span=(3, 1), flag = wx.ALIGN_CENTER_HORIZONTAL) sz.Add(sbox, pos=(6, 3), span=(3, 1), flag=wx.ALIGN_CENTER_HORIZONTAL)
labelWidth = 190; labelWidth = 190
k = 'MOVEBUFFER_SIZE' k = "MOVEBUFFER_SIZE"
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger) tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger)
sz.Add(tc, pos = (1, 5)) sz.Add(tc, pos=(1, 5))
k = 'TH_COUNT' k = "TH_COUNT"
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger) tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger)
sz.Add(tc, pos = (2, 5)) sz.Add(tc, pos=(2, 5))
k = 'ENDSTOP_STEPS' k = "ENDSTOP_STEPS"
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger) tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger)
sz.Add(tc, pos = (3, 5)) sz.Add(tc, pos=(3, 5))
k = 'PID_SCALE' k = "PID_SCALE"
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger) tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger)
sz.Add(tc, pos = (4, 5)) sz.Add(tc, pos=(4, 5))
k = 'TEMP_HYSTERESIS' k = "TEMP_HYSTERESIS"
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger) tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger)
sz.Add(tc, pos = (6, 5)) sz.Add(tc, pos=(6, 5))
k = 'TEMP_RESIDENCY_TIME' k = "TEMP_RESIDENCY_TIME"
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger) tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger)
sz.Add(tc, pos = (7, 5)) sz.Add(tc, pos=(7, 5))
k = 'TEMP_EWMA' k = "TEMP_EWMA"
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger) tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger)
sz.Add(tc, pos = (8, 5)) sz.Add(tc, pos=(8, 5))
self.SetSizer(sz) self.SetSizer(sz)
self.enableAll(False) self.enableAll(False)
def setHeaters(self, hlist): def setHeaters(self, hlist):
k = 'DC_EXTRUDER' k = "DC_EXTRUDER"
v = self.choices[k].GetSelection() v = self.choices[k].GetSelection()
currentChoice = self.heaterNames[v] currentChoice = self.heaterNames[v]
self.boardHeaters = [s[0] for s in hlist] self.boardHeaters = [s[0] for s in hlist]
self.heaterNames = [self.heaterNameNone] + self.boardHeaters self.heaterNames = [self.heaterNameNone] + self.boardHeaters
self.choices[k].Clear() self.choices[k].Clear()
self.choices[k].SetFont(self.font) self.choices[k].SetFont(self.font)
for h in self.heaterNames: for h in self.heaterNames:
self.choices[k].Append(h) self.choices[k].Append(h)
try: try:
v = self.heaterNames.index(currentChoice) v = self.heaterNames.index(currentChoice)
except: except:
v = 0 v = 0
dlg = wx.MessageDialog(self, dlg = wx.MessageDialog(
"Printer: Miscellaneous tab:\nDC Extruder heater " self,
"\"%s\" not defined for this board. Please check." "Printer: Miscellaneous tab:\nDC Extruder heater "
% currentChoice, "Warning", '"%s" not defined for this board. Please check.' % currentChoice,
wx.OK + wx.ICON_WARNING) "Warning",
wx.OK + wx.ICON_WARNING,
)
dlg.ShowModal() dlg.ShowModal()
dlg.Destroy() dlg.Destroy()
self.choices[k].SetSelection(v) self.choices[k].SetSelection(v)
def setOriginalHeater(self, h): def setOriginalHeater(self, h):
k = 'DC_EXTRUDER' k = "DC_EXTRUDER"
if h and h.startswith("HEATER_"): if h and h.startswith("HEATER_"):
hname = h[len("HEATER_"):] hname = h[len("HEATER_") :]
else: else:
hname = h hname = h
if hname and len(self.boardHeaters) != 0: if hname and len(self.boardHeaters) != 0:
if hname not in self.boardHeaters: if hname not in self.boardHeaters:
dlg = wx.MessageDialog(self, dlg = wx.MessageDialog(
"Printer: Miscellaneous tab:\nDC Extruder " self,
"heater \"%s\" not defined for this board. " "Printer: Miscellaneous tab:\nDC Extruder "
"Please check." 'heater "%s" not defined for this board. '
% hname, "Warning", wx.OK + wx.ICON_WARNING) "Please check." % hname,
"Warning",
wx.OK + wx.ICON_WARNING,
)
dlg.ShowModal() dlg.ShowModal()
dlg.Destroy() dlg.Destroy()
self.heaterNames = [self.heaterNameNone] + self.boardHeaters self.heaterNames = [self.heaterNameNone] + self.boardHeaters
else: else:
self.heaterNames = [self.heaterNameNone] self.heaterNames = [self.heaterNameNone]
if hname and hname != self.heaterNameNone: if hname and hname != self.heaterNameNone:
self.heaterNames.append(hname) self.heaterNames.append(hname)
self.choices[k].Clear() self.choices[k].Clear()
self.choices[k].SetFont(self.font) self.choices[k].SetFont(self.font)
for ht in self.heaterNames: for ht in self.heaterNames:
self.choices[k].Append(ht) self.choices[k].Append(ht)
if hname: if hname:
try: try:
v = self.heaterNames.index(hname) v = self.heaterNames.index(hname)
except: except:
v = 0 v = 0
else: else:
v = 0 v = 0
self.choices[k].SetSelection(v) self.choices[k].SetSelection(v)
def getValues(self): def getValues(self):
result = Page.getValues(self) result = Page.getValues(self)
k = 'DC_EXTRUDER' k = "DC_EXTRUDER"
s = self.choices[k].GetSelection() s = self.choices[k].GetSelection()
v = self.choices[k].GetString(s) v = self.choices[k].GetString(s)
if v == self.heaterNameNone: if v == self.heaterNameNone:
if k in self.choicesOriginal.keys(): if k in self.choicesOriginal.keys():
result[k] = self.choicesOriginal[k][0], False result[k] = self.choicesOriginal[k][0], False
else: else:
result[k] = "", False result[k] = "", False
else: else:
result[k] = "HEATER_%s" % v, True result[k] = "HEATER_%s" % v, True
return result return result

View File

@ -3,352 +3,370 @@ from __future__ import print_function
import wx import wx
from configtool.decoration import Decoration from configtool.decoration import Decoration
from configtool.data import (reInteger, reFloat, offsetChLabel, offsetTcLabel, from configtool.data import reInteger, reFloat, offsetChLabel, offsetTcLabel, pinNames
pinNames)
class Page: class Page:
def __init__(self, font): def __init__(self, font):
self.modified = False self.modified = False
self.valid = True self.valid = True
self.fieldValid = {} self.fieldValid = {}
self.textControls = {} self.textControls = {}
self.textControlsOriginal = {} self.textControlsOriginal = {}
self.checkBoxes = {} self.checkBoxes = {}
self.radioButtons = {} self.radioButtons = {}
self.radioButtonBoxes = {} self.radioButtonBoxes = {}
self.choices = {} self.choices = {}
self.choicesOriginal = {} self.choicesOriginal = {}
self.boolChoices = {} self.boolChoices = {}
self.deco = Decoration() self.deco = Decoration()
self.font = font self.font = font
self.SetBackgroundColour(self.deco.getBackgroundColour()) self.SetBackgroundColour(self.deco.getBackgroundColour())
self.Bind(wx.EVT_PAINT, self.deco.onPaintBackground) self.Bind(wx.EVT_PAINT, self.deco.onPaintBackground)
def enableAll(self, flag = True): def enableAll(self, flag=True):
for c in self.textControls.keys(): for c in self.textControls.keys():
self.textControls[c].Enable(flag) self.textControls[c].Enable(flag)
for c in self.checkBoxes.keys(): for c in self.checkBoxes.keys():
self.checkBoxes[c].Enable(flag) self.checkBoxes[c].Enable(flag)
for c in self.radioButtons.keys(): for c in self.radioButtons.keys():
self.radioButtons[c].Enable(flag) self.radioButtons[c].Enable(flag)
for c in self.choices.keys(): for c in self.choices.keys():
self.choices[c].Enable(flag) self.choices[c].Enable(flag)
def addTextCtrl(self, name, labelWidth, validator): def addTextCtrl(self, name, labelWidth, validator):
lsz = wx.BoxSizer(wx.HORIZONTAL) lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, self.labels[name] + " ", st = wx.StaticText(
size = (labelWidth, -1), style = wx.ALIGN_RIGHT) self,
st.SetFont(self.font) wx.ID_ANY,
lsz.Add(st, 1, wx.TOP, offsetTcLabel) self.labels[name] + " ",
size=(labelWidth, -1),
style=wx.ALIGN_RIGHT,
)
st.SetFont(self.font)
lsz.Add(st, 1, wx.TOP, offsetTcLabel)
tc = wx.TextCtrl(self, wx.ID_ANY, "", style = wx.TE_RIGHT, name = name) tc = wx.TextCtrl(self, wx.ID_ANY, "", style=wx.TE_RIGHT, name=name)
tc.SetFont(self.font) tc.SetFont(self.font)
self.fieldValid[name] = True self.fieldValid[name] = True
tc.Bind(wx.EVT_TEXT, validator) tc.Bind(wx.EVT_TEXT, validator)
self.textControls[name] = tc self.textControls[name] = tc
lsz.Add(tc) lsz.Add(tc)
return lsz return lsz
def addCheckBox(self, name, validator): def addCheckBox(self, name, validator):
if name in self.labels.keys(): if name in self.labels.keys():
lbl = self.labels[name] lbl = self.labels[name]
else:
lbl = name
cb = wx.CheckBox(self, wx.ID_ANY, lbl)
cb.SetFont(self.font)
cb.Bind(wx.EVT_CHECKBOX, validator)
self.checkBoxes[name] = cb
return cb
def addRadioButton(self, name, style, validator, sbox = None):
rb = wx.RadioButton(self, wx.ID_ANY, self.labels[name], style = style)
rb.SetFont(self.font)
self.Bind(wx.EVT_RADIOBUTTON, validator, rb)
self.radioButtons[name] = rb
if sbox is not None:
self.radioButtonBoxes[name] = sbox
return rb
def addChoice(self, name, choices, selection, labelWidth, validator,
size = (-1, -1)):
lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, self.labels[name],
size = (labelWidth, -1), style = wx.ALIGN_RIGHT)
st.SetFont(self.font)
lsz.Add(st, 1, wx.TOP, offsetChLabel)
ch = wx.Choice(self, wx.ID_ANY, choices = choices, size = size, name = name)
ch.SetBackgroundColour(self.deco.getBackgroundColour())
ch.SetFont(self.font)
ch.Bind(wx.EVT_CHOICE, validator)
ch.SetSelection(selection)
lsz.Add(ch)
self.choices[name] = ch
return lsz
def addPinChoice(self, name, labelWidth):
lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, self.labels[name],
size = (labelWidth, -1), style = wx.ALIGN_RIGHT)
st.SetFont(self.font)
lsz.Add(st, 1, wx.TOP, offsetChLabel)
ch = wx.Choice(self, wx.ID_ANY, name = name, style = wx.CB_SORT)
ch.SetBackgroundColour(self.deco.getBackgroundColour())
ch.SetFont(self.font)
ch.AppendItems(["-"] + pinNames)
ch.Bind(wx.EVT_CHOICE, self.onChoice)
self.choices[name] = ch
lsz.Add(ch)
return lsz
# addChoice handles #defines with value, this handles choices for
# sets of boolean #defines.
def addBoolChoice(self, name, allowBlank, labelWidth, validator,
size = (-1, -1)):
lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, self.labels[name],
size = (labelWidth, -1), style = wx.ALIGN_RIGHT)
st.SetFont(self.font)
lsz.Add(st, 1, wx.TOP, offsetChLabel)
ch = wx.Choice(self, wx.ID_ANY, size = size, name = name)
ch.SetBackgroundColour(self.deco.getBackgroundColour())
ch.SetFont(self.font)
ch.Bind(wx.EVT_CHOICE, validator)
if allowBlank:
ch.Append("(none)")
ch.SetSelection(0)
lsz.Add(ch)
self.boolChoices[name] = ch
return lsz
def setChoice(self, name, cfgValues, default):
if name in cfgValues.keys() and cfgValues[name][1] == True:
bv = cfgValues[name][0]
else:
bv = default
s = self.choices[name].FindString(bv)
if s < 0:
s = self.choices[name].FindString(default)
if s < 0:
s = 0
self.choices[name].SetSelection(s)
def onTextCtrlInteger(self, evt):
self.assertModified(True)
tc = evt.GetEventObject()
name = tc.GetName()
w = tc.GetValue().strip()
if w == "":
valid = True
else:
m = reInteger.match(w)
if m:
valid = True
else:
valid = False
self.setFieldValidity(name, valid)
if valid:
tc.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW))
else:
tc.SetBackgroundColour("pink")
tc.Refresh()
evt.Skip()
def onTextCtrlFloat(self, evt):
self.assertModified(True)
tc = evt.GetEventObject()
name = tc.GetName()
w = tc.GetValue().strip()
if w == "":
valid = True
else:
m = reFloat.match(w)
if m:
valid = True
else:
valid = False
self.setFieldValidity(name, valid)
if valid:
tc.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW))
else:
tc.SetBackgroundColour("pink")
tc.Refresh()
evt.Skip()
def onTextCtrlPin(self, evt):
self.assertModified(True)
tc = evt.GetEventObject()
self.validatePin(tc)
evt.Skip()
def onTextCtrl(self, evt):
self.assertModified(True)
evt.Skip()
def onChoice(self, evt):
self.assertModified(True)
evt.Skip()
def onCheckBox(self, evt):
self.assertModified(True)
evt.Skip()
def setHelpText(self, ht):
for k in self.textControls.keys():
if k in ht.keys():
self.textControls[k].SetToolTip(ht[k])
for k in self.checkBoxes.keys():
if k in ht.keys():
self.checkBoxes[k].SetToolTip(ht[k])
for k in self.radioButtons.keys():
if k in ht.keys():
self.radioButtons[k].SetToolTip(ht[k])
if k in self.radioButtonBoxes.keys():
self.radioButtonBoxes[k].SetToolTip(ht[k])
for k in self.choices.keys():
if k in ht.keys():
self.choices[k].SetToolTip(ht[k])
for k in self.boolChoices.keys():
for candidate in ht.keys():
if candidate.startswith(k):
self.boolChoices[k].SetToolTip(ht[candidate])
break
def insertValues(self, cfgValues):
self.assertValid(True)
self.enableAll(True)
for k in self.fieldValid.keys():
self.fieldValid[k] = True
for k in self.checkBoxes.keys():
if k in cfgValues.keys() and cfgValues[k]:
self.checkBoxes[k].SetValue(True)
else:
self.checkBoxes[k].SetValue(False)
for k in self.textControls.keys():
if k in cfgValues.keys():
self.textControlsOriginal[k] = cfgValues[k]
if cfgValues[k][1] == True:
self.textControls[k].SetValue(str(cfgValues[k][0]))
else: else:
self.textControls[k].SetValue("") lbl = name
else: cb = wx.CheckBox(self, wx.ID_ANY, lbl)
print("Key " + k + " not found in config data.") cb.SetFont(self.font)
cb.Bind(wx.EVT_CHECKBOX, validator)
self.checkBoxes[name] = cb
for k in self.choices.keys(): return cb
if k in cfgValues.keys():
self.choicesOriginal[k] = cfgValues[k]
self.setChoice(k, cfgValues, "-")
else:
print("Key " + k + " not found in config data.")
for k in self.boolChoices.keys(): def addRadioButton(self, name, style, validator, sbox=None):
choice = self.boolChoices[k] rb = wx.RadioButton(self, wx.ID_ANY, self.labels[name], style=style)
rb.SetFont(self.font)
self.Bind(wx.EVT_RADIOBUTTON, validator, rb)
self.radioButtons[name] = rb
if sbox is not None:
self.radioButtonBoxes[name] = sbox
# Remove items left behind from the previous configuration. return rb
while (choice.GetCount() and
not choice.GetString(choice.GetCount() - 1).startswith('(')):
choice.Delete(choice.GetCount() - 1)
# Add items found in this configuration. def addChoice(self, name, choices, selection, labelWidth, validator, size=(-1, -1)):
for cfg in cfgValues.keys(): lsz = wx.BoxSizer(wx.HORIZONTAL)
if cfg.startswith(k): st = wx.StaticText(
if cfg in self.labels.keys(): self,
choice.Append(self.labels[cfg]) wx.ID_ANY,
else: self.labels[name],
choice.Append(cfg) size=(labelWidth, -1),
style=wx.ALIGN_RIGHT,
)
st.SetFont(self.font)
lsz.Add(st, 1, wx.TOP, offsetChLabel)
# As we want to write the configuration name later, not the user ch = wx.Choice(self, wx.ID_ANY, choices=choices, size=size, name=name)
# friendly string, we store the configuration name as client data. ch.SetBackgroundColour(self.deco.getBackgroundColour())
n = choice.GetCount() - 1 ch.SetFont(self.font)
choice.SetClientData(n, cfg) ch.Bind(wx.EVT_CHOICE, validator)
ch.SetSelection(selection)
lsz.Add(ch)
self.choices[name] = ch
if cfgValues[cfg]: return lsz
choice.SetSelection(n)
self.assertModified(False) def addPinChoice(self, name, labelWidth):
lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(
self,
wx.ID_ANY,
self.labels[name],
size=(labelWidth, -1),
style=wx.ALIGN_RIGHT,
)
st.SetFont(self.font)
lsz.Add(st, 1, wx.TOP, offsetChLabel)
def getValues(self): ch = wx.Choice(self, wx.ID_ANY, name=name, style=wx.CB_SORT)
self.assertModified(False) ch.SetBackgroundColour(self.deco.getBackgroundColour())
result = {} ch.SetFont(self.font)
ch.AppendItems(["-"] + pinNames)
ch.Bind(wx.EVT_CHOICE, self.onChoice)
self.choices[name] = ch
lsz.Add(ch)
for k in self.checkBoxes.keys(): return lsz
cb = self.checkBoxes[k]
result[k] = cb.IsChecked()
for k in self.textControls.keys(): # addChoice handles #defines with value, this handles choices for
v = self.textControls[k].GetValue() # sets of boolean #defines.
if v == "": def addBoolChoice(self, name, allowBlank, labelWidth, validator, size=(-1, -1)):
if k in self.textControlsOriginal.keys(): lsz = wx.BoxSizer(wx.HORIZONTAL)
result[k] = self.textControlsOriginal[k][0], False st = wx.StaticText(
self,
wx.ID_ANY,
self.labels[name],
size=(labelWidth, -1),
style=wx.ALIGN_RIGHT,
)
st.SetFont(self.font)
lsz.Add(st, 1, wx.TOP, offsetChLabel)
ch = wx.Choice(self, wx.ID_ANY, size=size, name=name)
ch.SetBackgroundColour(self.deco.getBackgroundColour())
ch.SetFont(self.font)
ch.Bind(wx.EVT_CHOICE, validator)
if allowBlank:
ch.Append("(none)")
ch.SetSelection(0)
lsz.Add(ch)
self.boolChoices[name] = ch
return lsz
def setChoice(self, name, cfgValues, default):
if name in cfgValues.keys() and cfgValues[name][1] == True:
bv = cfgValues[name][0]
else: else:
result[k] = "", False bv = default
else:
result[k] = v, True
for k in self.radioButtons.keys(): s = self.choices[name].FindString(bv)
result[k] = self.radioButtons[k].GetValue(), True if s < 0:
s = self.choices[name].FindString(default)
if s < 0:
s = 0
for k in self.choices.keys(): self.choices[name].SetSelection(s)
v = self.choices[k].GetSelection()
s = self.choices[k].GetString(v) def onTextCtrlInteger(self, evt):
if s == "-": self.assertModified(True)
if k in self.choicesOriginal.keys(): tc = evt.GetEventObject()
result[k] = self.choicesOriginal[k][0], False name = tc.GetName()
w = tc.GetValue().strip()
if w == "":
valid = True
else: else:
result[k] = "", False m = reInteger.match(w)
else: if m:
result[k] = s, True valid = True
else:
valid = False
for k in self.boolChoices.keys(): self.setFieldValidity(name, valid)
choice = self.boolChoices[k]
for i in range(choice.GetCount()):
s = choice.GetClientData(i)
if s:
result[s] = (i == choice.GetSelection())
return result if valid:
tc.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW))
else:
tc.SetBackgroundColour("pink")
tc.Refresh()
evt.Skip()
def assertModified(self, flag): def onTextCtrlFloat(self, evt):
if flag != self.modified: self.assertModified(True)
self.parent.assertModified(self.id, flag) tc = evt.GetEventObject()
self.modified = flag name = tc.GetName()
w = tc.GetValue().strip()
if w == "":
valid = True
else:
m = reFloat.match(w)
if m:
valid = True
else:
valid = False
def setFieldValidity(self, name, flag): self.setFieldValidity(name, valid)
self.fieldValid[name] = flag
pgValid = True if valid:
for k in self.fieldValid.keys(): tc.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW))
if not self.fieldValid[k]: else:
pgValid = False tc.SetBackgroundColour("pink")
break tc.Refresh()
evt.Skip()
self.assertValid(pgValid) def onTextCtrlPin(self, evt):
self.assertModified(True)
tc = evt.GetEventObject()
self.validatePin(tc)
evt.Skip()
def assertValid(self, flag): def onTextCtrl(self, evt):
if flag != self.valid: self.assertModified(True)
self.parent.assertValid(self.id, flag) evt.Skip()
self.valid = flag
def onChoice(self, evt):
self.assertModified(True)
evt.Skip()
def onCheckBox(self, evt):
self.assertModified(True)
evt.Skip()
def setHelpText(self, ht):
for k in self.textControls.keys():
if k in ht.keys():
self.textControls[k].SetToolTip(ht[k])
for k in self.checkBoxes.keys():
if k in ht.keys():
self.checkBoxes[k].SetToolTip(ht[k])
for k in self.radioButtons.keys():
if k in ht.keys():
self.radioButtons[k].SetToolTip(ht[k])
if k in self.radioButtonBoxes.keys():
self.radioButtonBoxes[k].SetToolTip(ht[k])
for k in self.choices.keys():
if k in ht.keys():
self.choices[k].SetToolTip(ht[k])
for k in self.boolChoices.keys():
for candidate in ht.keys():
if candidate.startswith(k):
self.boolChoices[k].SetToolTip(ht[candidate])
break
def insertValues(self, cfgValues):
self.assertValid(True)
self.enableAll(True)
for k in self.fieldValid.keys():
self.fieldValid[k] = True
for k in self.checkBoxes.keys():
if k in cfgValues.keys() and cfgValues[k]:
self.checkBoxes[k].SetValue(True)
else:
self.checkBoxes[k].SetValue(False)
for k in self.textControls.keys():
if k in cfgValues.keys():
self.textControlsOriginal[k] = cfgValues[k]
if cfgValues[k][1] == True:
self.textControls[k].SetValue(str(cfgValues[k][0]))
else:
self.textControls[k].SetValue("")
else:
print("Key " + k + " not found in config data.")
for k in self.choices.keys():
if k in cfgValues.keys():
self.choicesOriginal[k] = cfgValues[k]
self.setChoice(k, cfgValues, "-")
else:
print("Key " + k + " not found in config data.")
for k in self.boolChoices.keys():
choice = self.boolChoices[k]
# Remove items left behind from the previous configuration.
while choice.GetCount() and not choice.GetString(
choice.GetCount() - 1
).startswith("("):
choice.Delete(choice.GetCount() - 1)
# Add items found in this configuration.
for cfg in cfgValues.keys():
if cfg.startswith(k):
if cfg in self.labels.keys():
choice.Append(self.labels[cfg])
else:
choice.Append(cfg)
# As we want to write the configuration name later, not the user
# friendly string, we store the configuration name as client data.
n = choice.GetCount() - 1
choice.SetClientData(n, cfg)
if cfgValues[cfg]:
choice.SetSelection(n)
self.assertModified(False)
def getValues(self):
self.assertModified(False)
result = {}
for k in self.checkBoxes.keys():
cb = self.checkBoxes[k]
result[k] = cb.IsChecked()
for k in self.textControls.keys():
v = self.textControls[k].GetValue()
if v == "":
if k in self.textControlsOriginal.keys():
result[k] = self.textControlsOriginal[k][0], False
else:
result[k] = "", False
else:
result[k] = v, True
for k in self.radioButtons.keys():
result[k] = self.radioButtons[k].GetValue(), True
for k in self.choices.keys():
v = self.choices[k].GetSelection()
s = self.choices[k].GetString(v)
if s == "-":
if k in self.choicesOriginal.keys():
result[k] = self.choicesOriginal[k][0], False
else:
result[k] = "", False
else:
result[k] = s, True
for k in self.boolChoices.keys():
choice = self.boolChoices[k]
for i in range(choice.GetCount()):
s = choice.GetClientData(i)
if s:
result[s] = i == choice.GetSelection()
return result
def assertModified(self, flag):
if flag != self.modified:
self.parent.assertModified(self.id, flag)
self.modified = flag
def setFieldValidity(self, name, flag):
self.fieldValid[name] = flag
pgValid = True
for k in self.fieldValid.keys():
if not self.fieldValid[k]:
pgValid = False
break
self.assertValid(pgValid)
def assertValid(self, flag):
if flag != self.valid:
self.parent.assertValid(self.id, flag)
self.valid = flag

View File

@ -1,183 +1,211 @@
import wx import wx
from configtool.page import Page from configtool.page import Page
class PinoutsPage(wx.Panel, Page): class PinoutsPage(wx.Panel, Page):
def __init__(self, parent, nb, idPg, font): def __init__(self, parent, nb, idPg, font):
wx.Panel.__init__(self, nb, wx.ID_ANY) wx.Panel.__init__(self, nb, wx.ID_ANY)
Page.__init__(self, font) Page.__init__(self, font)
self.parent = parent self.parent = parent
self.id = idPg self.id = idPg
pinXkeys = [('X_STEP_PIN', 2), ('X_DIR_PIN', 2), ('X_MIN_PIN', 2), pinXkeys = [
('X_MAX_PIN', 2), ('X_ENABLE_PIN', 2), ('X_INVERT_DIR', 1), ("X_STEP_PIN", 2),
('X_INVERT_MIN', 1), ('X_INVERT_MAX', 1), ("X_DIR_PIN", 2),
('X_INVERT_ENABLE', 1)] ("X_MIN_PIN", 2),
pinYkeys = [('Y_STEP_PIN', 2), ('Y_DIR_PIN', 2), ('Y_MIN_PIN', 2), ("X_MAX_PIN", 2),
('Y_MAX_PIN', 2), ('Y_ENABLE_PIN', 2), ('Y_INVERT_DIR', 1), ("X_ENABLE_PIN", 2),
('Y_INVERT_MIN', 1), ('Y_INVERT_MAX', 1), ("X_INVERT_DIR", 1),
('Y_INVERT_ENABLE', 1)] ("X_INVERT_MIN", 1),
pinZkeys = [('Z_STEP_PIN', 2), ('Z_DIR_PIN', 2), ('Z_MIN_PIN', 2), ("X_INVERT_MAX", 1),
('Z_MAX_PIN', 2), ('Z_ENABLE_PIN', 2), ('Z_INVERT_DIR', 1), ("X_INVERT_ENABLE", 1),
('Z_INVERT_MIN', 1), ('Z_INVERT_MAX', 1), ]
('Z_INVERT_ENABLE', 1)] pinYkeys = [
pinEkeys = [('E_STEP_PIN', 2), ('E_DIR_PIN', 2), ('E_ENABLE_PIN', 2), ("Y_STEP_PIN", 2),
('E_INVERT_DIR', 1), ('E_INVERT_ENABLE', 1)] ("Y_DIR_PIN", 2),
("Y_MIN_PIN", 2),
("Y_MAX_PIN", 2),
("Y_ENABLE_PIN", 2),
("Y_INVERT_DIR", 1),
("Y_INVERT_MIN", 1),
("Y_INVERT_MAX", 1),
("Y_INVERT_ENABLE", 1),
]
pinZkeys = [
("Z_STEP_PIN", 2),
("Z_DIR_PIN", 2),
("Z_MIN_PIN", 2),
("Z_MAX_PIN", 2),
("Z_ENABLE_PIN", 2),
("Z_INVERT_DIR", 1),
("Z_INVERT_MIN", 1),
("Z_INVERT_MAX", 1),
("Z_INVERT_ENABLE", 1),
]
pinEkeys = [
("E_STEP_PIN", 2),
("E_DIR_PIN", 2),
("E_ENABLE_PIN", 2),
("E_INVERT_DIR", 1),
("E_INVERT_ENABLE", 1),
]
self.labels = {'X_STEP_PIN': "Step Pin:", 'X_DIR_PIN': "Direction Pin:", self.labels = {
'X_MIN_PIN': "Minimum Pin:", 'X_MAX_PIN': "Maximum Pin:", "X_STEP_PIN": "Step Pin:",
'X_ENABLE_PIN': "Enable Pin:", "X_DIR_PIN": "Direction Pin:",
'X_INVERT_DIR': "Invert Direction", "X_MIN_PIN": "Minimum Pin:",
'X_INVERT_MIN': "Invert Minimum", "X_MAX_PIN": "Maximum Pin:",
'X_INVERT_MAX': "Invert Maximum", "X_ENABLE_PIN": "Enable Pin:",
'X_INVERT_ENABLE': "Invert Enable", "X_INVERT_DIR": "Invert Direction",
"X_INVERT_MIN": "Invert Minimum",
"X_INVERT_MAX": "Invert Maximum",
"X_INVERT_ENABLE": "Invert Enable",
"Y_STEP_PIN": "Step Pin:",
"Y_DIR_PIN": "Direction Pin:",
"Y_MIN_PIN": "Minimum Pin:",
"Y_MAX_PIN": "Maximum Pin:",
"Y_ENABLE_PIN": "Enable Pin:",
"Y_INVERT_DIR": "Invert Direction",
"Y_INVERT_MIN": "Invert Minimum",
"Y_INVERT_MAX": "Invert Maximum",
"Y_INVERT_ENABLE": "Invert Enable",
"Z_STEP_PIN": "Step Pin:",
"Z_DIR_PIN": "Direction Pin:",
"Z_MIN_PIN": "Minimum Pin:",
"Z_MAX_PIN": "Maximum Pin:",
"Z_ENABLE_PIN": "Enable Pin:",
"Z_INVERT_DIR": "Invert Direction",
"Z_INVERT_MIN": "Invert Minimum",
"Z_INVERT_MAX": "Invert Maximum",
"Z_INVERT_ENABLE": "Invert Enable",
"E_STEP_PIN": "Step Pin:",
"E_DIR_PIN": "Direction Pin:",
"E_ENABLE_PIN": "Enable Pin:",
"E_INVERT_DIR": "Invert Direction",
"E_INVERT_ENABLE": "Invert Enable",
"PS_ON_PIN": "PSU On Pin:",
"PS_INVERT_ON": "Invert PSU On Pin",
"PS_MOSFET_PIN": "PSU MOSFET Pin:",
"STEPPER_ENABLE_PIN": "Stepper Enable Pin:",
"STEPPER_INVERT_ENABLE": "Stepper Invert Enable",
"SD_CARD_SELECT_PIN": "SD Card Pin:",
"DEBUG_LED_PIN": "Debug LED Pin:",
}
'Y_STEP_PIN': "Step Pin:", 'Y_DIR_PIN': "Direction Pin:", labelWidth = 120
'Y_MIN_PIN': "Minimum Pin:", 'Y_MAX_PIN': "Maximum Pin:",
'Y_ENABLE_PIN': "Enable Pin:",
'Y_INVERT_DIR': "Invert Direction",
'Y_INVERT_MIN': "Invert Minimum",
'Y_INVERT_MAX': "Invert Maximum",
'Y_INVERT_ENABLE': "Invert Enable",
'Z_STEP_PIN': "Step Pin:", 'Z_DIR_PIN': "Direction Pin:", sz = wx.GridBagSizer()
'Z_MIN_PIN': "Minimum Pin:", 'Z_MAX_PIN': "Maximum Pin:", sz.Add((10, 10), pos=(0, 0))
'Z_ENABLE_PIN': "Enable Pin:",
'Z_INVERT_DIR': "Invert Direction",
'Z_INVERT_MIN': "Invert Minimum",
'Z_INVERT_MAX': "Invert Maximum",
'Z_INVERT_ENABLE': "Invert Enable",
'E_STEP_PIN': "Step Pin:", 'E_DIR_PIN': "Direction Pin:", b = wx.StaticBox(self, wx.ID_ANY, "X Axis")
'E_ENABLE_PIN': "Enable Pin:", b.SetFont(font)
'E_INVERT_DIR': "Invert Direction", sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
'E_INVERT_ENABLE': "Invert Enable", sbox.Add((5, 5))
for k, ctype in pinXkeys:
if ctype == 0:
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlPin)
sbox.Add(tc)
elif ctype == 2:
tc = self.addPinChoice(k, labelWidth)
sbox.Add(tc)
else:
cb = self.addCheckBox(k, self.onCheckBox)
sbox.Add(cb, 1, wx.LEFT, 30)
'PS_ON_PIN': "PSU On Pin:", sbox.Add((5, 5))
'PS_INVERT_ON': "Invert PSU On Pin",
'PS_MOSFET_PIN': "PSU MOSFET Pin:",
'STEPPER_ENABLE_PIN': "Stepper Enable Pin:", sz.Add(sbox, pos=(1, 1))
'STEPPER_INVERT_ENABLE': "Stepper Invert Enable",
'SD_CARD_SELECT_PIN': "SD Card Pin:", b = wx.StaticBox(self, wx.ID_ANY, "Y Axis")
'DEBUG_LED_PIN': "Debug LED Pin:"} b.SetFont(font)
sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.Add((5, 5))
for k, ctype in pinYkeys:
if ctype == 0:
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlPin)
sbox.Add(tc)
elif ctype == 2:
tc = self.addPinChoice(k, labelWidth)
sbox.Add(tc)
else:
cb = self.addCheckBox(k, self.onCheckBox)
sbox.Add(cb, 1, wx.LEFT, 30)
labelWidth = 120 sbox.Add((5, 5))
sz = wx.GridBagSizer() sz.Add(sbox, pos=(1, 3))
sz.Add((10, 10), pos = (0, 0))
b = wx.StaticBox(self, wx.ID_ANY, "X Axis") b = wx.StaticBox(self, wx.ID_ANY, "Z Axis")
b.SetFont(font) b.SetFont(font)
sbox = wx.StaticBoxSizer(b, wx.VERTICAL) sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.Add((5, 5)) sbox.Add((5, 5))
for k, ctype in pinXkeys: for k, ctype in pinZkeys:
if ctype == 0: if ctype == 0:
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlPin) tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlPin)
sbox.Add(tc) sbox.Add(tc)
elif ctype == 2: elif ctype == 2:
tc = self.addPinChoice(k, labelWidth) tc = self.addPinChoice(k, labelWidth)
sbox.Add(tc) sbox.Add(tc)
else: else:
cb = self.addCheckBox(k, self.onCheckBox)
sbox.Add(cb, 1, wx.LEFT, 30)
sbox.Add((5, 5))
sz.Add(sbox, pos=(1, 5))
b = wx.StaticBox(self, wx.ID_ANY, "E Axis")
b.SetFont(font)
sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.Add((5, 5))
for k, ctype in pinEkeys:
if ctype == 0:
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlPin)
sbox.Add(tc)
elif ctype == 2:
tc = self.addPinChoice(k, labelWidth)
sbox.Add(tc)
else:
cb = self.addCheckBox(k, self.onCheckBox)
sbox.Add(cb, 1, wx.LEFT, 30)
sbox.Add((5, 5))
sz.Add(sbox, pos=(1, 7))
k = "STEPPER_ENABLE_PIN"
tc = self.addPinChoice(k, labelWidth + 20)
sz.Add(tc, pos=(3, 1))
sz.Add((10, 10), pos=(4, 1))
k = "STEPPER_INVERT_ENABLE"
cb = self.addCheckBox(k, self.onCheckBox) cb = self.addCheckBox(k, self.onCheckBox)
sbox.Add(cb, 1, wx.LEFT, 30) sz.Add(cb, pos=(5, 1), flag=wx.ALIGN_CENTER_HORIZONTAL)
sbox.Add((5, 5)) k = "PS_ON_PIN"
sz.Add(sbox, pos = (1, 1))
b = wx.StaticBox(self, wx.ID_ANY, "Y Axis")
b.SetFont(font)
sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.Add((5, 5))
for k, ctype in pinYkeys:
if ctype == 0:
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlPin)
sbox.Add(tc)
elif ctype == 2:
tc = self.addPinChoice(k, labelWidth) tc = self.addPinChoice(k, labelWidth)
sbox.Add(tc) sz.Add(tc, pos=(3, 3))
else:
k = "PS_INVERT_ON"
cb = self.addCheckBox(k, self.onCheckBox) cb = self.addCheckBox(k, self.onCheckBox)
sbox.Add(cb, 1, wx.LEFT, 30) sz.Add(cb, pos=(5, 3), flag=wx.ALIGN_CENTER_HORIZONTAL)
sbox.Add((5, 5)) k = "PS_MOSFET_PIN"
sz.Add(sbox, pos = (1, 3))
b = wx.StaticBox(self, wx.ID_ANY, "Z Axis")
b.SetFont(font)
sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.Add((5, 5))
for k, ctype in pinZkeys:
if ctype == 0:
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlPin)
sbox.Add(tc)
elif ctype == 2:
tc = self.addPinChoice(k, labelWidth) tc = self.addPinChoice(k, labelWidth)
sbox.Add(tc) sz.Add(tc, pos=(7, 3))
else:
cb = self.addCheckBox(k, self.onCheckBox)
sbox.Add(cb, 1, wx.LEFT, 30)
sbox.Add((5, 5)) k = "SD_CARD_SELECT_PIN"
sz.Add(sbox, pos = (1, 5))
b = wx.StaticBox(self, wx.ID_ANY, "E Axis")
b.SetFont(font)
sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.Add((5, 5))
for k, ctype in pinEkeys:
if ctype == 0:
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlPin)
sbox.Add(tc)
elif ctype == 2:
tc = self.addPinChoice(k, labelWidth) tc = self.addPinChoice(k, labelWidth)
sbox.Add(tc) sz.Add(tc, pos=(3, 7))
else:
cb = self.addCheckBox(k, self.onCheckBox)
sbox.Add(cb, 1, wx.LEFT, 30)
sbox.Add((5, 5)) k = "DEBUG_LED_PIN"
tc = self.addPinChoice(k, labelWidth)
sz.Add(tc, pos=(5, 7))
sz.Add(sbox, pos = (1, 7)) self.SetSizer(sz)
self.enableAll(False)
k = "STEPPER_ENABLE_PIN" def onChoice(self, evt):
tc = self.addPinChoice(k, labelWidth + 20) self.assertModified(True)
sz.Add(tc, pos = (3, 1)) evt.Skip()
sz.Add((10, 10), pos = (4, 1))
k = "STEPPER_INVERT_ENABLE"
cb = self.addCheckBox(k, self.onCheckBox)
sz.Add(cb, pos = (5, 1), flag = wx.ALIGN_CENTER_HORIZONTAL)
k = "PS_ON_PIN"
tc = self.addPinChoice(k, labelWidth)
sz.Add(tc, pos = (3, 3))
k = "PS_INVERT_ON"
cb = self.addCheckBox(k, self.onCheckBox)
sz.Add(cb, pos = (5, 3), flag = wx.ALIGN_CENTER_HORIZONTAL)
k = "PS_MOSFET_PIN"
tc = self.addPinChoice(k, labelWidth)
sz.Add(tc, pos = (7, 3))
k = "SD_CARD_SELECT_PIN"
tc = self.addPinChoice(k, labelWidth)
sz.Add(tc, pos = (3, 7))
k = "DEBUG_LED_PIN"
tc = self.addPinChoice(k, labelWidth)
sz.Add(tc, pos = (5, 7))
self.SetSizer(sz)
self.enableAll(False)
def onChoice(self, evt):
self.assertModified(True)
evt.Skip()

View File

@ -4,314 +4,333 @@ import os
import re import re
from sys import platform from sys import platform
from configtool.data import (defineValueFormat, defineBoolFormat, from configtool.data import (
reHelpTextStart, reHelpTextEnd, defineValueFormat,
reDefine, reDefineBL, reDefQS, reDefQSm, defineBoolFormat,
reDefQSm2, reDefBool, reDefBoolBL, reHelpTextStart,
reHomingOpts, reStartHoming, reEndHoming, reHelpTextEnd,
reDefHoming, reHoming4) reDefine,
reDefineBL,
reDefQS,
reDefQSm,
reDefQSm2,
reDefBool,
reDefBoolBL,
reHomingOpts,
reStartHoming,
reEndHoming,
reDefHoming,
reHoming4,
)
class Printer: class Printer:
def __init__(self, settings): def __init__(self, settings):
self.configFile = None self.configFile = None
self.cfgValues = {} self.cfgValues = {}
self.settings = settings self.settings = settings
self.cfgDir = os.path.join(self.settings.folder, "configtool") self.cfgDir = os.path.join(self.settings.folder, "configtool")
def getValues(self): def getValues(self):
vars = [(x, self.cfgValues[x]) for x in self.cfgValues] vars = [(x, self.cfgValues[x]) for x in self.cfgValues]
return dict(vars) return dict(vars)
def hasData(self): def hasData(self):
return (self.configFile != None) return self.configFile != None
def getFileName(self): def getFileName(self):
return self.configFile return self.configFile
def loadConfigFile(self, fn): def loadConfigFile(self, fn):
cfgFn = os.path.join(self.cfgDir, "printer.generic.h") cfgFn = os.path.join(self.cfgDir, "printer.generic.h")
try: try:
self.cfgBuffer = list(open(cfgFn)) self.cfgBuffer = list(open(cfgFn))
except: except:
return False, cfgFn return False, cfgFn
try: try:
self.userBuffer = list(open(fn)) self.userBuffer = list(open(fn))
except: except:
return False, fn return False, fn
self.configFile = fn self.configFile = fn
gatheringHelpText = False gatheringHelpText = False
helpTextString = "" helpTextString = ""
helpKey = None helpKey = None
self.cfgValues = {} self.cfgValues = {}
self.cfgNames = [] self.cfgNames = []
self.helpText = {} self.helpText = {}
prevLines = ""
for ln in self.cfgBuffer:
if gatheringHelpText:
if reHelpTextEnd.match(ln):
gatheringHelpText = False
helpTextString = helpTextString.strip()
# Keep paragraphs with double-newline.
helpTextString = helpTextString.replace("\n\n ", "\n\n")
# Keep indented lines, typically a list.
helpTextString = helpTextString.replace("\n\n ", "\n\n ")
helpTextString = helpTextString.replace("\n ", "\n\n ")
# Remove all other newlines and indents.
helpTextString = helpTextString.replace("\n ", " ")
hk = helpKey.split()
for k in hk:
self.helpText[k] = helpTextString
helpTextString = ""
helpKey = None
continue
else:
helpTextString += ln
continue
m = reHelpTextStart.match(ln)
if m:
t = m.groups()
gatheringHelpText = True
helpKey = t[0]
continue
if ln.rstrip().endswith("\\"):
prevLines += ln.rstrip()[:-1]
continue
if prevLines != "":
ln = prevLines + ln
prevLines = "" prevLines = ""
for ln in self.cfgBuffer:
if gatheringHelpText:
if reHelpTextEnd.match(ln):
gatheringHelpText = False
helpTextString = helpTextString.strip()
# Keep paragraphs with double-newline.
helpTextString = helpTextString.replace("\n\n ", "\n\n")
# Keep indented lines, typically a list.
helpTextString = helpTextString.replace("\n\n ", "\n\n ")
helpTextString = helpTextString.replace("\n ", "\n\n ")
# Remove all other newlines and indents.
helpTextString = helpTextString.replace("\n ", " ")
hk = helpKey.split()
for k in hk:
self.helpText[k] = helpTextString
helpTextString = ""
helpKey = None
continue
else:
helpTextString += ln
continue
if self.parseCandidateValues(ln): m = reHelpTextStart.match(ln)
continue if m:
t = m.groups()
gatheringHelpText = True
helpKey = t[0]
continue
if self.parseHoming(ln): if ln.rstrip().endswith("\\"):
continue prevLines += ln.rstrip()[:-1]
continue
self.parseDefineName(ln) if prevLines != "":
self.parseDefineValue(ln) ln = prevLines + ln
prevLines = ""
# Set all boolean generic configuration items to False, so items not yet if self.parseCandidateValues(ln):
# existing in the user configuration default to disabled. continue
for k in self.cfgValues.keys():
if isinstance(self.cfgValues[k], bool):
self.cfgValues[k] = False
# Read the user configuration. This usually overwrites all of the items if self.parseHoming(ln):
# read above, but not those missing in the user configuration, e.g. continue
# when reading an older config.
gatheringHelpText = False
prevLines = ""
for ln in self.userBuffer:
if gatheringHelpText:
if reHelpTextEnd.match(ln):
gatheringHelpText = False
continue
if reHelpTextStart.match(ln): self.parseDefineName(ln)
gatheringHelpText = True self.parseDefineValue(ln)
continue
if ln.rstrip().endswith("\\"): # Set all boolean generic configuration items to False, so items not yet
prevLines += ln.rstrip()[:-1] # existing in the user configuration default to disabled.
continue for k in self.cfgValues.keys():
if isinstance(self.cfgValues[k], bool):
self.cfgValues[k] = False
if prevLines != "": # Read the user configuration. This usually overwrites all of the items
ln = prevLines + ln # read above, but not those missing in the user configuration, e.g.
# when reading an older config.
gatheringHelpText = False
prevLines = "" prevLines = ""
for ln in self.userBuffer:
if gatheringHelpText:
if reHelpTextEnd.match(ln):
gatheringHelpText = False
continue
if self.parseCandidateValues(ln): if reHelpTextStart.match(ln):
continue gatheringHelpText = True
continue
if self.parseHoming(ln): if ln.rstrip().endswith("\\"):
continue prevLines += ln.rstrip()[:-1]
continue
self.parseDefineValue(ln) if prevLines != "":
ln = prevLines + ln
prevLines = ""
# Parsing done. All parsed stuff is now in these array and dicts. if self.parseCandidateValues(ln):
if self.settings.verbose >= 2: continue
print(self.cfgValues) # #defines with a value.
print(self.cfgNames) # Names found in the generic file.
if self.settings.verbose >= 3:
print(self.helpText)
return True, None if self.parseHoming(ln):
continue
def parseDefineName(self, ln): self.parseDefineValue(ln)
m = reDefBool.search(ln)
if m:
t = m.groups()
if len(t) == 1:
self.cfgNames.append(t[0])
return True
return False # Parsing done. All parsed stuff is now in these array and dicts.
if self.settings.verbose >= 2:
print(self.cfgValues) # #defines with a value.
print(self.cfgNames) # Names found in the generic file.
if self.settings.verbose >= 3:
print(self.helpText)
def parseCandidateValues(self, ln): return True, None
m = reHomingOpts.match(ln)
if m:
t = m.groups()
if len(t) == 1:
if 'HOMING_OPTS' in self.cfgValues:
if t[0] not in self.cfgValues['HOMING_OPTS']:
self.cfgValues['HOMING_OPTS'].append(t[0])
else:
self.cfgValues['HOMING_OPTS'] = [t[0]]
return True
def parseHoming(self, ln): def parseDefineName(self, ln):
m = reDefHoming.search(ln) m = reDefBool.search(ln)
if m:
t = m.groups()
if len(t) == 1:
n = reHoming4.search(t[0])
if n:
u = n.groups()
if len(u) == 4:
t2 = []
for x in u:
t2.append(x)
self.cfgValues['HOMING_STEP1'] = t2[0]
self.cfgValues['HOMING_STEP2'] = t2[1]
self.cfgValues['HOMING_STEP3'] = t2[2]
self.cfgValues['HOMING_STEP4'] = t2[3]
return True
return None
return True
def parseDefineValue(self, ln):
m = reDefQS.search(ln)
if m:
t = m.groups()
if len(t) == 2:
m = reDefQSm.search(ln)
if m: if m:
t = m.groups() t = m.groups()
tt = re.findall(reDefQSm2, t[1]) if len(t) == 1:
if len(tt) == 1 and (t[0] in self.cfgNames): self.cfgNames.append(t[0])
self.cfgValues[t[0]] = tt[0], True
return True
elif len(tt) > 1 and (t[0] in self.cfgNames):
self.cfgValues[t[0]] = tt, True
return True return True
m = reDefine.search(ln) return False
if m:
t = m.groups()
if len(t) == 2 and (t[0] in self.cfgNames):
if reDefineBL.search(ln):
self.cfgValues[t[0]] = t[1], True
else:
self.cfgValues[t[0]] = t[1], False
return True
m = reDefBool.search(ln) def parseCandidateValues(self, ln):
if m: m = reHomingOpts.match(ln)
t = m.groups()
# Accept booleans, but not those for which a value exists already.
# Booleans already existing as values are most likely misconfigured
# manual edits (or result of a bug).
if len(t) == 1 and t[0] in self.cfgNames \
and not (t[0] in self.cfgValues \
and isinstance(self.cfgValues[t[0]], tuple)):
if reDefBoolBL.search(ln):
self.cfgValues[t[0]] = True
else:
self.cfgValues[t[0]] = False
return True
return False
def saveConfigFile(self, path, values):
if not values:
values = self.cfgValues
if self.settings.verbose >= 1:
print("Saving printer: %s." % path)
if self.settings.verbose >= 2:
print(values)
fp = file(path, 'w')
self.configFile = path
skipToHomingEnd = False
for ln in self.cfgBuffer:
m = reStartHoming.match(ln)
if m:
fp.write(ln)
sstr = "%s, %s, %s, %s" % ((values['HOMING_STEP1']),
(values['HOMING_STEP2']),
(values['HOMING_STEP3']),
(values['HOMING_STEP4']))
fp.write("DEFINE_HOMING(%s)\n" % sstr)
skipToHomingEnd = True
continue
if skipToHomingEnd:
m = reEndHoming.match(ln)
if m: if m:
fp.write(ln) t = m.groups()
skipToHomingEnd = False if len(t) == 1:
continue if "HOMING_OPTS" in self.cfgValues:
if t[0] not in self.cfgValues["HOMING_OPTS"]:
self.cfgValues["HOMING_OPTS"].append(t[0])
else:
self.cfgValues["HOMING_OPTS"] = [t[0]]
return True
def parseHoming(self, ln):
m = reDefHoming.search(ln)
if m:
t = m.groups()
if len(t) == 1:
n = reHoming4.search(t[0])
if n:
u = n.groups()
if len(u) == 4:
t2 = []
for x in u:
t2.append(x)
self.cfgValues["HOMING_STEP1"] = t2[0]
self.cfgValues["HOMING_STEP2"] = t2[1]
self.cfgValues["HOMING_STEP3"] = t2[2]
self.cfgValues["HOMING_STEP4"] = t2[3]
return True
return None
return True
def parseDefineValue(self, ln):
m = reDefQS.search(ln)
if m:
t = m.groups()
if len(t) == 2:
m = reDefQSm.search(ln)
if m:
t = m.groups()
tt = re.findall(reDefQSm2, t[1])
if len(tt) == 1 and (t[0] in self.cfgNames):
self.cfgValues[t[0]] = tt[0], True
return True
elif len(tt) > 1 and (t[0] in self.cfgNames):
self.cfgValues[t[0]] = tt, True
return True
m = reDefine.search(ln)
if m:
t = m.groups()
if len(t) == 2 and (t[0] in self.cfgNames):
if reDefineBL.search(ln):
self.cfgValues[t[0]] = t[1], True
else:
self.cfgValues[t[0]] = t[1], False
return True
m = reDefBool.search(ln)
if m:
t = m.groups()
# Accept booleans, but not those for which a value exists already.
# Booleans already existing as values are most likely misconfigured
# manual edits (or result of a bug).
if (
len(t) == 1
and t[0] in self.cfgNames
and not (
t[0] in self.cfgValues and isinstance(self.cfgValues[t[0]], tuple)
)
):
if reDefBoolBL.search(ln):
self.cfgValues[t[0]] = True
else:
self.cfgValues[t[0]] = False
return True
return False
def saveConfigFile(self, path, values):
if not values:
values = self.cfgValues
if self.settings.verbose >= 1:
print("Saving printer: %s." % path)
if self.settings.verbose >= 2:
print(values)
fp = file(path, "w")
self.configFile = path
skipToHomingEnd = False
for ln in self.cfgBuffer:
m = reStartHoming.match(ln)
if m:
fp.write(ln)
sstr = "%s, %s, %s, %s" % (
(values["HOMING_STEP1"]),
(values["HOMING_STEP2"]),
(values["HOMING_STEP3"]),
(values["HOMING_STEP4"]),
)
fp.write("DEFINE_HOMING(%s)\n" % sstr)
skipToHomingEnd = True
continue
if skipToHomingEnd:
m = reEndHoming.match(ln)
if m:
fp.write(ln)
skipToHomingEnd = False
continue
m = reDefine.match(ln)
if m:
t = m.groups()
if len(t) == 2 and t[0] in values.keys():
v = values[t[0]]
self.cfgValues[t[0]] = v
if v[1] == False:
fp.write("//")
fp.write(defineValueFormat % (t[0], v[0]))
else:
if t[0] == "CANNED_CYCLE":
# Known to be absent in the GUI. Worse, this value is replaced
# by the one in the metadata file.
#
# TODO: make value reading above recognize wether this value is
# commented out or not. Reading the value its self works
# already. Hint: it's the rule using reDefQS, reDefQSm, etc.
#
# TODO: add a multiline text field in the GUI to deal with this.
#
# TODO: write this value out properly. In /* comments */, if
# disabled.
#
# TODO: currently, the lines beyond the ones with the #define are
# treated like arbitrary comments. Having the former TODOs
# done, this will lead to duplicates.
fp.write(ln)
else:
print("Value key " + t[0] + " not found in GUI.")
continue
m = reDefBoolBL.match(ln)
if m:
t = m.groups()
if len(t) == 1 and t[0] in values.keys():
v = values[t[0]]
self.cfgValues[t[0]] = v
if v == "" or v == False:
fp.write("//")
fp.write(defineBoolFormat % t[0])
else:
print("Boolean key " + t[0] + " not found in GUI.")
continue
m = reDefine.match(ln)
if m:
t = m.groups()
if len(t) == 2 and t[0] in values.keys():
v = values[t[0]]
self.cfgValues[t[0]] = v
if v[1] == False:
fp.write("//")
fp.write(defineValueFormat % (t[0], v[0]))
else:
if t[0] == 'CANNED_CYCLE':
# Known to be absent in the GUI. Worse, this value is replaced
# by the one in the metadata file.
#
# TODO: make value reading above recognize wether this value is
# commented out or not. Reading the value its self works
# already. Hint: it's the rule using reDefQS, reDefQSm, etc.
#
# TODO: add a multiline text field in the GUI to deal with this.
#
# TODO: write this value out properly. In /* comments */, if
# disabled.
#
# TODO: currently, the lines beyond the ones with the #define are
# treated like arbitrary comments. Having the former TODOs
# done, this will lead to duplicates.
fp.write(ln) fp.write(ln)
else:
print("Value key " + t[0] + " not found in GUI.")
continue fp.close()
m = reDefBoolBL.match(ln) return True
if m:
t = m.groups()
if len(t) == 1 and t[0] in values.keys():
v = values[t[0]]
self.cfgValues[t[0]] = v
if v == "" or v == False:
fp.write("//")
fp.write(defineBoolFormat % t[0])
else:
print("Boolean key " + t[0] + " not found in GUI.")
continue
fp.write(ln)
fp.close()
return True

View File

@ -1,14 +1,22 @@
import os import os
import wx import wx
import re import re
from sys import platform from sys import platform
from configtool.decoration import Decoration from configtool.decoration import Decoration
from configtool.data import (defineValueFormat, defineBoolFormat, from configtool.data import (
reHelpTextStart, reHelpTextEnd, defineValueFormat,
reDefine, reDefineBL, reDefQS, reDefQSm, defineBoolFormat,
reDefQSm2, reDefBool, reDefBoolBL) reHelpTextStart,
reHelpTextEnd,
reDefine,
reDefineBL,
reDefQS,
reDefQSm,
reDefQSm2,
reDefBool,
reDefBoolBL,
)
from configtool.mechanicalpage import MechanicalPage from configtool.mechanicalpage import MechanicalPage
from configtool.accelerationpage import AccelerationPage from configtool.accelerationpage import AccelerationPage
from configtool.miscellaneouspage import MiscellaneousPage from configtool.miscellaneouspage import MiscellaneousPage
@ -17,249 +25,277 @@ from configtool.printer import Printer
class PrinterPanel(wx.Panel): class PrinterPanel(wx.Panel):
def __init__(self, parent, nb, settings): def __init__(self, parent, nb, settings):
wx.Panel.__init__(self, nb, wx.ID_ANY) wx.Panel.__init__(self, nb, wx.ID_ANY)
self.parent = parent self.parent = parent
self.deco = Decoration() self.deco = Decoration()
self.protFileLoaded = False self.protFileLoaded = False
self.settings = settings self.settings = settings
self.printer = Printer(self.settings) self.printer = Printer(self.settings)
self.dir = os.path.join(self.settings.folder, "config") self.dir = os.path.join(self.settings.folder, "config")
self.SetBackgroundColour(self.deco.getBackgroundColour()) self.SetBackgroundColour(self.deco.getBackgroundColour())
self.Bind(wx.EVT_PAINT, self.deco.onPaintBackground) self.Bind(wx.EVT_PAINT, self.deco.onPaintBackground)
sz = wx.BoxSizer(wx.HORIZONTAL) sz = wx.BoxSizer(wx.HORIZONTAL)
self.nb = wx.Notebook(self, wx.ID_ANY, size = (21, 21), self.nb = wx.Notebook(self, wx.ID_ANY, size=(21, 21), style=wx.BK_DEFAULT)
style = wx.BK_DEFAULT) self.nb.SetBackgroundColour(self.deco.getBackgroundColour())
self.nb.SetBackgroundColour(self.deco.getBackgroundColour()) self.nb.SetFont(self.settings.font)
self.nb.SetFont(self.settings.font)
self.pages = [] self.pages = []
self.titles = [] self.titles = []
self.pageModified = [] self.pageModified = []
self.pageValid = [] self.pageValid = []
self.pgMech = self.registerPage(MechanicalPage, "Mechanical") self.pgMech = self.registerPage(MechanicalPage, "Mechanical")
self.pgAcc = self.registerPage(AccelerationPage, "Acceleration") self.pgAcc = self.registerPage(AccelerationPage, "Acceleration")
self.pgMiscellaneous = self.registerPage(MiscellaneousPage, self.pgMiscellaneous = self.registerPage(MiscellaneousPage, "Miscellaneous")
"Miscellaneous")
sz.Add(self.nb, 1, wx.EXPAND + wx.ALL, 5) sz.Add(self.nb, 1, wx.EXPAND + wx.ALL, 5)
self.SetSizer(sz) self.SetSizer(sz)
self.Fit() self.Fit()
def registerPage(self, klass, label, *args, **kwargs): def registerPage(self, klass, label, *args, **kwargs):
page = klass(self, self.nb, len(self.pages), *args, page = klass(
font = self.settings.font, **kwargs) self, self.nb, len(self.pages), *args, font=self.settings.font, **kwargs
self.nb.AddPage(page, label) )
self.pages.append(page) self.nb.AddPage(page, label)
self.titles.append(label) self.pages.append(page)
self.pageModified.append(False) self.titles.append(label)
self.pageValid.append(True) self.pageModified.append(False)
return page self.pageValid.append(True)
return page
def assertModified(self, pg, flag = True): def assertModified(self, pg, flag=True):
self.pageModified[pg] = flag self.pageModified[pg] = flag
self.modifyTab(pg) self.modifyTab(pg)
def isModified(self): def isModified(self):
return (True in self.pageModified) return True in self.pageModified
def isValid(self): def isValid(self):
return not (False in self.pageValid) return not (False in self.pageValid)
def hasData(self): def hasData(self):
return self.printer.hasData() return self.printer.hasData()
def getFileName(self): def getFileName(self):
return self.printer.configFile return self.printer.configFile
def assertValid(self, pg, flag = True): def assertValid(self, pg, flag=True):
self.pageValid[pg] = flag self.pageValid[pg] = flag
self.modifyTab(pg) self.modifyTab(pg)
if False in self.pageValid: if False in self.pageValid:
self.parent.enableSavePrinter(False, False) self.parent.enableSavePrinter(False, False)
else: else:
self.parent.enableSavePrinter(not self.protFileLoaded, True) self.parent.enableSavePrinter(not self.protFileLoaded, True)
def modifyTab(self, pg): def modifyTab(self, pg):
if self.pageModified[pg] and not self.pageValid[pg]: if self.pageModified[pg] and not self.pageValid[pg]:
pfx = "?* " pfx = "?* "
elif self.pageModified[pg]: elif self.pageModified[pg]:
pfx = "* " pfx = "* "
elif not self.pageValid[pg]: elif not self.pageValid[pg]:
pfx = "? " pfx = "? "
else: else:
pfx = "" pfx = ""
self.nb.SetPageText(pg, pfx + self.titles[pg]) self.nb.SetPageText(pg, pfx + self.titles[pg])
if True in self.pageModified and False in self.pageValid: if True in self.pageModified and False in self.pageValid:
pfx = "?* " pfx = "?* "
elif True in self.pageModified: elif True in self.pageModified:
pfx = "* " pfx = "* "
elif False in self.pageValid: elif False in self.pageValid:
pfx = "? " pfx = "? "
else: else:
pfx = "" pfx = ""
self.parent.setPrinterTabDecor(pfx) self.parent.setPrinterTabDecor(pfx)
def setHeaters(self, ht): def setHeaters(self, ht):
return self.pgMiscellaneous.setHeaters(ht) return self.pgMiscellaneous.setHeaters(ht)
def onClose(self, evt): def onClose(self, evt):
if not self.confirmLoseChanges("exit"): if not self.confirmLoseChanges("exit"):
return return
self.Destroy() self.Destroy()
def confirmLoseChanges(self, msg): def confirmLoseChanges(self, msg):
if True not in self.pageModified: if True not in self.pageModified:
return True return True
dlg = wx.MessageDialog(self, "Are you sure you want to " + msg + "?\n" dlg = wx.MessageDialog(
"There are changes to your printer " self,
"configuration that will be lost.", "Are you sure you want to " + msg + "?\n"
"Changes pending", "There are changes to your printer "
wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION) "configuration that will be lost.",
rc = dlg.ShowModal() "Changes pending",
dlg.Destroy() wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION,
)
rc = dlg.ShowModal()
dlg.Destroy()
if rc != wx.ID_YES: if rc != wx.ID_YES:
return False return False
return True return True
def onLoadConfig(self, evt): def onLoadConfig(self, evt):
if not self.confirmLoseChanges("load a new printer configuration"): if not self.confirmLoseChanges("load a new printer configuration"):
return return
if platform.startswith("darwin"): if platform.startswith("darwin"):
# Mac OS X appears to be a bit limited on wildcards. # Mac OS X appears to be a bit limited on wildcards.
wildcard = "Printer configuration (printer.*.h)|*.h" wildcard = "Printer configuration (printer.*.h)|*.h"
else: else:
wildcard = "Printer configuration (printer.*.h)|printer.*.h" wildcard = "Printer configuration (printer.*.h)|printer.*.h"
dlg = wx.FileDialog(self, message = "Choose a printer config file", dlg = wx.FileDialog(
defaultDir = self.dir, defaultFile = "", self,
wildcard = wildcard, style = wx.FD_OPEN | wx.FD_CHANGE_DIR) message="Choose a printer config file",
defaultDir=self.dir,
defaultFile="",
wildcard=wildcard,
style=wx.FD_OPEN | wx.FD_CHANGE_DIR,
)
path = None path = None
if dlg.ShowModal() == wx.ID_OK: if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath() path = dlg.GetPath()
dlg.Destroy() dlg.Destroy()
if path is None: if path is None:
return return
self.dir = os.path.dirname(path) self.dir = os.path.dirname(path)
rc, efn = self.loadConfigFile(path) rc, efn = self.loadConfigFile(path)
if not rc: if not rc:
dlg = wx.MessageDialog(self, "Unable to process file %s." % efn, dlg = wx.MessageDialog(
"File error", wx.OK + wx.ICON_ERROR) self,
dlg.ShowModal() "Unable to process file %s." % efn,
dlg.Destroy() "File error",
return wx.OK + wx.ICON_ERROR,
)
dlg.ShowModal()
dlg.Destroy()
return
def loadConfigFile(self, fn): def loadConfigFile(self, fn):
ok, file = self.printer.loadConfigFile(fn) ok, file = self.printer.loadConfigFile(fn)
if not ok: if not ok:
return ok, file return ok, file
if os.path.basename(fn) in protectedFiles: if os.path.basename(fn) in protectedFiles:
self.parent.enableSavePrinter(False, True) self.parent.enableSavePrinter(False, True)
self.protFileLoaded = True self.protFileLoaded = True
else: else:
self.protFileLoaded = False self.protFileLoaded = False
self.parent.enableSavePrinter(True, True) self.parent.enableSavePrinter(True, True)
self.parent.setPrinterTabFile(os.path.basename(fn)) self.parent.setPrinterTabFile(os.path.basename(fn))
for pg in self.pages: for pg in self.pages:
pg.insertValues(self.printer.cfgValues) pg.insertValues(self.printer.cfgValues)
pg.setHelpText(self.printer.helpText) pg.setHelpText(self.printer.helpText)
k = 'DC_EXTRUDER' k = "DC_EXTRUDER"
if k in self.printer.cfgValues.keys() and self.printer.cfgValues[k][1] == True: if k in self.printer.cfgValues.keys() and self.printer.cfgValues[k][1] == True:
self.pgMiscellaneous.setOriginalHeater(self.printer.cfgValues[k][0]) self.pgMiscellaneous.setOriginalHeater(self.printer.cfgValues[k][0])
else: else:
self.pgMiscellaneous.setOriginalHeater(None) self.pgMiscellaneous.setOriginalHeater(None)
return True, None return True, None
def onSaveConfig(self, evt): def onSaveConfig(self, evt):
path = self.getFileName() path = self.getFileName()
return self.saveConfigFile(path) return self.saveConfigFile(path)
def onSaveConfigAs(self, evt): def onSaveConfigAs(self, evt):
if platform.startswith("darwin"): if platform.startswith("darwin"):
# Mac OS X appears to be a bit limited on wildcards. # Mac OS X appears to be a bit limited on wildcards.
wildcard = "Printer configuration (printer.*.h)|*.h" wildcard = "Printer configuration (printer.*.h)|*.h"
else: else:
wildcard = "Printer configuration (printer.*.h)|printer.*.h" wildcard = "Printer configuration (printer.*.h)|printer.*.h"
dlg = wx.FileDialog(self, message = "Save as ...", defaultDir = self.dir, dlg = wx.FileDialog(
defaultFile = "", wildcard = wildcard, self,
style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) message="Save as ...",
defaultDir=self.dir,
defaultFile="",
wildcard=wildcard,
style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT,
)
val = dlg.ShowModal() val = dlg.ShowModal()
if val != wx.ID_OK: if val != wx.ID_OK:
dlg.Destroy() dlg.Destroy()
return return
path = dlg.GetPath() path = dlg.GetPath()
dlg.Destroy() dlg.Destroy()
rc = self.saveConfigFile(path) rc = self.saveConfigFile(path)
if rc: if rc:
self.parent.setPrinterTabFile(os.path.basename(path)) self.parent.setPrinterTabFile(os.path.basename(path))
self.protFileLoaded = False self.protFileLoaded = False
self.parent.enableSavePrinter(True, True) self.parent.enableSavePrinter(True, True)
return rc return rc
def saveConfigFile(self, path): def saveConfigFile(self, path):
if os.path.basename(path) in protectedFiles: if os.path.basename(path) in protectedFiles:
dlg = wx.MessageDialog(self, "It's not allowed to overwrite files " dlg = wx.MessageDialog(
"distributed by Teacup. Choose another name.", self,
"Protected file error", wx.OK + wx.ICON_ERROR) "It's not allowed to overwrite files "
dlg.ShowModal() "distributed by Teacup. Choose another name.",
dlg.Destroy() "Protected file error",
return False wx.OK + wx.ICON_ERROR,
)
dlg.ShowModal()
dlg.Destroy()
return False
if not os.path.basename(path).startswith("printer."): if not os.path.basename(path).startswith("printer."):
dlg = wx.MessageDialog(self, "Illegal file name: %s.\n" dlg = wx.MessageDialog(
"File name must begin with \"printer.\"" % path, self,
"Illegal file name", wx.OK + wx.ICON_ERROR) "Illegal file name: %s.\n"
dlg.ShowModal() 'File name must begin with "printer."' % path,
dlg.Destroy() "Illegal file name",
return False wx.OK + wx.ICON_ERROR,
)
dlg.ShowModal()
dlg.Destroy()
return False
values = {} values = {}
for pg in self.pages: for pg in self.pages:
v1 = pg.getValues() v1 = pg.getValues()
for k in v1.keys(): for k in v1.keys():
values[k] = v1[k] values[k] = v1[k]
ext = os.path.splitext(os.path.basename(path))[1] ext = os.path.splitext(os.path.basename(path))[1]
self.dir = os.path.dirname(path) self.dir = os.path.dirname(path)
if ext == "": if ext == "":
path += ".h" path += ".h"
try: try:
self.printer.saveConfigFile(path, values) self.printer.saveConfigFile(path, values)
except: except:
dlg = wx.MessageDialog(self, "Unable to write to file %s." % path, dlg = wx.MessageDialog(
"File error", wx.OK + wx.ICON_ERROR) self,
dlg.ShowModal() "Unable to write to file %s." % path,
dlg.Destroy() "File error",
return False wx.OK + wx.ICON_ERROR,
)
dlg.ShowModal()
dlg.Destroy()
return False
return True return True

View File

@ -1,20 +1,19 @@
protectedFiles = [ protectedFiles = [
"board.3drag.h", "board.3drag.h",
"board.gen3.h", "board.gen3.h",
"board.gen6.h", "board.gen6.h",
"board.gen7-arm.h", "board.gen7-arm.h",
"board.gen7-v1.1-v1.3.h", "board.gen7-v1.1-v1.3.h",
"board.gen7-v1.4.h", "board.gen7-v1.4.h",
"board.nanoheart-v1.0.h", "board.nanoheart-v1.0.h",
"board.ramps-v1.2.h", "board.ramps-v1.2.h",
"board.ramps-v1.3.h", "board.ramps-v1.3.h",
"board.rumba.h", "board.rumba.h",
"board.sanguinololu-v1.1.h", "board.sanguinololu-v1.1.h",
"board.sanguinololu-v1.2.h", "board.sanguinololu-v1.2.h",
"board.sanguish.h", "board.sanguish.h",
"board.teensy-v2.0.h", "board.teensy-v2.0.h",
"board.teensy++-v2.0.h", "board.teensy++-v2.0.h",
"printer.mendel.h", "printer.mendel.h",
"printer.wolfstrap.h" "printer.wolfstrap.h",
] ]

View File

@ -1,86 +1,88 @@
import wx import wx
class SensorList(wx.ListCtrl): class SensorList(wx.ListCtrl):
def __init__(self, parent, font): def __init__(self, parent, font):
self.parent = parent self.parent = parent
self.currentItem = None self.currentItem = None
wx.ListCtrl.__init__(self, parent, wx.ID_ANY, wx.ListCtrl.__init__(
size = (105 + 105 + 55 + 280 + 4, 100), self,
style = wx.LC_REPORT | wx.LC_VIRTUAL | parent,
wx.LC_HRULES | wx.LC_VRULES) wx.ID_ANY,
size=(105 + 105 + 55 + 280 + 4, 100),
style=wx.LC_REPORT | wx.LC_VIRTUAL | wx.LC_HRULES | wx.LC_VRULES,
)
self.SetFont(font) self.SetFont(font)
self.valid = [] self.valid = []
self.sensorList = [] self.sensorList = []
self.InsertColumn(0, "Name") self.InsertColumn(0, "Name")
self.InsertColumn(1, "Sensor Type") self.InsertColumn(1, "Sensor Type")
self.InsertColumn(2, "Pin") self.InsertColumn(2, "Pin")
self.InsertColumn(3, "Additional") self.InsertColumn(3, "Additional")
self.SetColumnWidth(0, 105) self.SetColumnWidth(0, 105)
self.SetColumnWidth(1, 105) self.SetColumnWidth(1, 105)
self.SetColumnWidth(2, 55) self.SetColumnWidth(2, 55)
self.SetColumnWidth(3, 280) self.SetColumnWidth(3, 280)
self.SetItemCount(0) self.SetItemCount(0)
self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected) self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnItemDeselected) self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnItemDeselected)
def updateList(self, sensorList): def updateList(self, sensorList):
self.sensorList = sensorList self.sensorList = sensorList
self.valid = [True] * len(sensorList) self.valid = [True] * len(sensorList)
self.currentItem = None self.currentItem = None
self.parent.setItemSelected(None) self.parent.setItemSelected(None)
i = self.GetFirstSelected() i = self.GetFirstSelected()
while i != -1: while i != -1:
self.Select(i, False) self.Select(i, False)
i = self.GetFirstSelected() i = self.GetFirstSelected()
self.SetItemCount(len(sensorList)) self.SetItemCount(len(sensorList))
def setRowValidity(self, i, flag = False): def setRowValidity(self, i, flag=False):
if i < 0 or i >= len(self.sensorList): if i < 0 or i >= len(self.sensorList):
return return
self.valid[i] = flag self.valid[i] = flag
self.Refresh() self.Refresh()
def setTableValidity(self, flag = False): def setTableValidity(self, flag=False):
for i in range(len(self.sensorList)): for i in range(len(self.sensorList)):
self.setRowValidity(i, flag) self.setRowValidity(i, flag)
def OnItemSelected(self, event): def OnItemSelected(self, event):
self.currentItem = event.m_itemIndex self.currentItem = event.m_itemIndex
self.parent.setItemSelected(self.currentItem) self.parent.setItemSelected(self.currentItem)
def OnItemDeselected(self, event): def OnItemDeselected(self, event):
self.currentItem = None self.currentItem = None
self.parent.setItemSelected(None) self.parent.setItemSelected(None)
def getColumnText(self, index, col): def getColumnText(self, index, col):
item = self.GetItem(index, col) item = self.GetItem(index, col)
return item.GetText() return item.GetText()
def OnGetItemText(self, item, col): def OnGetItemText(self, item, col):
if item < 0 or item >= len(self.sensorList): if item < 0 or item >= len(self.sensorList):
return "Error - no sensors" return "Error - no sensors"
s = self.sensorList[item] s = self.sensorList[item]
if col == 0: if col == 0:
return s[0] return s[0]
elif col == 1: elif col == 1:
return s[1] return s[1]
elif col == 2: elif col == 2:
return s[2] return s[2]
elif len(s) == 3: elif len(s) == 3:
return "" return ""
else: else:
if s[3] is None: if s[3] is None:
return "" return ""
else: else:
return "[%s]" % (", ".join(s[3])) return "[%s]" % (", ".join(s[3]))

View File

@ -8,191 +8,208 @@ from .addsensordlg import AddSensorDlg
class SensorsPage(wx.Panel, Page): class SensorsPage(wx.Panel, Page):
def __init__(self, parent, nb, idPg, heatersPage, font): def __init__(self, parent, nb, idPg, heatersPage, font):
wx.Panel.__init__(self, nb, wx.ID_ANY) wx.Panel.__init__(self, nb, wx.ID_ANY)
Page.__init__(self, font) Page.__init__(self, font)
self.parent = parent self.parent = parent
self.heatersPage = heatersPage self.heatersPage = heatersPage
self.font = font self.font = font
self.id = idPg self.id = idPg
self.sensorTypeKeys = {'TT_MAX6675': 'TEMP_MAX6675', self.sensorTypeKeys = {
'TT_THERMISTOR': 'TEMP_THERMISTOR', "TT_MAX6675": "TEMP_MAX6675",
'TT_AD595': 'TEMP_AD595', 'TT_PT100': 'TEMP_PT100', "TT_THERMISTOR": "TEMP_THERMISTOR",
'TT_INTERCOM': 'TEMP_INTERCOM', "TT_AD595": "TEMP_AD595",
'TT_MCP3008': 'TEMP_MCP3008'} "TT_PT100": "TEMP_PT100",
self.labels = {'TEMP_MAX6675': "MAX6675", 'TEMP_THERMISTOR': "Thermistor", "TT_INTERCOM": "TEMP_INTERCOM",
'TEMP_AD595': "AD595", 'TEMP_PT100': "PT100", "TT_MCP3008": "TEMP_MCP3008",
'TEMP_INTERCOM': "Intercom", }
'TEMP_MCP3008': 'MCP3008', self.labels = {
'MCP3008_SELECT_PIN': "MCP3008 CS Pin:"} "TEMP_MAX6675": "MAX6675",
"TEMP_THERMISTOR": "Thermistor",
"TEMP_AD595": "AD595",
"TEMP_PT100": "PT100",
"TEMP_INTERCOM": "Intercom",
"TEMP_MCP3008": "MCP3008",
"MCP3008_SELECT_PIN": "MCP3008 CS Pin:",
}
self.validPins = pinNames self.validPins = pinNames
labelWidth = 120 labelWidth = 120
sz = wx.GridBagSizer() sz = wx.GridBagSizer()
sz.Add((10, 10), pos = (0, 0)) sz.Add((10, 10), pos=(0, 0))
self.sensors = [] self.sensors = []
self.lb = SensorList(self, font) self.lb = SensorList(self, font)
sz.Add(self.lb, pos = (1, 1)) sz.Add(self.lb, pos=(1, 1))
sz.Add((20, 20), pos = (1, 2)) sz.Add((20, 20), pos=(1, 2))
bsz = wx.BoxSizer(wx.VERTICAL) bsz = wx.BoxSizer(wx.VERTICAL)
self.bAdd = wx.Button(self, wx.ID_ANY, "Add", size = BSIZESMALL) self.bAdd = wx.Button(self, wx.ID_ANY, "Add", size=BSIZESMALL)
self.bAdd.SetBackgroundColour(self.deco.getBackgroundColour()) self.bAdd.SetBackgroundColour(self.deco.getBackgroundColour())
self.bAdd.SetFont(font) self.bAdd.SetFont(font)
self.Bind(wx.EVT_BUTTON, self.doAdd, self.bAdd) self.Bind(wx.EVT_BUTTON, self.doAdd, self.bAdd)
self.bAdd.Enable(False) self.bAdd.Enable(False)
self.bAdd.SetToolTip("Add a sensor to the configuration.") self.bAdd.SetToolTip("Add a sensor to the configuration.")
bsz.Add(self.bAdd) bsz.Add(self.bAdd)
bsz.Add((10, 10)) bsz.Add((10, 10))
self.bModify = wx.Button(self, wx.ID_ANY, "Modify", size = BSIZESMALL) self.bModify = wx.Button(self, wx.ID_ANY, "Modify", size=BSIZESMALL)
self.bModify.SetBackgroundColour(self.deco.getBackgroundColour()) self.bModify.SetBackgroundColour(self.deco.getBackgroundColour())
self.bModify.SetFont(font) self.bModify.SetFont(font)
self.bModify.Enable(False) self.bModify.Enable(False)
self.Bind(wx.EVT_BUTTON, self.doModify, self.bModify) self.Bind(wx.EVT_BUTTON, self.doModify, self.bModify)
bsz.Add(self.bModify) bsz.Add(self.bModify)
self.bModify.SetToolTip("Modify the selected temperature sensor.") self.bModify.SetToolTip("Modify the selected temperature sensor.")
bsz.Add((10, 10)) bsz.Add((10, 10))
self.bDelete = wx.Button(self, wx.ID_ANY, "Delete", size = BSIZESMALL) self.bDelete = wx.Button(self, wx.ID_ANY, "Delete", size=BSIZESMALL)
self.bDelete.SetBackgroundColour(self.deco.getBackgroundColour()) self.bDelete.SetBackgroundColour(self.deco.getBackgroundColour())
self.bDelete.SetFont(font) self.bDelete.SetFont(font)
self.bDelete.Enable(False) self.bDelete.Enable(False)
self.Bind(wx.EVT_BUTTON, self.doDelete, self.bDelete) self.Bind(wx.EVT_BUTTON, self.doDelete, self.bDelete)
bsz.Add(self.bDelete) bsz.Add(self.bDelete)
self.bDelete.SetToolTip("Remove the selected temperature sensor " self.bDelete.SetToolTip(
"from the configuration.") "Remove the selected temperature sensor " "from the configuration."
)
sz.Add(bsz, pos = (1, 3)) sz.Add(bsz, pos=(1, 3))
k = "MCP3008_SELECT_PIN" k = "MCP3008_SELECT_PIN"
tc = self.addPinChoice(k, labelWidth) tc = self.addPinChoice(k, labelWidth)
sz.Add(tc, pos = (2, 1)) sz.Add(tc, pos=(2, 1))
self.SetSizer(sz) self.SetSizer(sz)
self.enableAll(False) self.enableAll(False)
def setItemSelected(self, n): def setItemSelected(self, n):
self.selection = n self.selection = n
if n is None: if n is None:
self.bDelete.Enable(False) self.bDelete.Enable(False)
self.bModify.Enable(False) self.bModify.Enable(False)
else: else:
self.bDelete.Enable(True) self.bDelete.Enable(True)
self.bModify.Enable(True) self.bModify.Enable(True)
def doAdd(self, evt): def doAdd(self, evt):
nm = [] nm = []
for s in self.sensors: for s in self.sensors:
nm.append(s[0]) nm.append(s[0])
dlg = AddSensorDlg(self, nm, self.validPins, self.heatersPage, self.font) dlg = AddSensorDlg(self, nm, self.validPins, self.heatersPage, self.font)
rc = dlg.ShowModal() rc = dlg.ShowModal()
if rc == wx.ID_OK: if rc == wx.ID_OK:
tt = dlg.getValues() tt = dlg.getValues()
dlg.Destroy() dlg.Destroy()
if rc != wx.ID_OK: if rc != wx.ID_OK:
return return
self.sensors.append(tt) self.sensors.append(tt)
self.lb.updateList(self.sensors) self.lb.updateList(self.sensors)
self.validateTable() self.validateTable()
self.assertModified(True) self.assertModified(True)
def doModify(self, evt): def doModify(self, evt):
if self.selection is None: if self.selection is None:
return return
nm = [] nm = []
for s in self.sensors: for s in self.sensors:
nm.append(s[0]) nm.append(s[0])
s = self.sensors[self.selection] s = self.sensors[self.selection]
if s[3] is None: if s[3] is None:
params = [] params = []
else: else:
params = s[3] params = s[3]
dlg = AddSensorDlg(self, nm, self.validPins, self.heatersPage, self.font, dlg = AddSensorDlg(
name = s[0], stype = s[1], pin = s[2], self,
params = params, modify = True) nm,
rc = dlg.ShowModal() self.validPins,
if rc == wx.ID_OK: self.heatersPage,
tt = dlg.getValues() self.font,
name=s[0],
stype=s[1],
pin=s[2],
params=params,
modify=True,
)
rc = dlg.ShowModal()
if rc == wx.ID_OK:
tt = dlg.getValues()
dlg.Destroy() dlg.Destroy()
if rc != wx.ID_OK: if rc != wx.ID_OK:
return return
self.assertModified(True) self.assertModified(True)
self.sensors[self.selection] = tt self.sensors[self.selection] = tt
self.lb.updateList(self.sensors) self.lb.updateList(self.sensors)
self.validateTable() self.validateTable()
self.assertModified(True) self.assertModified(True)
def doDelete(self, evt): def doDelete(self, evt):
if self.selection is None: if self.selection is None:
return return
self.assertModified(True) self.assertModified(True)
del self.sensors[self.selection] del self.sensors[self.selection]
self.lb.updateList(self.sensors) self.lb.updateList(self.sensors)
self.validateTable() self.validateTable()
self.assertModified(True) self.assertModified(True)
def insertValues(self, cfgValues): def insertValues(self, cfgValues):
Page.insertValues(self, cfgValues) Page.insertValues(self, cfgValues)
self.bAdd.Enable(True) self.bAdd.Enable(True)
def setSensors(self, sensors): def setSensors(self, sensors):
self.sensors = sensors self.sensors = sensors
self.lb.updateList(self.sensors) self.lb.updateList(self.sensors)
self.validateTable() self.validateTable()
def setCandidatePins(self, plist): def setCandidatePins(self, plist):
if not plist or len(plist) == 0: if not plist or len(plist) == 0:
self.validPins = pinNames self.validPins = pinNames
else: else:
self.validPins = plist self.validPins = plist
self.validateTable() self.validateTable()
def validateTable(self): def validateTable(self):
self.lb.setTableValidity(True) self.lb.setTableValidity(True)
self.setFieldValidity('SENSORLIST', True) self.setFieldValidity("SENSORLIST", True)
for i in range(len(self.sensors)): for i in range(len(self.sensors)):
if self.sensors[i][2] not in self.validPins: if self.sensors[i][2] not in self.validPins:
self.lb.setRowValidity(i, False) self.lb.setRowValidity(i, False)
self.setFieldValidity('SENSORLIST', False) self.setFieldValidity("SENSORLIST", False)
def setHelpText(self, ht): def setHelpText(self, ht):
Page.setHelpText(self, ht) Page.setHelpText(self, ht)
k = 'DEFINE_TEMP_SENSOR' k = "DEFINE_TEMP_SENSOR"
if k in ht.keys(): if k in ht.keys():
self.bAdd.SetToolTip(ht[k]) self.bAdd.SetToolTip(ht[k])
def getValues(self): def getValues(self):
result = Page.getValues(self) result = Page.getValues(self)
values = {} values = {}
for k in self.sensorTypeKeys.values(): for k in self.sensorTypeKeys.values():
values[k] = False values[k] = False
for s in self.sensors: for s in self.sensors:
values[self.sensorTypeKeys[s[1]]] = True values[self.sensorTypeKeys[s[1]]] = True
for v in values.keys(): for v in values.keys():
result[v] = values[v] result[v] = values[v]
return result return result

View File

@ -9,121 +9,122 @@ DEFAULT_INIFILE = "configtool.default.ini"
class Settings: class Settings:
def __init__(self, app, folder, ini=None): def __init__(self, app, folder, ini=None):
self.app = app self.app = app
self.folder = folder self.folder = folder
self.inifile = os.path.join(folder, INIFILE) self.inifile = os.path.join(folder, INIFILE)
self.section = "configtool" self.section = "configtool"
self.arduinodir = "" self.arduinodir = ""
self.cflags = "" self.cflags = ""
self.ldflags = "" self.ldflags = ""
self.objcopyflags = "" self.objcopyflags = ""
self.programmer = "wiring" self.programmer = "wiring"
self.programflags = "" self.programflags = ""
self.port = "/dev/ttyACM0" self.port = "/dev/ttyACM0"
self.uploadspeed = 38400 self.uploadspeed = 38400
self.t0 = 25; self.t0 = 25
self.r1 = 0; self.r1 = 0
self.numTemps = 25 self.numTemps = 25
self.maxAdc = 1023 self.maxAdc = 1023
self.minAdc = 1 self.minAdc = 1
# Runtime settings # Runtime settings
self.verbose = 0 self.verbose = 0
self.cfg = ConfigParser.ConfigParser() self.cfg = ConfigParser.ConfigParser()
self.cfg.optionxform = str self.cfg.optionxform = str
self.loaded = self.readConfig(ini) self.loaded = self.readConfig(ini)
def readConfig(self, ini): def readConfig(self, ini):
if ini: if ini:
if not self.cfg.read(ini): if not self.cfg.read(ini):
return False return False
else:
if not self.cfg.read(self.inifile):
if not self.cfg.read(os.path.join(self.folder, DEFAULT_INIFILE)):
print ("Neither of settings files %s or %s exist. Using default values."
% (INIFILE, DEFAULT_INIFILE))
return False
if self.cfg.has_section(self.section):
for opt, value in self.cfg.items(self.section):
value = value.replace('\n', ' ')
if opt == "arduinodir":
self.arduinodir = value
elif opt == "cflags":
self.cflags = value
elif opt == "ldflags":
self.ldflags = value
elif opt == "programmer":
self.programmer = value
elif opt == "port":
self.port = value
elif opt == "objcopyflags":
self.objcopyflags = value
elif opt == "programflags":
self.programflags = value
elif opt == "t0":
self.t0 = value
elif opt == "r1":
self.r1 = value
elif opt == "numtemps":
self.numTemps = value
elif opt == "maxadc":
self.maxAdc = value
elif opt == "minadc":
self.minAdc = value
elif opt == "uploadspeed":
self.uploadspeed = value
else: else:
print("Unknown %s option: %s - ignoring." % (self.section, opt)) if not self.cfg.read(self.inifile):
else: if not self.cfg.read(os.path.join(self.folder, DEFAULT_INIFILE)):
print("Missing %s section - assuming defaults." % self.section) print(
return False "Neither of settings files %s or %s exist. Using default values."
% (INIFILE, DEFAULT_INIFILE)
)
return False
return True if self.cfg.has_section(self.section):
for opt, value in self.cfg.items(self.section):
value = value.replace("\n", " ")
if opt == "arduinodir":
self.arduinodir = value
elif opt == "cflags":
self.cflags = value
elif opt == "ldflags":
self.ldflags = value
elif opt == "programmer":
self.programmer = value
elif opt == "port":
self.port = value
elif opt == "objcopyflags":
self.objcopyflags = value
elif opt == "programflags":
self.programflags = value
elif opt == "t0":
self.t0 = value
elif opt == "r1":
self.r1 = value
elif opt == "numtemps":
self.numTemps = value
elif opt == "maxadc":
self.maxAdc = value
elif opt == "minadc":
self.minAdc = value
elif opt == "uploadspeed":
self.uploadspeed = value
else:
print("Unknown %s option: %s - ignoring." % (self.section, opt))
else:
print("Missing %s section - assuming defaults." % self.section)
return False
def getValues(self): return True
return {
"arduinodir": str(self.arduinodir),
"cflags": str(self.cflags),
"ldflags": str(self.ldflags),
"objcopyflags": str(self.objcopyflags),
"programmer": str(self.programmer),
"port": str(self.port),
"t0": str(self.t0),
"r1": str(self.r1),
"numtemps": str(self.numTemps),
"maxadc": str(self.maxAdc),
"minadc": str(self.minAdc),
"uploadspeed": str(self.uploadspeed)
}
def saveSettings(self, inifile = None): def getValues(self):
if not inifile: return {
inifile = self.inifile "arduinodir": str(self.arduinodir),
"cflags": str(self.cflags),
"ldflags": str(self.ldflags),
"objcopyflags": str(self.objcopyflags),
"programmer": str(self.programmer),
"port": str(self.port),
"t0": str(self.t0),
"r1": str(self.r1),
"numtemps": str(self.numTemps),
"maxadc": str(self.maxAdc),
"minadc": str(self.minAdc),
"uploadspeed": str(self.uploadspeed),
}
self.section = "configtool" def saveSettings(self, inifile=None):
try: if not inifile:
self.cfg.add_section(self.section) inifile = self.inifile
except ConfigParser.DuplicateSectionError:
pass
values = self.getValues() self.section = "configtool"
for k in values.keys(): try:
self.cfg.set(self.section, k, values[k]) self.cfg.add_section(self.section)
except ConfigParser.DuplicateSectionError:
pass
try: values = self.getValues()
cfp = open(inifile, 'wb') for k in values.keys():
except: self.cfg.set(self.section, k, values[k])
print("Unable to open settings file %s for writing." % inifile)
return False
self.cfg.write(cfp) try:
cfp.close() cfp = open(inifile, "wb")
except:
print("Unable to open settings file %s for writing." % inifile)
return False
return True self.cfg.write(cfp)
cfp.close()
return True

View File

@ -1,11 +1,10 @@
import wx import wx
from configtool.data import BSIZESMALL, offsetTcLabel from configtool.data import BSIZESMALL, offsetTcLabel
ARDUINODIR = 0 ARDUINODIR = 0
CFLAGS = 1 CFLAGS = 1
LDFLAGS = 2 LDFLAGS = 2
OBJCOPYFLAGS= 3 OBJCOPYFLAGS = 3
PROGRAMMER = 4 PROGRAMMER = 4
PROGRAMFLAGS = 5 PROGRAMFLAGS = 5
PORT = 6 PORT = 6
@ -18,168 +17,183 @@ R1 = 12
class SettingsDlg(wx.Dialog): class SettingsDlg(wx.Dialog):
def __init__(self, parent, settings): def __init__(self, parent, settings):
wx.Dialog.__init__(self, parent, wx.ID_ANY, "Modify settings", wx.Dialog.__init__(self, parent, wx.ID_ANY, "Modify settings", size=(500, 300))
size = (500, 300)) self.SetFont(settings.font)
self.SetFont(settings.font) self.settings = settings
self.settings = settings
self.modified = False self.modified = False
self.Bind(wx.EVT_CLOSE, self.onExit) self.Bind(wx.EVT_CLOSE, self.onExit)
htArdDir = "Path to the Arduino IDE folder. Configtool will figure the " \ htArdDir = (
"details on where to find avr-gcc and avrdude inside there." \ "Path to the Arduino IDE folder. Configtool will figure the "
"\n\nIf empty, the system wide installed tools will be used." "details on where to find avr-gcc and avrdude inside there."
htCFlags = "Flags passed into the avr-gcc compiler. These flags can " \ "\n\nIf empty, the system wide installed tools will be used."
"have 3 different variables embedded within them:" \ )
"\n\n %F_CPU% will be replaced by the value of the CPU " \ htCFlags = (
"Clock Rate." \ "Flags passed into the avr-gcc compiler. These flags can "
"\n\n %CPU% will be replaced by the value of the CPU. " \ "have 3 different variables embedded within them:"
"\n\n %ALNAME% is the name of the source file being " \ "\n\n %F_CPU% will be replaced by the value of the CPU "
"compiled with the .c extension replaced by .al.\n\n" \ "Clock Rate."
"Note: the flag -save-temps=obj does not appear to be a " \ "\n\n %CPU% will be replaced by the value of the CPU. "
"valid flag for some compiler versions. Omit the \"=obj\", " \ "\n\n %ALNAME% is the name of the source file being "
"omit the flag entirely, or simply ignore the related warnings." "compiled with the .c extension replaced by .al.\n\n"
htLDFlags = "Flags passed to avr-gcc to be passed on to the linker." "Note: the flag -save-temps=obj does not appear to be a "
htObjCopy = "Flags passed to avr-objcopy." 'valid flag for some compiler versions. Omit the "=obj", '
htProgrammer = "The programmer type - passed to avrdude." "omit the flag entirely, or simply ignore the related warnings."
htProgramFlags = "Extra flags passed to avrdude." )
htPort = "The port to which the controller is connected. Typically a " \ htLDFlags = "Flags passed to avr-gcc to be passed on to the linker."
"path starting with /dev/... on Linux or Mac OS X, or some " \ htObjCopy = "Flags passed to avr-objcopy."
"COM... on Windows." htProgrammer = "The programmer type - passed to avrdude."
htSpeed = "The baud rate with which to communicate with the bootloader." htProgramFlags = "Extra flags passed to avrdude."
htNumTemps = "The number of entries generated for the thermistor tables. " \ htPort = (
"Higher numbers slightly increase temperature reading " \ "The port to which the controller is connected. Typically a "
"accuracy, but also cost binary size. Default is 25." "path starting with /dev/... on Linux or Mac OS X, or some "
htMinAdc = "The minimum ADC value returned by the thermistor. Typically 0." "COM... on Windows."
htMaxAdc = "The maximum ADC value returned by the thermistor. " \ )
"Typically 1023 (maximum of 10-bit ADCs)." htSpeed = "The baud rate with which to communicate with the bootloader."
htT0 = "The T0 value used for thermistor table calculation. Typically 25." htNumTemps = (
htR1 = "The R1 value used for thermistor table calculation. Typically 0." "The number of entries generated for the thermistor tables. "
"Higher numbers slightly increase temperature reading "
"accuracy, but also cost binary size. Default is 25."
)
htMinAdc = "The minimum ADC value returned by the thermistor. Typically 0."
htMaxAdc = (
"The maximum ADC value returned by the thermistor. "
"Typically 1023 (maximum of 10-bit ADCs)."
)
htT0 = "The T0 value used for thermistor table calculation. Typically 25."
htR1 = "The R1 value used for thermistor table calculation. Typically 0."
# This table MUST be in the same order as the constants defined at # This table MUST be in the same order as the constants defined at
# the top of this file. # the top of this file.
self.fields = [["Arduino Directory", settings.arduinodir, htArdDir], self.fields = [
["C Compiler Flags", settings.cflags, htCFlags], ["Arduino Directory", settings.arduinodir, htArdDir],
["LD Flags", settings.ldflags, htLDFlags], ["C Compiler Flags", settings.cflags, htCFlags],
["Object Copy Flags", settings.objcopyflags, htObjCopy], ["LD Flags", settings.ldflags, htLDFlags],
["AVR Programmer", settings.programmer, htProgrammer], ["Object Copy Flags", settings.objcopyflags, htObjCopy],
["AVR Upload Flags", settings.programflags, htProgramFlags], ["AVR Programmer", settings.programmer, htProgrammer],
["Port", settings.port, htPort], ["AVR Upload Flags", settings.programflags, htProgramFlags],
["Upload Speed", settings.uploadspeed, htSpeed], ["Port", settings.port, htPort],
["Number of Temps", settings.numTemps, htNumTemps], ["Upload Speed", settings.uploadspeed, htSpeed],
["Minimum ADC value", settings.minAdc, htMinAdc], ["Number of Temps", settings.numTemps, htNumTemps],
["Maximum ADC value", settings.maxAdc, htMaxAdc], ["Minimum ADC value", settings.minAdc, htMinAdc],
["T0", settings.t0, htT0], ["Maximum ADC value", settings.maxAdc, htMaxAdc],
["R1", settings.r1, htR1]] ["T0", settings.t0, htT0],
["R1", settings.r1, htR1],
]
self.teList = [] self.teList = []
hsz = wx.BoxSizer(wx.HORIZONTAL) hsz = wx.BoxSizer(wx.HORIZONTAL)
hsz.Add((10, 10)) hsz.Add((10, 10))
sz = wx.BoxSizer(wx.VERTICAL) sz = wx.BoxSizer(wx.VERTICAL)
sz.Add((10, 10)) sz.Add((10, 10))
labelWidth = 140 labelWidth = 140
for f in self.fields: for f in self.fields:
lsz = wx.BoxSizer(wx.HORIZONTAL) lsz = wx.BoxSizer(wx.HORIZONTAL)
t = wx.StaticText(self, wx.ID_ANY, f[0], size = (labelWidth, -1), t = wx.StaticText(
style = wx.ALIGN_RIGHT) self, wx.ID_ANY, f[0], size=(labelWidth, -1), style=wx.ALIGN_RIGHT
t.SetFont(settings.font) )
lsz.Add(t, 1, wx.TOP, offsetTcLabel) t.SetFont(settings.font)
lsz.Add(t, 1, wx.TOP, offsetTcLabel)
lsz.Add((8, 8)) lsz.Add((8, 8))
te = wx.TextCtrl(self, wx.ID_ANY, f[1], size = (600, -1)) te = wx.TextCtrl(self, wx.ID_ANY, f[1], size=(600, -1))
te.Bind(wx.EVT_TEXT, self.onTextCtrl) te.Bind(wx.EVT_TEXT, self.onTextCtrl)
te.SetToolTip(f[2]) te.SetToolTip(f[2])
lsz.Add(te) lsz.Add(te)
self.teList.append(te) self.teList.append(te)
sz.Add(lsz) sz.Add(lsz)
sz.Add((10, 10)) sz.Add((10, 10))
sz.Add((20, 20)) sz.Add((20, 20))
bsz = wx.BoxSizer(wx.HORIZONTAL) bsz = wx.BoxSizer(wx.HORIZONTAL)
b = wx.Button(self, wx.ID_ANY, "Save", size = BSIZESMALL) b = wx.Button(self, wx.ID_ANY, "Save", size=BSIZESMALL)
b.SetFont(settings.font) b.SetFont(settings.font)
self.Bind(wx.EVT_BUTTON, self.onSave, b) self.Bind(wx.EVT_BUTTON, self.onSave, b)
bsz.Add(b) bsz.Add(b)
self.bSave = b self.bSave = b
bsz.Add((5, 5)) bsz.Add((5, 5))
b = wx.Button(self, wx.ID_ANY, "Exit", size = BSIZESMALL) b = wx.Button(self, wx.ID_ANY, "Exit", size=BSIZESMALL)
b.SetFont(settings.font) b.SetFont(settings.font)
self.Bind(wx.EVT_BUTTON, self.onExit, b) self.Bind(wx.EVT_BUTTON, self.onExit, b)
bsz.Add(b) bsz.Add(b)
self.bExit = b self.bExit = b
sz.Add(bsz, 1, wx.ALIGN_CENTER_HORIZONTAL) sz.Add(bsz, 1, wx.ALIGN_CENTER_HORIZONTAL)
sz.Add((10, 10)) sz.Add((10, 10))
hsz.Add(sz) hsz.Add(sz)
hsz.Add((10, 10)) hsz.Add((10, 10))
self.SetSizer(hsz) self.SetSizer(hsz)
self.setModified(False) self.setModified(False)
self.Fit() self.Fit()
def setModified(self, flag): def setModified(self, flag):
self.modified = flag self.modified = flag
if flag: if flag:
self.bSave.Enable(True) self.bSave.Enable(True)
self.bExit.SetLabel("Cancel") self.bExit.SetLabel("Cancel")
else: else:
self.bSave.Enable(False) self.bSave.Enable(False)
self.bExit.SetLabel("Exit") self.bExit.SetLabel("Exit")
def onTextCtrl(self, evt): def onTextCtrl(self, evt):
self.setModified(True) self.setModified(True)
evt.Skip() evt.Skip()
def onSave(self, evt): def onSave(self, evt):
self.saveValues() self.saveValues()
self.EndModal(wx.ID_OK) self.EndModal(wx.ID_OK)
def saveValues(self): def saveValues(self):
self.settings.arduinodir = self.teList[ARDUINODIR].GetValue() self.settings.arduinodir = self.teList[ARDUINODIR].GetValue()
self.settings.cflags = self.teList[CFLAGS].GetValue() self.settings.cflags = self.teList[CFLAGS].GetValue()
self.settings.ldflags = self.teList[LDFLAGS].GetValue() self.settings.ldflags = self.teList[LDFLAGS].GetValue()
self.settings.objcopyflags = self.teList[OBJCOPYFLAGS].GetValue() self.settings.objcopyflags = self.teList[OBJCOPYFLAGS].GetValue()
self.settings.programmer = self.teList[PROGRAMMER].GetValue() self.settings.programmer = self.teList[PROGRAMMER].GetValue()
self.settings.programflags = self.teList[PROGRAMFLAGS].GetValue() self.settings.programflags = self.teList[PROGRAMFLAGS].GetValue()
self.settings.port = self.teList[PORT].GetValue() self.settings.port = self.teList[PORT].GetValue()
self.settings.uploadspeed = self.teList[UPLOADSPEED].GetValue() self.settings.uploadspeed = self.teList[UPLOADSPEED].GetValue()
self.settings.numTemps = self.teList[NUMTEMPS].GetValue() self.settings.numTemps = self.teList[NUMTEMPS].GetValue()
self.settings.minAdc = self.teList[MINADC].GetValue() self.settings.minAdc = self.teList[MINADC].GetValue()
self.settings.maxAdc = self.teList[MAXADC].GetValue() self.settings.maxAdc = self.teList[MAXADC].GetValue()
self.settings.t0 = self.teList[T0].GetValue() self.settings.t0 = self.teList[T0].GetValue()
self.settings.r1 = self.teList[R1].GetValue() self.settings.r1 = self.teList[R1].GetValue()
self.settings.saveSettings() self.settings.saveSettings()
def onExit(self, evt): def onExit(self, evt):
if not self.confirmLoseChanges("exit"): if not self.confirmLoseChanges("exit"):
return return
self.EndModal(wx.ID_EXIT) self.EndModal(wx.ID_EXIT)
def confirmLoseChanges(self, msg): def confirmLoseChanges(self, msg):
if not self.modified: if not self.modified:
return True return True
dlg = wx.MessageDialog(self, "Are you sure you want to " + msg + "?\n" dlg = wx.MessageDialog(
"There are changes to your settings that " self,
"will be lost.", "Are you sure you want to " + msg + "?\n"
"Changes pending", "There are changes to your settings that "
wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION) "will be lost.",
rc = dlg.ShowModal() "Changes pending",
dlg.Destroy() wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION,
)
rc = dlg.ShowModal()
dlg.Destroy()
if rc != wx.ID_YES: if rc != wx.ID_YES:
return False return False
return True return True

View File

@ -5,95 +5,100 @@ import sys
class SHThermistor: class SHThermistor:
def __init__(self, rp, t0, r0, t1, r1, t2, r2): def __init__(self, rp, t0, r0, t1, r1, t2, r2):
self.rp = rp self.rp = rp
self.paramsOK = True self.paramsOK = True
try: try:
T0 = t0 + 273.15; T1 = t1 + 273.15; T2 = t2 + 273.15 T0 = t0 + 273.15
a0 = log(r0); a1 = log(r1); a2 = log(r2) T1 = t1 + 273.15
z = a0 - a1 T2 = t2 + 273.15
y = a0 - a2 a0 = log(r0)
x = 1 / T0 - 1 / T1 a1 = log(r1)
w = 1 / T0 - 1 / T2 a2 = log(r2)
v = a0 ** 3 - a1 ** 3 z = a0 - a1
u = a0 ** 3 - a2 ** 3 y = a0 - a2
x = 1 / T0 - 1 / T1
w = 1 / T0 - 1 / T2
v = a0 ** 3 - a1 ** 3
u = a0 ** 3 - a2 ** 3
self.C = (x - z * w / y) / (v - z * u / y) self.C = (x - z * w / y) / (v - z * u / y)
self.B = (x - self.C * v) / z self.B = (x - self.C * v) / z
self.A = 1 / T0 - self.C * a0 ** 3 - self.B * a0 self.A = 1 / T0 - self.C * a0 ** 3 - self.B * a0
except: except:
self.paramsOK = False self.paramsOK = False
def setting(self, t): def setting(self, t):
if not self.paramsOK: if not self.paramsOK:
return None, None return None, None
try: try:
T = t + 273.15 T = t + 273.15
y = (self.A - 1/T) / self.C y = (self.A - 1 / T) / self.C
x = ((self.B / (3 * self.C)) ** 3 + (y ** 2) / 4) ** 0.5 x = ((self.B / (3 * self.C)) ** 3 + (y ** 2) / 4) ** 0.5
r = exp((x - y / 2) ** (1.0/3) - (x + y / 2) ** (1.0/3)) r = exp((x - y / 2) ** (1.0 / 3) - (x + y / 2) ** (1.0 / 3))
return self.adc(r), r return self.adc(r), r
except: except:
return None, None return None, None
def temp(self, adc): def temp(self, adc):
r = self.adcInv(adc) r = self.adcInv(adc)
t = (1.0 / (self.A + self.B * log(r) + self.C * (log(r) ** 3))) - 273.15; t = (1.0 / (self.A + self.B * log(r) + self.C * (log(r) ** 3))) - 273.15
return t return t
def adc(self, r): def adc(self, r):
return 1023.0 * r / (r + self.rp) return 1023.0 * r / (r + self.rp)
def adcInv(self, adc):
return (self.rp * adc) / (1023.0 - adc)
def adcInv(self, adc):
return (self.rp * adc)/(1023.0 - adc)
class BetaThermistor: class BetaThermistor:
def __init__(self, r0, t0, beta, r1, r2, vadc): def __init__(self, r0, t0, beta, r1, r2, vadc):
self.paramsOK = True self.paramsOK = True
try: try:
self.r0 = r0 self.r0 = r0
self.t0 = t0 + 273.15 self.t0 = t0 + 273.15
self.beta = beta self.beta = beta
self.vadc = vadc self.vadc = vadc
self.k = r0 * exp(-beta / self.t0) self.k = r0 * exp(-beta / self.t0)
if r1 > 0: if r1 > 0:
self.vs = r1 * self.vadc / (r1 + r2) self.vs = r1 * self.vadc / (r1 + r2)
self.rs = r1 * r2 / (r1 + r2) self.rs = r1 * r2 / (r1 + r2)
else: else:
self.vs = self.vadc self.vs = self.vadc
self.rs = r2 self.rs = r2
except: except:
self.paramsOK = False self.paramsOK = False
def temp(self, adc): def temp(self, adc):
v = adc * self.vadc / 1024 v = adc * self.vadc / 1024
if (self.vs - v): if self.vs - v:
r = self.rs * v / (self.vs - v) r = self.rs * v / (self.vs - v)
else: else:
r = self.r0 * 10 r = self.r0 * 10
try: try:
return (self.beta / log(r / self.k)) - 273.15 return (self.beta / log(r / self.k)) - 273.15
except: except:
print("// error for ADC = {adc}, {v}, {r}".format(adc = adc, v = v, r = r)) print("// error for ADC = {adc}, {v}, {r}".format(adc=adc, v=v, r=r))
return None return None
def resistance(self, t): def resistance(self, t):
return self.r0 * exp(self.beta * (1 / (t + 273.15) - 1 / self.t0)) return self.r0 * exp(self.beta * (1 / (t + 273.15) - 1 / self.t0))
def setting(self, t): def setting(self, t):
if not self.paramsOK: if not self.paramsOK:
return None, None return None, None
try: try:
r = self.r0 * exp(self.beta * (1 / (t + 273.15) - 1 / self.t0)) r = self.r0 * exp(self.beta * (1 / (t + 273.15) - 1 / self.t0))
v = self.vs * r / (self.rs + r) v = self.vs * r / (self.rs + r)
return round(v / self.vadc * 1024), r return round(v / self.vadc * 1024), r
except: except:
return None, None return None, None
def adcInv(self, adc): def adcInv(self, adc):
return (adc * self.vadc)/1024.0 return (adc * self.vadc) / 1024.0

View File

@ -1,4 +1,3 @@
# Define thermistor presets. These can either be defined to use the # Define thermistor presets. These can either be defined to use the
# beta argorithm by specifying 4 parameters: # beta argorithm by specifying 4 parameters:
# R0, beta, Rp, Vadc # R0, beta, Rp, Vadc
@ -7,12 +6,12 @@
# Rp, T0, R0, T1, R1, T2, R2 # Rp, T0, R0, T1, R1, T2, R2
# #
thermistorPresets = { thermistorPresets = {
"RS 10K": ['10000', '3480', '1600', '5.0'], "RS 10K": ["10000", "3480", "1600", "5.0"],
"RRRF 10K": ['10000', '3964', '1600', '5.0'], "RRRF 10K": ["10000", "3964", "1600", "5.0"],
"ATC Semitec 104GT-2": ['100000', '4267', '4700', '5.0'], "ATC Semitec 104GT-2": ["100000", "4267", "4700", "5.0"],
"EPCOS 100K (B57560G1104F)": ['100000', '4092', '4700', '5.0'], "EPCOS 100K (B57560G1104F)": ["100000", "4092", "4700", "5.0"],
"EPCOS 100K (B5754061104)": ['100000', '4066', '4700', '5.0'], "EPCOS 100K (B5754061104)": ["100000", "4066", "4700", "5.0"],
"EPCOS 100K (B57560G104F)": ['100000', '4036', '4700', '5.0'], "EPCOS 100K (B57560G104F)": ["100000", "4036", "4700", "5.0"],
"Honeywell 100K": ['100000', '3974', '4700', '5.0'], "Honeywell 100K": ["100000", "3974", "4700", "5.0"],
"RRRF 100K": ['100000', '3960', '4700', '5.0'], "RRRF 100K": ["100000", "3960", "4700", "5.0"],
} }

View File

@ -5,233 +5,275 @@ from .thermistor import SHThermistor, BetaThermistor
class ThermistorTableFile: class ThermistorTableFile:
def __init__(self, folder): def __init__(self, folder):
self.error = False self.error = False
fn = os.path.join(folder, "thermistortable.h") fn = os.path.join(folder, "thermistortable.h")
try: try:
self.fp = open(fn, 'wb') self.fp = open(fn, "wb")
except: except:
self.error = True self.error = True
def close(self): def close(self):
self.fp.close() self.fp.close()
def output(self, text):
self.fp.write(text + "\n")
def output(self, text):
self.fp.write(text + "\n")
def paramsEqual(p1, p2): def paramsEqual(p1, p2):
for i in range(len(p1)): for i in range(len(p1)):
if p1[i] != p2[i]: if p1[i] != p2[i]:
return False return False
return True
def generateTempTables(sensors, settings):
ofp = ThermistorTableFile(settings.folder)
if ofp.error:
return False
N = int(settings.numTemps)
tl = []
for sensor in sensors:
if sensor[3] is not None:
found = False
for t in tl:
if paramsEqual(t[0], sensor[3]):
t[1].append(sensor[0].upper())
found = True
if not found:
tl.append((sensor[3], [sensor[0].upper()]))
ofp.output("");
ofp.output("/**");
ofp.output(" This file was autogenerated when saving a board with");
ofp.output(" Teacup's Configtool. You can edit it, but the next board");
ofp.output(" save operation in Configtool will overwrite it without");
ofp.output(" asking.");
ofp.output("*/");
ofp.output("");
ofp.output("#define NUMTABLES %d" % len(tl))
ofp.output("#define NUMTEMPS %d" % N)
ofp.output("");
for i in range(len(tl)):
for n in tl[i][1]:
ofp.output("#define THERMISTOR_%s %d" % (n, i))
ofp.output("");
if len(tl) == 0 or N == 0:
ofp.close();
return True return True
ofp.output("const uint16_t PROGMEM temptable[NUMTABLES][NUMTEMPS][3] = {")
tcount = 0 def generateTempTables(sensors, settings):
for tn in tl: ofp = ThermistorTableFile(settings.folder)
tcount += 1 if ofp.error:
finalTable = tcount == len(tl) return False
if len(tn[0]) == 4:
BetaTable(ofp, tn[0], tn[1], settings, finalTable) N = int(settings.numTemps)
elif len(tn[0]) == 7:
SteinhartHartTable(ofp, tn[0], tn[1], settings, finalTable) tl = []
else: for sensor in sensors:
pass if sensor[3] is not None:
found = False
for t in tl:
if paramsEqual(t[0], sensor[3]):
t[1].append(sensor[0].upper())
found = True
if not found:
tl.append((sensor[3], [sensor[0].upper()]))
ofp.output("")
ofp.output("/**")
ofp.output(" This file was autogenerated when saving a board with")
ofp.output(" Teacup's Configtool. You can edit it, but the next board")
ofp.output(" save operation in Configtool will overwrite it without")
ofp.output(" asking.")
ofp.output("*/")
ofp.output("")
ofp.output("#define NUMTABLES %d" % len(tl))
ofp.output("#define NUMTEMPS %d" % N)
ofp.output("")
for i in range(len(tl)):
for n in tl[i][1]:
ofp.output("#define THERMISTOR_%s %d" % (n, i))
ofp.output("")
if len(tl) == 0 or N == 0:
ofp.close()
return True
ofp.output("const uint16_t PROGMEM temptable[NUMTABLES][NUMTEMPS][3] = {")
tcount = 0
for tn in tl:
tcount += 1
finalTable = tcount == len(tl)
if len(tn[0]) == 4:
BetaTable(ofp, tn[0], tn[1], settings, finalTable)
elif len(tn[0]) == 7:
SteinhartHartTable(ofp, tn[0], tn[1], settings, finalTable)
else:
pass
ofp.output("};")
ofp.close()
return True
ofp.output("};")
ofp.close()
return True
def BetaTable(ofp, params, names, settings, finalTable): def BetaTable(ofp, params, names, settings, finalTable):
r0 = params[0] r0 = params[0]
beta = params[1] beta = params[1]
r2 = params[2] r2 = params[2]
vadc = float(params[3]) vadc = float(params[3])
ofp.output(" // %s temp table using Beta algorithm with parameters:" % ofp.output(
(", ".join(names))) " // %s temp table using Beta algorithm with parameters:" % (", ".join(names))
ofp.output((" // R0 = %s, T0 = %s, R1 = %s, R2 = %s, beta = %s, " )
"maxadc = %s") % (r0, settings.t0, settings.r1, r2, ofp.output(
beta, settings.maxAdc)) (" // R0 = %s, T0 = %s, R1 = %s, R2 = %s, beta = %s, " "maxadc = %s")
ofp.output(" {") % (r0, settings.t0, settings.r1, r2, beta, settings.maxAdc)
)
ofp.output(" {")
thrm = BetaThermistor(int(r0), int(settings.t0), int(beta), int(settings.r1), thrm = BetaThermistor(
int(r2), vadc) int(r0), int(settings.t0), int(beta), int(settings.r1), int(r2), vadc
)
hiadc = thrm.setting(0)[0] hiadc = thrm.setting(0)[0]
N = int(settings.numTemps) N = int(settings.numTemps)
samples = optimizeTempTable(thrm, N, hiadc) samples = optimizeTempTable(thrm, N, hiadc)
prev = samples[0] prev = samples[0]
for i in samples: for i in samples:
t = thrm.temp(i) t = thrm.temp(i)
if t is None: if t is None:
ofp.output("// ERROR CALCULATING THERMISTOR VALUES AT ADC %d" % i) ofp.output("// ERROR CALCULATING THERMISTOR VALUES AT ADC %d" % i)
continue continue
v = thrm.adcInv(i) v = thrm.adcInv(i)
r = thrm.resistance(t) r = thrm.resistance(t)
vTherm = i * vadc / 1024 vTherm = i * vadc / 1024
ptherm = vTherm * vTherm / r ptherm = vTherm * vTherm / r
if i == max(samples): if i == max(samples):
c = " " c = " "
else:
c = ","
delta = (t - thrm.temp(prev)) / (prev - i) if i != prev else 0
ostr = (
" {%4s, %5s, %5s}%s // %4d C, %6.0f ohms, %0.3f V,"
" %0.2f mW, m = %6.3f"
) % (
i,
int(t * 4),
int(delta * 4 * 256),
c,
int(t),
int(round(r)),
vTherm,
ptherm * 1000,
delta,
)
ofp.output(ostr)
prev = i
if finalTable:
ofp.output(" }")
else: else:
c = "," ofp.output(" },")
delta = (t - thrm.temp(prev)) / (prev - i) if i != prev else 0
ostr = (" {%4s, %5s, %5s}%s // %4d C, %6.0f ohms, %0.3f V,"
" %0.2f mW, m = %6.3f") % (i, int(t * 4), int(delta * 4 * 256), c,
int(t), int(round(r)), vTherm, ptherm * 1000, delta)
ofp.output(ostr)
prev = i
if finalTable:
ofp.output(" }")
else:
ofp.output(" },")
def SteinhartHartTable(ofp, params, names, settings, finalTable): def SteinhartHartTable(ofp, params, names, settings, finalTable):
ofp.output((" // %s temp table using Steinhart-Hart algorithm with " ofp.output(
"parameters:") % (", ".join(names))) (" // %s temp table using Steinhart-Hart algorithm with " "parameters:")
ofp.output((" // Rp = %s, T0 = %s, R0 = %s, T1 = %s, R1 = %s, " % (", ".join(names))
"T2 = %s, R2 = %s") % )
(params[0], params[1], params[2], params[3], params[4], params[5], ofp.output(
params[6])) (" // Rp = %s, T0 = %s, R0 = %s, T1 = %s, R1 = %s, " "T2 = %s, R2 = %s")
ofp.output(" {") % (params[0], params[1], params[2], params[3], params[4], params[5], params[6])
)
ofp.output(" {")
thrm = SHThermistor(int(params[0]), float(params[1]), int(params[2]), thrm = SHThermistor(
float(params[3]), int(params[4]), float(params[5]), int(params[0]),
int(params[6])) float(params[1]),
int(params[2]),
float(params[3]),
int(params[4]),
float(params[5]),
int(params[6]),
)
hiadc = thrm.setting(0)[0] hiadc = thrm.setting(0)[0]
N = int(settings.numTemps) N = int(settings.numTemps)
samples = optimizeTempTable(thrm, N, hiadc) samples = optimizeTempTable(thrm, N, hiadc)
prev = samples[0] prev = samples[0]
for i in samples: for i in samples:
t = thrm.temp(i) t = thrm.temp(i)
if t is None: if t is None:
ofp.output("// ERROR CALCULATING THERMISTOR VALUES AT ADC %d" % i) ofp.output("// ERROR CALCULATING THERMISTOR VALUES AT ADC %d" % i)
continue continue
r = int(thrm.adcInv(i)) r = int(thrm.adcInv(i))
if i == max(samples): if i == max(samples):
c = " " c = " "
else:
c = ","
delta = (t - thrm.temp(prev)) / (prev - i) if i != prev else 0
ofp.output(
" {%4d, %5d, %5d}%s // %4d C, %6d ohms, m = %6.3f"
% (i, int(t * 4), int(delta * 4 * 256), c, int(t), int(round(r)), delta)
)
prev = i
if finalTable:
ofp.output(" }")
else: else:
c = "," ofp.output(" },")
delta = (t - thrm.temp(prev)) / (prev - i) if i != prev else 0
ofp.output(" {%4d, %5d, %5d}%s // %4d C, %6d ohms, m = %6.3f" %
(i, int(t * 4), int(delta * 4 * 256), c, int(t), int(round(r)),
delta))
prev = i
if finalTable:
ofp.output(" }")
else:
ofp.output(" },")
def optimizeTempTable(thrm, length, hiadc): def optimizeTempTable(thrm, length, hiadc):
# This is a variation of the Ramer-Douglas-Peucker algorithm, see # This is a variation of the Ramer-Douglas-Peucker algorithm, see
# https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm # https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm
# #
# It works like this: # It works like this:
# #
# - Calculate all (1024) ideal values. # - Calculate all (1024) ideal values.
# - Keep only the ones in the interesting range (0..500C). # - Keep only the ones in the interesting range (0..500C).
# - Insert the two extremes into our sample list. # - Insert the two extremes into our sample list.
# - Calculate the linear approximation of the remaining values. # - Calculate the linear approximation of the remaining values.
# - Insert the correct value for the "most-wrong" estimation into our # - Insert the correct value for the "most-wrong" estimation into our
# sample list. # sample list.
# - Repeat until "N" values are chosen as requested. # - Repeat until "N" values are chosen as requested.
# Calculate actual temps for all ADC values. # Calculate actual temps for all ADC values.
actual = dict([(x, thrm.temp(1.0 * x)) for x in range(1, int(hiadc + 1))]) actual = dict([(x, thrm.temp(1.0 * x)) for x in range(1, int(hiadc + 1))])
# Limit ADC range to 0C to 500C. # Limit ADC range to 0C to 500C.
MIN_TEMP = 0 MIN_TEMP = 0
MAX_TEMP = 500 MAX_TEMP = 500
actual = dict([(adc, actual[adc]) for adc in actual actual = dict(
if actual[adc] <= MAX_TEMP and actual[adc] >= MIN_TEMP]) [
(adc, actual[adc])
for adc in actual
if actual[adc] <= MAX_TEMP and actual[adc] >= MIN_TEMP
]
)
# Build a lookup table starting with the extremes. # Build a lookup table starting with the extremes.
A = min(actual) A = min(actual)
B = max(actual) B = max(actual)
lookup = dict([(x, actual[x]) for x in [A, B]]) lookup = dict([(x, actual[x]) for x in [A, B]])
error = dict({}) error = dict({})
while len(lookup) < length: while len(lookup) < length:
error.update(dict([(x, abs(actual[x] - LinearTableEstimate(lookup, x))) error.update(
for x in range(A + 1, B)])) dict(
[
(x, abs(actual[x] - LinearTableEstimate(lookup, x)))
for x in range(A + 1, B)
]
)
)
# Correct the most-wrong lookup value. # Correct the most-wrong lookup value.
next = max(error, key = error.get) next = max(error, key=error.get)
lookup[next] = actual[next] lookup[next] = actual[next]
# Prepare to update the error range. # Prepare to update the error range.
A = before(lookup, next) A = before(lookup, next)
B = after(lookup, next) B = after(lookup, next)
return sorted(lookup)
return sorted(lookup)
def after(lookup, value): def after(lookup, value):
return min([x for x in lookup.keys() if x > value]) return min([x for x in lookup.keys() if x > value])
def before(lookup, value): def before(lookup, value):
return max([x for x in lookup.keys() if x < value]) return max([x for x in lookup.keys() if x < value])
def LinearTableEstimate(lookup, value): def LinearTableEstimate(lookup, value):
if value in lookup: if value in lookup:
return lookup[value] return lookup[value]
# Estimate result with linear estimation algorithm. # Estimate result with linear estimation algorithm.
x0 = before(lookup, value) x0 = before(lookup, value)
x1 = after(lookup, value) x1 = after(lookup, value)
y0 = lookup[x0] y0 = lookup[x0]
y1 = lookup[x1] y1 = lookup[x1]
return ((value - x0) * y1 + (x1 - value) * y0) / (x1 - x0) return ((value - x0) * y1 + (x1 - value) * y0) / (x1 - x0)

View File

@ -5,14 +5,14 @@
# on a microcontroller # on a microcontroller
# based on: http://hydraraptor.blogspot.com/2007/10/measuring-temperature-easy-way.html # based on: http://hydraraptor.blogspot.com/2007/10/measuring-temperature-easy-way.html
# Modified Thu 10 Feb 2011 02:02:28 PM MST jgilmore for 5D_on_arduino firmware # Modified Thu 10 Feb 2011 02:02:28 PM MST jgilmore for 5D_on_arduino firmware
# temps are now in 14.2 fixed point notation (i.e. measured in quarter-degrees) # temps are now in 14.2 fixed point notation (i.e. measured in quarter-degrees)
# temps are not permitted to be negative (BUG:may result in numtemps fewer than requested) # temps are not permitted to be negative (BUG:may result in numtemps fewer than requested)
# bugfix: --num-temps command line option works. # bugfix: --num-temps command line option works.
# 2012-11-08, DaveX: Modified to add --vcc=, --min_adc=, --mult and to print per-ADC comments # 2012-11-08, DaveX: Modified to add --vcc=, --min_adc=, --mult and to print per-ADC comments
"""Thermistor Value Lookup Table Generator """Thermistor Value Lookup Table Generator
Generates lookup to temperature values for use in a microcontroller in C format based on: Generates lookup to temperature values for use in a microcontroller in C format based on:
http://hydraraptor.blogspot.com/2007/10/measuring-temperature-easy-way.html http://hydraraptor.blogspot.com/2007/10/measuring-temperature-easy-way.html
The main use is for Arduino programs that read data from the circuit board described here: The main use is for Arduino programs that read data from the circuit board described here:
@ -21,15 +21,15 @@ The main use is for Arduino programs that read data from the circuit board descr
Usage: python createTemperatureLookup.py [options] Usage: python createTemperatureLookup.py [options]
Options: Options:
-h, --help show this help -h, --help show this help
--r0=... thermistor rating where # is the ohm rating of the thermistor at t0 (eg: 10K = 10000) --r0=... thermistor rating where # is the ohm rating of the thermistor at t0 (eg: 10K = 10000)
--t0=... thermistor temp rating where # is the temperature in Celsuis to get r0 (from your datasheet) --t0=... thermistor temp rating where # is the temperature in Celsuis to get r0 (from your datasheet)
--beta=... thermistor beta rating. see http://reprap.org/bin/view/Main/MeasuringThermistorBeta --beta=... thermistor beta rating. see http://reprap.org/bin/view/Main/MeasuringThermistorBeta
--r1=... R1 rating where # is the ohm rating of R1 (eg: 10K = 10000) --r1=... R1 rating where # is the ohm rating of R1 (eg: 10K = 10000)
--r2=... R2 rating where # is the ohm rating of R2 (eg: 10K = 10000) --r2=... R2 rating where # is the ohm rating of R2 (eg: 10K = 10000)
--num-temps=... the number of temperature points to calculate (default: 20) --num-temps=... the number of temperature points to calculate (default: 20)
--max-adc=... the max ADC reading to use. if you use R1, it limits the top value for the thermistor circuit, and thus the possible range of ADC values --max-adc=... the max ADC reading to use. if you use R1, it limits the top value for the thermistor circuit, and thus the possible range of ADC values
--min-adc=... the minimum ADC reading to use. --min-adc=... the minimum ADC reading to use.
--vadc=... ADC reference voltage (high leg of R2) same as Vcc --vadc=... ADC reference voltage (high leg of R2) same as Vcc
--vcc=... Voltage divider supply (high leg of R2) Unused --vcc=... Voltage divider supply (high leg of R2) Unused
--table Format data as one of an array of tables --table Format data as one of an array of tables
@ -43,171 +43,246 @@ from math import *
import sys import sys
import getopt import getopt
class Thermistor: class Thermistor:
"Class to do the thermistor maths" "Class to do the thermistor maths"
def __init__(self, r0, t0, beta, r1, r2,vcc,vadc):
self.r0 = r0 # stated resistance, e.g. 10K
self.t0 = t0 + 273.15 # temperature at stated resistance, e.g. 25C
self.beta = beta # stated beta, e.g. 3500
self.vadc = vadc # ADC reference
self.vcc = vcc # supply voltage to potential divider
self.k = r0 * exp(-beta / self.t0) # constant part of calculation
if r1 > 0: def __init__(self, r0, t0, beta, r1, r2, vcc, vadc):
self.vs = r1 * self.vadc / (r1 + r2) # effective bias voltage self.r0 = r0 # stated resistance, e.g. 10K
self.rs = r1 * r2 / (r1 + r2) # effective bias impedance self.t0 = t0 + 273.15 # temperature at stated resistance, e.g. 25C
else: self.beta = beta # stated beta, e.g. 3500
self.vs = self.vadc # effective bias voltage self.vadc = vadc # ADC reference
self.rs = r2 # effective bias impedance self.vcc = vcc # supply voltage to potential divider
self.k = r0 * exp(-beta / self.t0) # constant part of calculation
def temp(self,adc): if r1 > 0:
"Convert ADC reading into a temperature in Celcius" self.vs = r1 * self.vadc / (r1 + r2) # effective bias voltage
v = adc * self.vadc / 1024 # convert the 10 bit ADC value to a voltage self.rs = r1 * r2 / (r1 + r2) # effective bias impedance
if (self.vs - v): # can be zero due to accuracy limitations else:
r = self.rs * v / (self.vs - v) # resistance of thermistor self.vs = self.vadc # effective bias voltage
else: self.rs = r2 # effective bias impedance
r = self.r0 * 10 # dummy value
try:
return (self.beta / log(r / self.k)) - 273.15 # temperature
except:
print("// error for ADC={adc}, {v},{r}".format(adc=adc, v=v,r=r))
return 0
def resistance(self, t): def temp(self, adc):
"Convert a temperature into a thermistor resistance" "Convert ADC reading into a temperature in Celcius"
return self.r0 * exp(self.beta * (1 / (t + 273.15) - 1 / self.t0)) # resistance of the thermistor v = adc * self.vadc / 1024 # convert the 10 bit ADC value to a voltage
if self.vs - v: # can be zero due to accuracy limitations
r = self.rs * v / (self.vs - v) # resistance of thermistor
else:
r = self.r0 * 10 # dummy value
try:
return (self.beta / log(r / self.k)) - 273.15 # temperature
except:
print("// error for ADC={adc}, {v},{r}".format(adc=adc, v=v, r=r))
return 0
def resistance(self, t):
"Convert a temperature into a thermistor resistance"
return self.r0 * exp(
self.beta * (1 / (t + 273.15) - 1 / self.t0)
) # resistance of the thermistor
def setting(self, t):
"Convert a temperature into a ADC value"
r = self.r0 * exp(
self.beta * (1 / (t + 273.15) - 1 / self.t0)
) # resistance of the thermistor
v = self.vs * r / (self.rs + r) # the voltage at the potential divider
return round(v / self.vadc * 1024) # the ADC reading
def setting(self, t):
"Convert a temperature into a ADC value"
r = self.r0 * exp(self.beta * (1 / (t + 273.15) - 1 / self.t0)) # resistance of the thermistor
v = self.vs * r / (self.rs + r) # the voltage at the potential divider
return round(v / self.vadc * 1024) # the ADC reading
def main(argv): def main(argv):
r0 = 10000; r0 = 10000
t0 = 25; t0 = 25
beta = 3947; beta = 3947
r1 = 680; r1 = 680
r2 = 1600; r2 = 1600
num_temps = int(20); num_temps = int(20)
max_adc = int(1023); max_adc = int(1023)
min_adc = int(1); min_adc = int(1)
vadc=5.0 vadc = 5.0
vcc=5.0 vcc = 5.0
mult=4 mult = 4
table=False table = False
try: try:
opts, args = getopt.getopt(argv, "h", ["help", "r0=", "t0=", "beta=", "r1=", opts, args = getopt.getopt(
"r2=", "max-adc=", "min-adc=", argv,
"num-temps=", "vcc=", "vadc=", "h",
"multiplier=", "table"]) [
except getopt.GetoptError: "help",
usage() "r0=",
sys.exit(2) "t0=",
"beta=",
"r1=",
"r2=",
"max-adc=",
"min-adc=",
"num-temps=",
"vcc=",
"vadc=",
"multiplier=",
"table",
],
)
except getopt.GetoptError:
usage()
sys.exit(2)
for opt, arg in opts: for opt, arg in opts:
if opt in ("-h", "--help"): if opt in ("-h", "--help"):
usage() usage()
sys.exit() sys.exit()
elif opt == "--r0": elif opt == "--r0":
r0 = int(arg) r0 = int(arg)
elif opt == "--t0": elif opt == "--t0":
t0 = int(arg) t0 = int(arg)
elif opt == "--beta": elif opt == "--beta":
beta = int(arg) beta = int(arg)
elif opt == "--r1": elif opt == "--r1":
r1 = int(arg) r1 = int(arg)
elif opt == "--r2": elif opt == "--r2":
r2 = int(arg) r2 = int(arg)
elif opt == "--max-adc": elif opt == "--max-adc":
max_adc = int(arg) max_adc = int(arg)
elif opt == "--min-adc": elif opt == "--min-adc":
min_adc = int(arg) min_adc = int(arg)
elif opt == "--num-temps": elif opt == "--num-temps":
num_temps = int(arg) num_temps = int(arg)
elif opt == "--vadc": elif opt == "--vadc":
vadc = float(arg) vadc = float(arg)
elif opt == "--vcc": elif opt == "--vcc":
vcc = float(arg) vcc = float(arg)
elif opt == "--multiplier": elif opt == "--multiplier":
mult = float(arg) mult = float(arg)
elif opt == "--table": elif opt == "--table":
table = True table = True
if r1: if r1:
max_adc = int(1023. * r1 / (r1 + r2)) max_adc = int(1023.0 * r1 / (r1 + r2))
else: else:
max_adc = 1023 max_adc = 1023
increment = int((max_adc-min_adc)/(num_temps-1)); increment = int((max_adc - min_adc) / (num_temps - 1))
t = Thermistor(r0, t0, beta, r1, r2, vcc, vadc) t = Thermistor(r0, t0, beta, r1, r2, vcc, vadc)
adcs = range(min_adc, max_adc, increment); adcs = range(min_adc, max_adc, increment)
adcs.append(max_adc) adcs.append(max_adc)
# adcs = [1, 20, 25, 30, 35, 40, 45, 50, 60, 70, 80, 90, 100, 110, 130, 150, 190, 220, 250, 300] # adcs = [1, 20, 25, 30, 35, 40, 45, 50, 60, 70, 80, 90, 100, 110, 130, 150, 190, 220, 250, 300]
#Chop of negative temperatures (as we're using a unsigned 16-bit value for temp) # Chop of negative temperatures (as we're using a unsigned 16-bit value for temp)
for i in range(0,len(adcs)): for i in range(0, len(adcs)):
if int(t.temp(adcs[i])*mult) < 0: if int(t.temp(adcs[i]) * mult) < 0:
adcs=adcs[0:i+1] adcs = adcs[0 : i + 1]
#Replace this with the ADC reading for 0C # Replace this with the ADC reading for 0C
adcs[i]=int(t.setting(0)) adcs[i] = int(t.setting(0))
#If the closes ADC reading to 0C is negative, convert to next highest ADC reading # If the closes ADC reading to 0C is negative, convert to next highest ADC reading
if int(t.temp(adcs[i])*mult)<0: if int(t.temp(adcs[i]) * mult) < 0:
adcs[i] -=1 adcs[i] -= 1
break break
print("// Thermistor lookup table for RepRap Temperature Sensor Boards (http://reprap.org/wiki/Temperature_Sensor_2_0)") print(
print("// Made with createTemperatureLookup.py (https://github.com/traumflug/Teacup_Firmware/blob/master/createTemperatureLookup.py)") "// Thermistor lookup table for RepRap Temperature Sensor Boards (http://reprap.org/wiki/Temperature_Sensor_2_0)"
print("// (patched per https://github.com/drf5n/Teacup_Firmware/blob/Gen7/createTemperatureLookup.py)") )
print("// default thermistor lookup table") print(
print("// You may be able to improve the accuracy of this table in various ways.") "// Made with createTemperatureLookup.py (https://github.com/traumflug/Teacup_Firmware/blob/master/createTemperatureLookup.py)"
print("// 1. Measure the actual resistance of the resistor. It's \"nominally\" 4.7K, but that's ± 5%.") )
print("// 2. Measure the actual beta of your thermistor:http://reprap.org/wiki/MeasuringThermistorBeta") print(
print("// 3. Generate more table entries than you need, then trim down the ones in uninteresting ranges.") "// (patched per https://github.com/drf5n/Teacup_Firmware/blob/Gen7/createTemperatureLookup.py)"
print("// In either case you'll have to regenerate this table, which requires python, which is difficult to install on windows.") )
print("// Since you'll have to do some testing to determine the correct temperature for your application anyway, you") print("// default thermistor lookup table")
print("// may decide that the effort isn't worth it. Who cares if it's reporting the \"right\" temperature as long as it's") print("// You may be able to improve the accuracy of this table in various ways.")
print("// keeping the temperature steady enough to print, right?") print(
print("// Temp*%s table from https://github.com/drf5n/Teacup_Firmware/blob/Gen7/createTemperatureLookup.py" %mult) "// 1. Measure the actual resistance of the resistor. It's \"nominally\" 4.7K, but that's ± 5%."
print("// ./createTemperatureLookup.py --r0=%s --t0=%s --r1=%s --r2=%s --beta=%s --max-adc=%s --min-adc=%s --multiplier=%s --vadc=%s" % ( )
r0, t0, r1, r2, beta, max_adc, min_adc, mult, vadc)) print(
print("// r0: %s" % (r0)) "// 2. Measure the actual beta of your thermistor:http://reprap.org/wiki/MeasuringThermistorBeta"
print("// t0: %s" % (t0)) )
print("// r1: %s (parallel with rTherm)" % (r1)) print(
print("// r2: %s (series with rTherm)" % (r2)) "// 3. Generate more table entries than you need, then trim down the ones in uninteresting ranges."
print("// beta: %s" % (beta)) )
print("// min adc: %s at %s V" % (min_adc, min_adc*t.vadc/1024)) print(
print("// max adc: %s at %s V" % (max_adc, max_adc*t.vadc/1024)) "// In either case you'll have to regenerate this table, which requires python, which is difficult to install on windows."
print("// ADC counts from {min} to {max} by {x}".format(min=min_adc, max=max_adc, x=increment)) )
if table == True: print(
print("// #define NUMTABLES 1 // These three lines open the temptable[NUMTABLES]... array") "// Since you'll have to do some testing to determine the correct temperature for your application anyway, you"
print("// #define NUMTEMPS %s // ... " % (len(adcs))) )
print("// uint16_t temptable[NUMTABLES][NUMTEMPS][2] PROGMEM = { // ...") print(
print("{ //" + " Table 0 chunk for B={b}, R0={r0}, R1={r1}, R2={r2}, Vref={v}".format(par="{",b=beta,r0=r0,r1=r1,r2=r2,v=vadc)) "// may decide that the effort isn't worth it. Who cares if it's reporting the \"right\" temperature as long as it's"
else: )
print("#define NUMTEMPS %s " % (len(adcs))) print("// keeping the temperature steady enough to print, right?")
print("const uint16_t temptable[NUMTEMPS][2] PROGMEM = {") print(
print("// {ADC, temp*%s }, // temp Rtherm Vtherm resolution power" % (mult)) "// Temp*%s table from https://github.com/drf5n/Teacup_Firmware/blob/Gen7/createTemperatureLookup.py"
% mult
)
print(
"// ./createTemperatureLookup.py --r0=%s --t0=%s --r1=%s --r2=%s --beta=%s --max-adc=%s --min-adc=%s --multiplier=%s --vadc=%s"
% (r0, t0, r1, r2, beta, max_adc, min_adc, mult, vadc)
)
print("// r0: %s" % (r0))
print("// t0: %s" % (t0))
print("// r1: %s (parallel with rTherm)" % (r1))
print("// r2: %s (series with rTherm)" % (r2))
print("// beta: %s" % (beta))
print("// min adc: %s at %s V" % (min_adc, min_adc * t.vadc / 1024))
print("// max adc: %s at %s V" % (max_adc, max_adc * t.vadc / 1024))
print(
"// ADC counts from {min} to {max} by {x}".format(
min=min_adc, max=max_adc, x=increment
)
)
if table == True:
print(
"// #define NUMTABLES 1 // These three lines open the temptable[NUMTABLES]... array"
)
print("// #define NUMTEMPS %s // ... " % (len(adcs)))
print("// uint16_t temptable[NUMTABLES][NUMTEMPS][2] PROGMEM = { // ...")
print(
"{ //"
+ " Table 0 chunk for B={b}, R0={r0}, R1={r1}, R2={r2}, Vref={v}".format(
par="{", b=beta, r0=r0, r1=r1, r2=r2, v=vadc
)
)
else:
print("#define NUMTEMPS %s " % (len(adcs)))
print("const uint16_t temptable[NUMTEMPS][2] PROGMEM = {")
print(
"// {ADC, temp*%s }, // temp Rtherm Vtherm resolution power"
% (mult)
)
counter = 0
for adc in adcs:
counter = counter + 1
degC = t.temp(adc)
resistance = t.resistance(t.temp(adc))
vTherm = adc * t.vadc / 1024
ptherm = vTherm * vTherm / resistance
resolution = (
t.temp(adc - 1) - t.temp(adc) if adc > 1 else t.temp(adc) - t.temp(adc + 1)
)
sep = "," if counter != len(adcs) else " "
print(
" {%4s, %6s}%s // %7.2f C, %7.0f Ohm, %0.3f V, %0.2f C/count, %0.2fmW"
% (
adc,
int(t.temp(adc) * mult),
sep,
degC,
resistance,
vTherm,
resolution,
ptherm * 1000,
)
)
if table == False:
print("};")
else:
print("}, // remove comma for last table chunk")
print("// }; // Closure for the temptable[NUMTABLES] array")
counter = 0
for adc in adcs:
counter = counter +1
degC=t.temp(adc)
resistance=t.resistance(t.temp(adc))
vTherm= adc*t.vadc/1024
ptherm= vTherm*vTherm/resistance
resolution = ( t.temp(adc-1)-t.temp(adc) if adc>1 else t.temp(adc) -t.temp(adc+1))
sep = (',' if counter != len(adcs) else ' ')
print(" {%4s, %6s}%s // %7.2f C, %7.0f Ohm, %0.3f V, %0.2f C/count, %0.2fmW" % (adc, int(t.temp(adc)*mult), sep,degC, resistance,vTherm,resolution,ptherm*1000))
if table == False:
print("};")
else:
print('}, // remove comma for last table chunk')
print("// }; // Closure for the temptable[NUMTABLES] array")
def usage(): def usage():
print(__doc__) print(__doc__)
if __name__ == "__main__": if __name__ == "__main__":
main(sys.argv[1:]) main(sys.argv[1:])

View File

@ -12,6 +12,6 @@ doc = open("gcode_doc.txt", "wt")
for line in f.readlines(): for line in f.readlines():
m = re_comment.match(line) m = re_comment.match(line)
if m: if m:
doc.write(m.group(1) + "\n") doc.write(m.group(1) + "\n")
f.close() f.close()
doc.close() doc.close()

View File

@ -9,100 +9,102 @@
# Translate a point relative to some origin # Translate a point relative to some origin
from __future__ import print_function from __future__ import print_function
def translate(point, origin): def translate(point, origin):
return tuple([a-b for a,b in zip(point, origin)]) return tuple([a - b for a, b in zip(point, origin)])
# Given two points in 3d space, define a vector # Given two points in 3d space, define a vector
def vector(p1, p2): def vector(p1, p2):
return tuple([b-a for a,b in zip(p1,p2)]) return tuple([b - a for a, b in zip(p1, p2)])
# Given two vectors in a plane, find the normal vector # Given two vectors in a plane, find the normal vector
def normal(u, v): def normal(u, v):
# A normal vector is the cross-product of two coplanar vectors # A normal vector is the cross-product of two coplanar vectors
return tuple([ return tuple(
u[1]*v[2] - u[2]*v[1], [
u[2]*v[0] - u[0]*v[2], u[1] * v[2] - u[2] * v[1],
u[0]*v[1] - u[1]*v[0] u[2] * v[0] - u[0] * v[2],
]) u[0] * v[1] - u[1] * v[0],
]
)
def plane_from_three_points(P, Q, R): def plane_from_three_points(P, Q, R):
u = vector(P, Q) u = vector(P, Q)
v = vector(P, R) v = vector(P, R)
n = normal(u, v) n = normal(u, v)
# Find the coefficients # Find the coefficients
(A,B,C) = n (A, B, C) = n
# The equation of the plane is thus Ax+By+Cz+K=0. # The equation of the plane is thus Ax+By+Cz+K=0.
# Solve for K to get the final coefficient # Solve for K to get the final coefficient
(x,y,z) = P (x, y, z) = P
K = -(A*x + B*y + C*z) K = -(A * x + B * y + C * z)
return (A, B, C, K)
return (A, B, C, K)
# find the Z offset for any x,y # find the Z offset for any x,y
# z = -(Ax + By + K) / C # z = -(Ax + By + K) / C
def calcz(x, y, plane, translation=(0,0,0)): def calcz(x, y, plane, translation=(0, 0, 0)):
(A,B,C,K) = plane (A, B, C, K) = plane
(tx, ty, tz) = translation (tx, ty, tz) = translation
return -(A*(x-tx) + B*(y-ty) + K) / C + tz return -(A * (x - tx) + B * (y - ty) + K) / C + tz
# Verify a point is on this plane # Verify a point is on this plane
def validate(plane, point): def validate(plane, point):
(A, B, C, K) = plane (A, B, C, K) = plane
(x, y, z) = point (x, y, z) = point
return z == calcz(x, y, plane) return z == calcz(x, y, plane)
def verify_plane(points): def verify_plane(points):
print(' ', '\n '.join([str(p) for p in points])) print(" ", "\n ".join([str(p) for p in points]))
plane = plane_from_three_points( *points) plane = plane_from_three_points(*points)
print('Plane coordinates: ', plane) print("Plane coordinates: ", plane)
if plane[2] == 0: if plane[2] == 0:
print(' Error: points are colinear') print(" Error: points are colinear")
return return
valid = True
for p in points:
if not validate(plane, p):
print("Failed: sample point not on plane, ", p)
valid = False
print("Validation:", "Failed" if not valid else "Passed")
valid = True
for p in points:
if not validate(plane, p):
print("Failed: sample point not on plane, ", p)
valid = False
print("Validation:", "Failed" if not valid else "Passed")
samples = [ samples = [
# canonical example # canonical example
[ (1,-2,0), (4,-2,-2), (4,1,4) ], [(1, -2, 0), (4, -2, -2), (4, 1, 4)],
# three colinear points (infinite planes)
# three colinear points (infinite planes) [(2, 2, 2), (4, 4, 4), (10, 10, 10)],
[ (2,2,2), (4,4,4), (10,10,10) ], # Extreme tilt example in mm
[(57, 123, -5), (200, 0, 35), (0, 207, 2)],
# Extreme tilt example in mm # Some more examples in um
[ (57,123,-5), (200,0,35), (0,207,2) ], [(0, 0, 1300), (200000, 200000, 3500), (0, 150000, -1000)],
[(20000, 20000, -300), (220000, 120000, -1700), (120000, 220000, -700)],
# Some more examples in um # some example in tenths of mm
[ (0, 0, 1300), (200000, 200000, 3500), (0, 150000, -1000) ], [(200, 200, -300), (2200, 1200, -1700), (1200, 2200, -700)],
[ (20000, 20000, -300), (220000, 120000, -1700), (120000, 220000, -700) ], [(20000, 20000, -300), (220000, 120000, -1700), (120000, 220000, -700)],
[(200, 200, -300), (2200, 1200, -1700), (1200, 2200, -700)],
# some example in tenths of mm
[ (200, 200, -300), (2200, 1200, -1700), (1200, 2200, -700) ],
[ (20000, 20000 , -300 ), (220000, 120000 , -1700 ), (120000, 220000 , -700 ) ],
[ (200, 200, -300 ), (2200, 1200, -1700 ), (1200, 2200, -700 ) ]
] ]
for points in samples: for points in samples:
verify_plane(points) verify_plane(points)
print("====[Translated]=========") print("====[Translated]=========")
# Translate plane to origin at P (simplifies by removing K coefficient) # Translate plane to origin at P (simplifies by removing K coefficient)
# A*x' + B*y' + C*z' = 0 # A*x' + B*y' + C*z' = 0
P = points[0] P = points[0]
T = translate((0,0,0), P) T = translate((0, 0, 0), P)
xpoints = [translate(p, P) for p in points] xpoints = [translate(p, P) for p in points]
verify_plane(xpoints) verify_plane(xpoints)
print("=========================\n") print("=========================\n")

View File

@ -9,30 +9,32 @@ diff_list = list()
pseudo_print = list() pseudo_print = list()
#define STEPS_PER_M_X 40000 # define STEPS_PER_M_X 40000
#define STEPS_PER_M_Y 40000 # define STEPS_PER_M_Y 40000
#define STEPS_PER_M_Z 320000 # define STEPS_PER_M_Z 320000
def parse_stepper_position(line): def parse_stepper_position(line):
s_line = line.split() s_line = line.split()
X = float(s_line[1]) / 40. # X-axis X = float(s_line[1]) / 40.0 # X-axis
Y = float(s_line[2]) / 40. # Y-axis Y = float(s_line[2]) / 40.0 # Y-axis
Z = float(s_line[3]) / 320. # Z-axis Z = float(s_line[3]) / 320.0 # Z-axis
return X, Y, Z return X, Y, Z
def parse_m114_position(line): def parse_m114_position(line):
s_line = line.split(',') s_line = line.split(",")
X = float(s_line[0][4:]) X = float(s_line[0][4:])
Y = float(s_line[1][2:]) Y = float(s_line[1][2:])
Z = float(s_line[2][2:]) Z = float(s_line[2][2:])
return X, Y, Z return X, Y, Z
with open(in_file, 'r') as file:
with open(in_file, "r") as file:
start_with_line = 50 start_with_line = 50
found_m114 = False found_m114 = False
@ -43,22 +45,22 @@ with open(in_file, 'r') as file:
found_m114 = False found_m114 = False
x1, y1, z1 = parse_m114_position(line) x1, y1, z1 = parse_m114_position(line)
x = x2 - x1 x = x2 - x1
diff_list.append('{}\t\t\t{}\t\t\t{}\t\t\t{}\n'.format(i, x1, x2, x)) diff_list.append("{}\t\t\t{}\t\t\t{}\t\t\t{}\n".format(i, x1, x2, x))
pseudo_print.append('{}\t\t\t{}\t\t\t{}\n'.format(x2, y2, z2)) pseudo_print.append("{}\t\t\t{}\t\t\t{}\n".format(x2, y2, z2))
if line[0] == '#': if line[0] == "#":
if line[2:6] == 'M114': if line[2:6] == "M114":
found_m114 = True found_m114 = True
# find the line with stepping positions before the M114 # find the line with stepping positions before the M114
# print(linecache.getline(in_file, i)) # print(linecache.getline(in_file, i))
for x in range(i - 1, i-20, -1): for x in range(i - 1, i - 20, -1):
pre_m114_line = linecache.getline(in_file, x) pre_m114_line = linecache.getline(in_file, x)
if len(pre_m114_line.split()) == 21: if len(pre_m114_line.split()) == 21:
break break
x2, y2, z2 = parse_stepper_position(pre_m114_line) x2, y2, z2 = parse_stepper_position(pre_m114_line)
with open(out_file, 'w') as file: with open(out_file, "w") as file:
file.writelines(diff_list) file.writelines(diff_list)
with open(pp_file, 'w') as file: with open(pp_file, "w") as file:
file.writelines(pseudo_print) file.writelines(pseudo_print)