From e7ade6bb923039f1b4a2585a7a7b2b2dc66c1c9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20K=C3=B6gl?= Date: Tue, 20 Nov 2012 16:55:22 +0100 Subject: [PATCH] much better --- reflowctl/reflowctl.ino | 17 - reflowctl/reflowctl_gui.py | 703 +++++++++++++------- reflowctl/solder_types/lead_noclean.xml | 7 +- reflowctl/solder_types/leadfree_noclean.xml | 7 +- setup.py | 3 +- 5 files changed, 453 insertions(+), 284 deletions(-) delete mode 100644 reflowctl/reflowctl.ino diff --git a/reflowctl/reflowctl.ino b/reflowctl/reflowctl.ino deleted file mode 100644 index 07d994a..0000000 --- a/reflowctl/reflowctl.ino +++ /dev/null @@ -1,17 +0,0 @@ -// include the library code: -#include -#include -#include -#include - -OvenCtl oven_ctl; - -void setup() { - Serial.begin(9600); - delay(300); - oven_ctl.set_config_state(); -} - -void loop() { - oven_ctl.handle_states(); -} diff --git a/reflowctl/reflowctl_gui.py b/reflowctl/reflowctl_gui.py index c09c45a..ab4d769 100755 --- a/reflowctl/reflowctl_gui.py +++ b/reflowctl/reflowctl_gui.py @@ -18,40 +18,48 @@ from matplotlib.path import Path import matplotlib.patches as patches #from mpltools import annotation - progname = os.path.basename(sys.argv[0]) progversion = "0.1" -PSTEP_COLORS = ("lightgreen", "yellow", "orange", "red") +def calc_colors(count): + r = 0 + g = 255 + step = int(512. / (count-1)) + colors = list() + print "step", step + for i in range(count): + colors.append(QtGui.QColor(r, g, 0)) + if r < 255: + r += step + if r > 255: + g -= r - 255 + r = 255 + else: + g -= step + g = max(0, g) + return colors -class State(object): - def __init__(self, name, temp): +def getTemperature(): + return 20. + + +class TempLevel(object): + def __init__(self, name, temp, is_env=False): self.name = name self.temp = temp + self.is_env = is_env + self.color = None class Solder(object): - def __init__(self): - self.psteps = [] + + def __init__(self, name=str(), description=str()): + self.name = name + self.description = description + self.temp_levels = list() self.durations = list() self.rates = list() - self.name = None - - #start = self.add_state("start", 25) - #ps = self.add_state("preheat start", 150) - #pe = self.add_state("preheat end", 185) - #tal = self.add_state("tal", 220) - #peak = self.add_state("peak", 250) - #end = self.add_state("end", 25) - - #self.add_duration((ps, pe), 100) - #self.add_duration((tal, peak, tal), 100) - - #self.add_rate((start, ps), 1) - #self.add_rate((ps, pe), 1) - #self.add_rate((pe, tal), 1) - #self.add_rate((tal, end), -2) def __unicode__(self): return unicode(self.name) @@ -59,26 +67,28 @@ class Solder(object): def __str__(self): return self.name - def color(self, index): - return PSTEP_COLORS[index] - - def add_state(self, name, temp): - s = State(name, temp) - self.psteps.append(s) + def add_temp_level(self, name, temp, is_env): + s = TempLevel(name, temp, is_env) + self.temp_levels.append(s) return s def add_rate(self, states, rate): - self.rates.append((states, rate)) + self.rates.append([states, rate]) def add_duration(self, states, duration): - self.durations.append((states, duration)) + self.durations.append([states, duration]) - def get_state(self, name): - for i in self.psteps: + def get_temp_level_by_name(self, name): + assert isinstance(name, basestring) + for i in self.temp_levels: if i.name == name: return i return None + def get_temp_level(self, ix): + assert isinstance(ix, int) + return self.temp_levels[ix] + def calc_profile(self): x = list() @@ -86,64 +96,55 @@ class Solder(object): duration_points = dict() rate_points = dict() self.time = 0 - used_steps = set() - for ix, pstep in enumerate(self.psteps): - #print "-- ", repr(pstep.name), pstep.temp, pstep.used, self.time, x, y + used_temp_levels = set() + for ix, temp_level in enumerate(self.temp_levels): + if temp_level != self.temp_levels[0] and temp_level not in used_temp_levels: + ix = self.temp_levels.index(temp_level) + raise ValueError("TempLevel %r not connected to %r" % (temp_level.name[ix-1], self.temp_levels[ix].name)) - if pstep != self.psteps[0] and pstep not in used_steps: - ix = self.psteps.index(pstep) - raise Exception("step %r not connected to step %r or step %r" % (pstep.name, self.psteps[ix-1].name, self.psteps[ix+1].name)) - - psteps = None + temp_levels = None duration = None for sts, dur in self.durations: - if sts[0] == pstep: + if sts[0] == temp_level: duration = dur - psteps = sts + temp_levels = sts break - if pstep not in used_steps: - used_steps.add(pstep) + if temp_level not in used_temp_levels: + used_temp_levels.add(temp_level) x.append(self.time) - y.append(pstep.temp) + y.append(temp_level.temp) if duration is not None: - if len(psteps) == 3: - used_steps.add(psteps[1]) - used_steps.add(psteps[2]) + if len(temp_levels) == 3: + used_temp_levels.add(temp_levels[1]) + used_temp_levels.add(temp_levels[2]) self.time += duration / 2 x.append(self.time) - y.append(psteps[1].temp) - - #print "3er duration", (self.time, psteps[1].temp) + y.append(temp_levels[1].temp) self.time += duration / 2 x.append(self.time) - y.append(psteps[2].temp) + y.append(temp_levels[2].temp) duration_points[ix] = (x[-3:], y[-3:]) - - #print "3er duration", (self.time, psteps[2].temp) else: - y.append(psteps[1].temp) - used_steps.add(psteps[1]) + y.append(temp_levels[1].temp) + used_temp_levels.add(temp_levels[1]) self.time += duration x.append(self.time) duration_points[ix] = (x[-2:], y[-2:]) - #print "2er duration", (self.time, psteps[1].temp) else: for ex, (sts, rate) in enumerate(self.rates): - if sts[0] == pstep: - used_steps.add(sts[1]) - duration = (sts[1].temp - pstep.temp) / rate + if sts[0] == temp_level: + used_temp_levels.add(sts[1]) + duration = (sts[1].temp - temp_level.temp) / rate self.time += duration x.append(self.time) y.append(sts[1].temp) - #print "rate", (self.time, sts[1].temp) rate_points[ex] = (x[-2:], y[-2:]) - return array(map(float, x)), array(map(float, y)), max(x), max(y), duration_points, rate_points @@ -151,27 +152,43 @@ class Solder(object): def unpack(filename): xmltree = etree.parse(filename) root = xmltree.getroot() - s = Solder() - s.name = root[0].attrib["name"] - for state in root[0].findall("state"): - s.add_state(state.attrib["name"], int(state.attrib["temperature"])) - for duration in root[0].findall("duration"): - states = list() - for state in duration: - states.append(s.get_state(state.attrib["name"])) - s.add_duration(states, int(duration.attrib["value"])) - for rate in root[0].findall("rate"): - #print rate - states = list() - for state in rate: - states.append(s.get_state(state.attrib["name"])) - s.add_rate(states, int(rate.attrib["value"])) + solder_node = root[0] + s = Solder(solder_node.attrib["name"], solder_node) + env_count = 0 + for temp_level in solder_node.findall("state"): + tstr = temp_level.attrib["temperature"] + is_env = False + try: + temp = int(tstr) + except ValueError: + if tstr == "$ENV": + temp = getTemperature() + is_env = True + env_count += 1 + s.add_temp_level(temp_level.attrib["name"], temp, is_env) + + colors = calc_colors(len(s.temp_levels) - 1) + ix = 0 + for temp_level in s.temp_levels: + if not temp_level.is_env: + temp_level.color = colors[ix] + ix += 1 + else: + temp_level.color = QtGui.QColor("black") + + for duration in solder_node.findall("duration"): + temp_levels = list() + for temp_level in duration: + temp_levels.append(s.get_temp_level_by_name(temp_level.attrib["name"])) + s.add_duration(temp_levels, int(duration.attrib["value"])) + for rate in solder_node.findall("rate"): + temp_levels = list() + for temp_level in rate: + temp_levels.append(s.get_temp_level_by_name(temp_level.attrib["name"])) + s.add_rate(temp_levels, int(rate.attrib["value"])) return s - def serialize(self, pstep_list): - return ", ".join(map(attrgetter("name"), pstep_list)) - class SolderListModel(QtCore.QAbstractListModel): def __init__(self, parent=None, *args): @@ -180,14 +197,17 @@ class SolderListModel(QtCore.QAbstractListModel): super(SolderListModel, self).__init__(parent, *args) dirname = os.path.join(os.path.dirname(__file__), "solder_types") - dirlisting = os.listdir(dirname) + dirlisting = filter(lambda x: os.path.splitext(x)[1] == ".xml", os.listdir(dirname)) self.listdata = [] for p in dirlisting: #try: self.listdata.append(Solder.unpack(os.path.join(dirname, p))) - #except Exception: + #except Exception, e: + #print e #pass + print len(self.listdata) + def rowCount(self, parent=QtCore.QModelIndex()): return len(self.listdata) @@ -204,10 +224,11 @@ class SolderListModel(QtCore.QAbstractListModel): return QtCore.QVariant() -class PStepModel(QtCore.QAbstractTableModel): - def __init__(self, parent=None, *args): - super(PStepModel, self).__init__(parent, *args) - self.psteps = list() +class TempLevelModel(QtCore.QAbstractTableModel): + def __init__(self, parent): + super(TempLevelModel, self).__init__(parent) + self._changed = False + self.temp_levels = list() self.headerdata = [u"Name", u"Temperature (°C)"] def headerData(self, col, orientation, role): @@ -216,7 +237,7 @@ class PStepModel(QtCore.QAbstractTableModel): return QtCore.QVariant() def rowCount(self, parent): - return len(self.psteps) + return len(self.temp_levels) def columnCount(self, parent): return 2 @@ -228,57 +249,79 @@ class PStepModel(QtCore.QAbstractTableModel): if role == QtCore.Qt.DisplayRole: col = index.column() if col == 0: - return QtCore.QVariant(self.psteps[index.row()].name) + return QtCore.QVariant(self.temp_levels[index.row()].name) else: - return QtCore.QVariant(self.psteps[index.row()].temp) + return QtCore.QVariant(self.temp_levels[index.row()].temp) if index.column() == 0 and role == QtCore.Qt.DecorationRole: p = QtGui.QPixmap(10,10) - cr = row = index.row() - color = index.row() in (0, len(self.psteps)-1) and QtGui.QColor("black") or QtGui.QColor(PSTEP_COLORS[cr-1]) + color = self.temp_levels[index.row()].color p.fill(color) return p return QtCore.QVariant() - def remove_pstep(self, index): - del self.psteps[index] - self.reset() + def flags(self, index): + if not index.isValid(): + return 0 + return QtCore.Qt.ItemFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable) - def add_pstep(self, index, pstep): - self.psteps.insert(index, pstep) - self.reset() + def setData(self, index, variant, role): + if index.isValid() and role == QtCore.Qt.EditRole: + col = index.column() + if col == 0: + self.temp_levels[index.row()].name = str(variant.toString()) + elif col == 1: + self.temp_levels[index.row()].temp = variant.toInt()[0] + self._changed = True + return True + return False - def setSteps(self, steps): - assert isinstance(steps, list) - self.psteps = steps + def remove_temp_level(self, index): + tmp = self.temp_levels[index] + del self.temp_levels[index] + self.reset() + self._changed = True + return tmp + + def add_temp_level(self, index, temp_level): + self.beginInsertRows(QtGui.QModelIndex(), index.row(), 1) + self.temp_levels.temp_levels.insert(index.row(), temp_level) + self.endInsertRows() + self.reset() + self._changed = True + + def setTempLevels(self, temp_levels): + assert isinstance(temp_levels, list) + self.temp_levels = temp_levels self.reset() + def clear(self): + self.temp_levels = list() + self.reset() -class MyDynamicMplCanvas(FigureCanvas): + +class Plotter(FigureCanvas): """A canvas that updates itself every second with a new plot.""" def __init__(self, parent, myapp, width, height, dpi): self.fig = Figure(figsize=(width, height), dpi=dpi) - super(MyDynamicMplCanvas, self).__init__(self.fig) + super(Plotter, self).__init__(self.fig) self.axes = self.fig.add_subplot(111) - ## We want the axes cleared every time plot() is called self.axes.set_axis_bgcolor('black') self.axes.set_title(u'reflow profile', size=12) self.axes.set_xlabel(u'time (seconds)', size=12) self.axes.set_ylabel(u'temperature (°C)', size=12) - - #pylab.setp(self.axes.get_xticklabels(), fontsize=8) - #pylab.setp(self.axes.get_yticklabels(), fontsize=8) + self.axes.set_ymargin(0) + self.axes.set_xmargin(0) self.setParent(parent) self.myapp = myapp - self.solder = None self.plot_data, = self.axes.plot([], linewidth=1.0, color=(0,0,1), zorder=10) - self.selection_data, = self.axes.plot([], linewidth=1.0, color=(1,1,1), zorder=5) + #self.selection_data, = self.axes.plot([], linewidth=1.0, color=(1,1,1), zorder=5) FigureCanvas.setSizePolicy(self, QtGui.QSizePolicy.Expanding, @@ -290,30 +333,24 @@ class MyDynamicMplCanvas(FigureCanvas): self.counter = list() QtCore.QObject.connect(timer, QtCore.SIGNAL("timeout()"), self.update_figure) timer.start(1000) - + self.updated = True def update_figure(self): - #Build a list of 4 random integers between 0 and 10 (both inclusive) - x, y, xmax, ymax, duration_points, rate_points = self.solder.calc_profile() - - lines = list() - legend = list() + if self.updated: + updated = False + self.x, self.y, self.xmax, self.ymax, self.duration_points, self.rate_points = self.solder.calc_profile() #for states, value in self.durations.iteritems(): #annotation.slope_marker((states[0]) - self.fig.lines = lines + self.axes.set_xbound(lower=0, upper=self.xmax + 20) + self.axes.set_ybound(lower=0, upper=self.ymax + 20) - self.axes.set_xbound(lower=0, upper=xmax + 20) - self.axes.set_ybound(lower=0, upper=ymax + 20) - self.axes.set_ymargin(0) - self.axes.set_xmargin(0) + self.axes.set_yticks([state.temp for state in self.solder.temp_levels]) + self.axes.set_xticks(self.x) - self.axes.set_yticks([state.temp for state in self.solder.psteps]) - self.axes.set_xticks(x) - - self.plot_data.set_xdata(x) - self.plot_data.set_ydata(y) + self.plot_data.set_xdata(self.x) + self.plot_data.set_ydata(self.y) self.plot_data.set_zorder(20) duration_widget = self.myapp.duration_widget @@ -321,159 +358,317 @@ class MyDynamicMplCanvas(FigureCanvas): #self.selection_data.set_xdata(array(da)) #self.selection_data.set_ydata(array(db)) - for ix, i in enumerate(self.solder.psteps[1:-1]): - line = Line2D([0, xmax + 20], [i.temp, i.temp], - transform=self.axes.transData, figure=self.fig, color=self.solder.color(ix), label="name", zorder=1) - lines.append(line) + self.fig.lines = lines = list() + for temp_level in self.solder.temp_levels: + if not temp_level.is_env: + line = Line2D([0, self.xmax + 20], [temp_level.temp, temp_level.temp], + transform=self.axes.transData, figure=self.fig, color=str(temp_level.color.name()), label="name", zorder=1) + lines.append(line) self.axes.legend(("Estimated profile",)) self.draw() -# contraint_list | label - checkboxes -# label - value +class AddRemoveWidget(QtGui.QWidget): + def __init__(self, parent, with_upown=True): + #super(AddRemoveWidget, self).__init__(parent) + QtGui.QWidget.__init__(self, parent) + self.add_button = QtGui.QPushButton("Add") + self.remove_button = QtGui.QPushButton("Remove") + + sh = "QPushButton:disabled { background-color: #555555; }" + self.remove_button.setStyleSheet(sh) + self.add_button.setStyleSheet(sh) + + self._layout = QtGui.QVBoxLayout(self) + + self._layout.addWidget(self.add_button) + self._layout.addWidget(self.remove_button) + if with_upown: + self.up_button = QtGui.QPushButton("Up") + self.down_button = QtGui.QPushButton("Down") + self._layout.addWidget(self.up_button) + self._layout.addWidget(self.down_button) + self._layout.addStretch(4) + + +class AddRemoveValueWidget(AddRemoveWidget): + def __init__(self, parent): + super(AddRemoveValueWidget, self).__init__(parent) + self.value = QtGui.QSpinBox(self) + self.value.setRange(-500, 500) + self._layout.addWidget(self.value) + + +class ConstraintListModel(QtCore.QAbstractListModel): + def __init__(self, parent=None, *args): + """ datain: a list where each item is a row + """ + super(ConstraintListModel, self).__init__(parent, *args) + self.constraint_list = list() + + def rowCount(self, parent=QtCore.QModelIndex()): + return len(self.constraint_list) + + def headerData(self, col, orientation, role): + if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole: + return QtCore.QVariant("Solder Paste") + return QtCore.QVariant() + + def data(self, index, role): + if index.isValid() and role == QtCore.Qt.DisplayRole: + return QtCore.QVariant(index.row() + 1) + else: + return QtCore.QVariant() + + def append_constraint(self): + self.constraint_list.append([[], 0]) + self.reset() + + def remove_constraint(self): + del self.constraint_list[self.currentIndex().row()] + self.reset() class ConstraintWidget(QtGui.QWidget): - def __init__(self, name): + def __init__(self, name, solder): super(ConstraintWidget, self).__init__() self.name = name - self.solder = None + #self.solder = solder + self.spinbox_block = False - self.value = QtGui.QSpinBox(self) - self.value.setRange(-300, 400) - self.constraint_model = QtGui.QStringListModel(self) # constraint selection - self.all_psteps = PStepModel(self) # pstep selection pool - self.selected_psteps = PStepModel(self) # selected psteps + self.constraint_model = ConstraintListModel(self) # constraint selection + self.all_temp_levels = TempLevelModel(self) # temp_level selection pool + self.selected_temp_levels = TempLevelModel(self) # selected temp_levels - #self.all_psteps.setSteps(self.solder.psteps) + self.controls = AddRemoveValueWidget(self) + self.constraint_controls = AddRemoveWidget(self, False) - self.add_button = QtGui.QPushButton("Add", self) - self.remove_button = QtGui.QPushButton("Remove", self) + self.controls.add_button.setText(u"Add TempLevel") + self.controls.remove_button.setText(u"Remove TempLevel") - bg = QtGui.QWidget(self) - gbl = QtGui.QVBoxLayout(bg) + self.constraint_controls.add_button.setText(u"Add Constraint") + self.constraint_controls.remove_button.setText(u"Remove Constraint") - gbl.addWidget(self.add_button) - gbl.addWidget(self.remove_button) - gbl.addStretch(5) - gbl.addWidget(self.value) + self.constraint_view = QtGui.QListView(self) + self.constraint_view.setModel(self.constraint_model) + self.all_temp_levels_view = QtGui.QListView(self) + self.all_temp_levels_view.setModel(self.all_temp_levels) - self.constraint_list_view = QtGui.QListView(self) - self.constraint_list_view.setModel(self.constraint_model) - - self.all_psteps_view = QtGui.QListView(self) - self.all_psteps_view.setModel(self.all_psteps) - - self.selected_psteps_view = QtGui.QListView(self) - self.selected_psteps_view.setModel(self.selected_psteps) - - #self.left = QtGui.QWidget(self) - #gl = QtGui.QGridLayout(self.left) - #gl.addWidget(QtGui.QLabel(u"Steps"), 0,0) - #gl.addWidget(QtGui.QLabel(name), 1,0) - #gl.addWidget(self.checkboxes_group, 0, 1) - #gl.addWidget(self.value, 1, 1) - #self.gl = gl - + self.selected_temp_levels_view = QtGui.QListView(self) + self.selected_temp_levels_view.setModel(self.selected_temp_levels) h = QtGui.QHBoxLayout() - h.addWidget(self.constraint_list_view) - h.addWidget(self.all_psteps_view) - h.addWidget(bg) - h.addWidget(self.selected_psteps_view) + h.addWidget(self.constraint_view, 1) + h.addWidget(self.constraint_controls, 1) + h.addWidget(self.all_temp_levels_view, 3) + h.addWidget(self.controls, 1) + h.addWidget(self.selected_temp_levels_view, 3) self.setLayout(h) self.connect( - self.constraint_list_view, + self.constraint_view, QtCore.SIGNAL("clicked(QModelIndex)"), - self.constraint_clicked) + self._constraint_selected) self.connect( - self.add_button, + self.controls.add_button, QtCore.SIGNAL("clicked()"), - self.add_constraint) + self.add_temp_level_to_constraint) self.connect( - self.remove_button, + self.controls.remove_button, + QtCore.SIGNAL("clicked()"), + self.remove_temp_level_from_constraint) + + self.connect( + self.controls.up_button, + QtCore.SIGNAL("clicked()"), + self.temp_level_up) + + self.connect( + self.controls.down_button, + QtCore.SIGNAL("clicked()"), + self.temp_level_down) + + self.connect( + self.constraint_controls.add_button, + QtCore.SIGNAL("clicked()"), + self.constraint_model.append_constraint) + + self.connect( + self.constraint_controls.remove_button, QtCore.SIGNAL("clicked()"), self.remove_constraint) + self.connect( + self.controls.value, + QtCore.SIGNAL("valueChanged(int)"), + self.constraint_value_changed) def setData(self, solder): + print self.setData, 1 self.solder = solder - self.all_psteps.setSteps(self.solder.psteps) - self.getConstraints() + print self.setData, 2 + self.all_temp_levels.setTempLevels(self.solder.temp_levels) + print self.setData, 3 + self._set_data() + print self.setData, 4 - def constraint_clicked(self, index): + def _constraint_selected(self, index): raise NotImplementedError() - def getConstraints(self): + def _set_data(self): raise NotImplementedError() - def add_constraint(self): - raise NotImplementedError() + def add_temp_level_to_constraint(self): + src_row = self.all_temp_levels_view.currentIndex().row() + dst_row = self.selected_temp_levels_view.currentIndex().row() + temp_level = self.all_temp_levels.temp_levels[src_row] + self.selected_temp_levels.temp_levels.insert(dst_row, temp_level) + #tls.append(temp_level) + #tls.sort(key=attrgetter("temp")) + self.selected_temp_levels.reset() + self.selected_temp_levels_view.clearSelection() + + def remove_temp_level_from_constraint(self): + del self.selected_temp_levels.temp_levels[self.selected_temp_levels_view.currentIndex().row()] + self.selected_temp_levels.reset() + self.selected_temp_levels_view.clearSelection() def remove_constraint(self): - raise NotImplementedError() + src_row = self.all_temp_levels_view.currentIndex().row() + del self.constraint_model.constraint_list[src_row] + self.constraint_model.reset() + self.selected_temp_levels.clear() + def constraint_value_changed(self, value): + print self.constraint_value_changed + if self.spinbox_block: + self.spinbox_block = False + return + print + print + print "IIIIEK" + print + src_index = self.constraint_view.currentIndex().row() + self.constraint_model.constraint_list[src_index][1] = value + + def slot_temp_level_removed(self, temp_level): + print "temp_level" + self.reset() + + def temp_level_up(self): + dst_row = self.selected_temp_levels_view.currentIndex().row() + self.selected_temp_levels.temp_levels[dst_row - 1], self.selected_temp_levels.temp_levels[dst_row] = self.selected_temp_levels.temp_levels[dst_row], self.selected_temp_levels.temp_levels[dst_row - 1] + self.selected_temp_levels.reset() + + def temp_level_down(self): + dst_row = self.selected_temp_levels_view.currentIndex().row() + self.selected_temp_levels.temp_levels[dst_row], self.selected_temp_levels.temp_levels[dst_row + 1] = self.selected_temp_levels.temp_levels[dst_row + 1], self.selected_temp_levels.temp_levels[dst_row] + self.selected_temp_levels.reset() class DurationConstraintWidget(ConstraintWidget): - def getConstraints(self): - tmp = QtCore.QStringList() - for ix in xrange(len(self.solder.durations)): - tmp << unicode(ix + 1) + def _set_data(self): + self.spinbox_block = True + print self._set_data, 1 + self.constraint_model.constraint_list.extend(self.solder.durations) + print self._set_data, 2 + ix = self.constraint_model.index(0, 0) + print self._set_data, 3 + self.constraint_view.setCurrentIndex(ix) + print self._set_data, 4 + self._constraint_selected(ix) + print self._set_data, 5 - self.constraint_model.setStringList(tmp) - k, t = self.solder.durations[0] - - self.value.setValue(t) - self.constraint_list_view.setCurrentIndex(self.constraint_model.index(0, 0)) - self.constraint_clicked(self.constraint_model.index(0, 0)) - - def add_constraint(self): - self.selected_psteps.psteps.append(self.all_psteps.psteps[self.all_psteps_view.currentIndex().row()]) - self.selected_psteps.reset() - #self.selected_psteps_view.setCurrentIndex(QtCore.QModelIndex()) - self.selected_psteps_view.clearSelection() - - def remove_constraint(self): - del self.selected_psteps.psteps[self.selected_psteps_view.currentIndex().row()] - self.selected_psteps.reset() - #self.selected_psteps_view.setCurrentIndex(QtCore.QModelIndex()) - self.selected_psteps_view.clearSelection() - - def constraint_clicked(self, index): - psteps, value = self.solder.durations[index.row()] - self.selected_psteps.setSteps(psteps) - self.value.setValue(value) + def _constraint_selected(self, index): + print self._constraint_selected + self.spinbox_block = True + temp_levels, value = self.constraint_model.constraint_list[index.row()] + self.selected_temp_levels.setTempLevels(temp_levels) + self.controls.value.setValue(value) class RateConstraintWidget(ConstraintWidget): - def getConstraints(self): - tmp = QtCore.QStringList() - for ix in xrange(len(self.solder.durations)): - tmp << unicode(ix + 1) + def _set_data(self): + print self._set_data + self.spinbox_block = True + self.constraint_model.constraint_list.extend(self.solder.rates) - self.constraint_model.setStringList(tmp) - k, t = self.solder.rates[0] + ix = self.constraint_model.index(0, 0) + self.constraint_view.setCurrentIndex(ix) + self._constraint_selected(ix) - self.value.setValue(t) - self.constraint_list_view.setCurrentIndex(self.constraint_model.index(0, 0)) - self.constraint_clicked(self.constraint_model.index(0, 0)) + def _constraint_selected(self, index): + print self._constraint_selected + self.spinbox_block = True + temp_levels, value = self.constraint_model.constraint_list[index.row()] + self.selected_temp_levels.setTempLevels(temp_levels) + self.controls.value.setValue(value) - def constraint_clicked(self, index): - psteps, value = self.solder.durations[index.row()] - self.selected_psteps.setSteps(psteps) - self.value.setValue(value) + + +class TempLevelWidget(QtGui.QWidget): + temp_level_removed = QtCore.pyqtSignal(TempLevel) + + def __init__(self, parent, solder): + super(TempLevelWidget, self).__init__(parent) + self.temp_level_model = TempLevelModel(self) + + self.temp_level_view = QtGui.QTableView() + self.temp_level_view.setModel(self.temp_level_model) + self.temp_level_view.verticalHeader().setVisible(False) + self.temp_level_view.resizeColumnsToContents() + self.temp_level_view.horizontalHeader().setStretchLastSection(True) + self.temp_level_view.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) + + self.controls = AddRemoveWidget(self) + + h = QtGui.QHBoxLayout() + h.addWidget(self.temp_level_view) + h.addWidget(self.controls) + self.setLayout(h) + + self.connect( + self.temp_level_view, + QtCore.SIGNAL("clicked(QModelIndex)"), + self.temp_level_selected) + + self.connect( + self.controls.remove_button, + QtCore.SIGNAL("clicked()"), + self.remove_temp_level) + + + def add_temp_level(self): + index = self.temp_level_view.currentIndex() + self.temp_level_model.add_temp_level(index,TempLevel("new", 0)) + + + def remove_temp_level(self): + self.temp_level_removed.emit( + self.temp_level_model.remove_temp_level( + self.temp_level_view.currentIndex().row())) + + + def temp_level_selected(self, index): + if index.isValid(): + row = index.row() + is_env = self.temp_level_model.temp_levels[row].is_env + is_end = row == len(self.temp_level_model.temp_levels) - 1 + self.controls.add_button.setEnabled(not is_end) + self.controls.remove_button.setEnabled(not is_env) + print "remove_button state", self.controls.remove_button.isEnabled() class ApplicationWindow(QtGui.QMainWindow): def __init__(self): QtGui.QMainWindow.__init__(self) + self.dpi = 100 + self.setAttribute(QtCore.Qt.WA_DeleteOnClose) self.setWindowTitle("application main window") @@ -490,70 +685,64 @@ class ApplicationWindow(QtGui.QMainWindow): self.help_menu.addAction('&About', self.about) - self.main_widget = QtGui.QWidget(self) - self.profile_widget = QtGui.QWidget(self) - - self.tab_widget = QtGui.QTabWidget(self) - self.duration_widget = DurationConstraintWidget(u"Duration (s)") - self.rate_widget = RateConstraintWidget(u"Rate (°C/s)") - - self.dpi = 100 - - pl = QtGui.QHBoxLayout(self.profile_widget) - self.solder_model = SolderListModel(self) - self.pstep_model = PStepModel(self) - - self.pstep_view = QtGui.QTableView() - self.pstep_view.setModel(self.pstep_model) - self.pstep_view.verticalHeader().setVisible(False) - self.pstep_view.resizeColumnsToContents() - self.solder_view = QtGui.QListView() self.solder_view.setModel(self.solder_model) + + self.solder_controls = AddRemoveWidget(self, False) + + self.tab_widget = QtGui.QTabWidget(self) + self.temp_level_widget = TempLevelWidget(self, self.solder_model.listdata[0]) + self.duration_widget = DurationConstraintWidget(u"Duration (s)", self.solder_model.listdata[0]) + self.rate_widget = RateConstraintWidget(u"Rate (°C/s)", self.solder_model.listdata[0]) + self.tab_widget.addTab(self.temp_level_widget, u"Temperature Levels") + self.tab_widget.addTab(self.duration_widget, u"Duration (s)") + self.tab_widget.addTab(self.rate_widget, u"Rate (°C/s)") + self.connect( self.solder_view, QtCore.SIGNAL("clicked(QModelIndex)"), self.solder_selected) - self.tab_widget.addTab(self.pstep_view, u"Temperature Steps") - self.tab_widget.addTab(self.duration_widget, u"Duration (s)") - self.tab_widget.addTab(self.rate_widget, u"Rate (°C/s)") - - + self.settings_widget = QtGui.QWidget(self) + pl = QtGui.QHBoxLayout(self.settings_widget) pl.addWidget(self.solder_view, 1) - pl.addWidget(self.tab_widget, 7) - #pl.addWidget(self.duration_widget) - #pl.addWidget(self.rate_widget) + pl.addWidget(self.solder_controls, 1) + pl.addWidget(self.tab_widget, 6) - l = QtGui.QVBoxLayout(self.main_widget) - self.dc = MyDynamicMplCanvas(self, self, width=5, height=4, dpi=self.dpi) + self.splitter = QtGui.QSplitter(QtCore.Qt.Vertical, self) + self.plotter = Plotter(self, self, width=5, height=4, dpi=self.dpi) self.solder_view.setCurrentIndex(self.solder_model.index(0,0)) self.solder_selected(self.solder_model.index(0,0)) - l.addWidget(self.profile_widget, 2) - l.addWidget(self.dc, 8) + self.splitter.addWidget(self.settings_widget) + self.splitter.addWidget(self.plotter) + self.splitter.setStretchFactor(0, 2) + self.splitter.setStretchFactor(1, 8) - self.main_widget.setFocus() - self.setCentralWidget(self.main_widget) + self.setCentralWidget(self.splitter) self.statusBar().showMessage("I'm in reflow heaven", 2000) + self.temp_level_widget.temp_level_removed.connect(self.duration_widget.slot_temp_level_removed) + self.temp_level_widget.temp_level_removed.connect(self.rate_widget.slot_temp_level_removed) + def solder_selected(self, index): if index.isValid(): - self.dc.solder = self.solder_model.listdata[index.row()] - self.pstep_model.setSteps(self.dc.solder.psteps) - self.pstep_view.resizeColumnsToContents() - self.duration_widget.setData(self.dc.solder) - self.rate_widget.setData(self.dc.solder) + solder = self.plotter.solder = self.solder_model.listdata[index.row()] + self.temp_level_widget.temp_level_model.setTempLevels(solder.temp_levels) + self.temp_level_widget.temp_level_view.resizeColumnsToContents() + self.duration_widget.setData(solder) + self.rate_widget.setData(solder) + self.plotter.updated = True def save_plot(self): file_choices = "PNG (*.png)|*.png" filename = QtGui.QFileDialog.getSaveFileName(self, 'Save File', 'qtplot.png') print type(filename), dir(filename) - self.dc.print_figure(str(filename), dpi=self.dpi) + self.plotter.print_figure(str(filename), dpi=self.dpi) def fileQuit(self): self.close() diff --git a/reflowctl/solder_types/lead_noclean.xml b/reflowctl/solder_types/lead_noclean.xml index e9a9a8b..1405853 100644 --- a/reflowctl/solder_types/lead_noclean.xml +++ b/reflowctl/solder_types/lead_noclean.xml @@ -1,11 +1,10 @@ - + - @@ -16,7 +15,7 @@ - + @@ -29,7 +28,7 @@ - + diff --git a/reflowctl/solder_types/leadfree_noclean.xml b/reflowctl/solder_types/leadfree_noclean.xml index ff3f65d..8bd4a22 100644 --- a/reflowctl/solder_types/leadfree_noclean.xml +++ b/reflowctl/solder_types/leadfree_noclean.xml @@ -1,11 +1,10 @@ - + - @@ -16,7 +15,7 @@ - + @@ -29,7 +28,7 @@ - + diff --git a/setup.py b/setup.py index 718a699..5e5a33e 100644 --- a/setup.py +++ b/setup.py @@ -9,8 +9,7 @@ from setuptools import find_packages, setup if sys.version_info >= (3,): extras['use_2to3'] = True - extras['convert_2to3_doctests'] = ['src/your/module/README.txt'] - #extra['use_2to3_fixers'] = ['your.fixers'] + setup( name='reflowctl_gui',