From 4ad06f77d416a0ff933d5e8135ac53893079a85f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20K=C3=B6gl?= Date: Wed, 28 Nov 2012 09:28:50 +0100 Subject: [PATCH] better data structures, refined gui, picking and visual feedback implemented --- reflowctl/reflowctl_gui.py | 1102 ++----------------- reflowctl/solder_types/lead_noclean.xml | 36 +- reflowctl/solder_types/leadfree_noclean.xml | 2 - reflowctl/solder_types/test kotze.xml | 2 - 4 files changed, 90 insertions(+), 1052 deletions(-) delete mode 100644 reflowctl/solder_types/leadfree_noclean.xml delete mode 100644 reflowctl/solder_types/test kotze.xml diff --git a/reflowctl/reflowctl_gui.py b/reflowctl/reflowctl_gui.py index 3fb9ca5..4d3390e 100755 --- a/reflowctl/reflowctl_gui.py +++ b/reflowctl/reflowctl_gui.py @@ -1,95 +1,26 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -import sys, os, random, copy, struct +import sys, os, struct -from datetime import datetime - -from collections import deque -from operator import attrgetter - -import xml.etree.ElementTree as etree - -import pylab import numpy from PyQt4 import QtGui, QtCore -from numpy import arange, sin, pi, array, linspace, arange from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.figure import Figure from matplotlib.lines import Line2D -from matplotlib.path import Path -import matplotlib.patches as patches - +import matplotlib.pyplot as plt from mpltools import annotation +from solder import Solder, SolderWidget +from temp_level import TempLevelWidget, TempLevel +from edge_widget import ConstraintWidget + progname = os.path.basename(sys.argv[0]) progversion = "0.1" -def calc_colors(count): - if count == 1: - return [QtGui.QColor(0, 255, 0),] - r = 0 - g = 255 - step = int(512. / (count-1)) - colors = list() - for i in range(count): - colors.append(QtGui.QColor(r, g, 0)) - if r < 255: - r += step - if r > 255: - g -= (r - 256) - r = 255 - g = max(0, g) - else: - g -= step - g = max(0, g) - return colors - -def set_colors(temp_levels): - colors = calc_colors(len(temp_levels) - 1) - ix = 0 - for temp_level in temp_levels: - if not temp_level.is_env: - temp_level.color = colors[ix] - ix += 1 - else: - temp_level.color = QtGui.QColor("black") - - - -def get_valid_neigbors(temp_level, temp_levels): - ix = temp_levels.index(temp_level) - return temp_levels[ix-1], temp_levels[ix+1] - - -def create_profile_header(x_list, y_list): - tpl = "#ifndef _H_SOLDER_PROFILE\n" \ - "#define _H_SOLDER_PROFILE\n" \ - "\n" \ - "%s\n" \ - "\n" \ - "};\n" \ - "\n" \ - "#endif\n" - - data_tpl = "unsigned int profile_%d[%d][2] = \n%s\n" \ - - s = list() - - ix = 0 - for x, y in zip(x_list, y_list): - tmp = list() - for a, b in zip(x, y): - tmp.append("{%d, %d}" % (a, b)) - s.append(data_tpl % (ix, len(x), "\n".join(tmp))) - ix += 1 - - open("test.h", "w").write(tpl % ("\n".join(s))) def create_profile_packet(x, y): - # msg = [length, x_0, y_0,x_1, y_1, ..., x_length-1, y_length-1] - tmp = [len(x) * 2 * 2,] for a, b in zip(x, y): @@ -100,580 +31,16 @@ def create_profile_packet(x, y): res = struct.pack(tpl, *tmp) return res -def getTemperature(): - return 20. - - -class TempLevel(QtCore.QObject): - def __init__(self, name, temp, is_env=False): - super(TempLevel, self).__init__() - self.name = name - self.temp = temp - self.is_env = is_env - self.color = None - - def __str__(self): - return "" % (self.name, self.temp) - - def __lt__(self, other): - return self.temp < other.temp - - def __le__(self, other): - return self.temp <= other.temp - - def __eq__(self, other): - return self.temp == other.temp - - def is_between(self, tl1, tl2): - tmp = [tl1, tl2, self] - tmp.sort() - return self == tmp[1] - - __repr__ = __str__ - -class TempLevelModel(QtCore.QAbstractTableModel): - solder_changed = QtCore.pyqtSignal() - - 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): - if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole: - return QtCore.QVariant(self.headerdata[col]) - return QtCore.QVariant() - - def rowCount(self, parent): - return len(self.temp_levels) - - def columnCount(self, parent): - return 2 - - def data(self, index, role): - if not index.isValid(): - return QtCore.QVariant() - - if role == QtCore.Qt.DisplayRole: - col = index.column() - if col == 0: - return QtCore.QVariant(self.temp_levels[index.row()].name) - else: - return QtCore.QVariant(self.temp_levels[index.row()].temp) - - if index.column() == 0 and role == QtCore.Qt.DecorationRole: - p = QtGui.QPixmap(10,10) - color = self.temp_levels[index.row()].color - p.fill(color) - return p - - return QtCore.QVariant() - - def flags(self, index): - if not index.isValid(): - return 0 - return QtCore.Qt.ItemFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable) - - 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] - print "emit solder_changed" - self.solder_changed.emit() - return True - return False - - def remove_temp_level(self, index): - tmp = self.temp_levels[index] - del self.temp_levels[index] - self.reset() - self.solder_changed.emit() - return tmp - - def add_temp_level(self, index, temp_level): - self.temp_levels.insert(index.row() + 1, temp_level) - set_colors(self.temp_levels) - self.reset() - self.solder_changed.emit() - - 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 TempLevelWidget(QtGui.QWidget): - temp_level_removed = QtCore.pyqtSignal(TempLevel) - temp_levels_changed = QtCore.pyqtSignal() - solder_changed = QtCore.pyqtSignal() - - def __init__(self, parent, readonly=False): - super(TempLevelWidget, self).__init__(parent) - self.readonly = readonly - 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) - - h = QtGui.QHBoxLayout() - h.addWidget(self.temp_level_view) - self.setLayout(h) - - self.connect( - self.temp_level_view, - QtCore.SIGNAL("clicked(QModelIndex)"), - self.temp_level_selected) - - if not readonly: - self.controls = AddRemoveWidget(self) - h.addWidget(self.controls) - - self.connect( - self.controls.add_button, - QtCore.SIGNAL("clicked()"), - self.add_temp_level) - - self.connect( - self.controls.remove_button, - QtCore.SIGNAL("clicked()"), - self.remove_temp_level) - - 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.temp_level_model, - QtCore.SIGNAL("solder_changed()"), - self._solder_changed) - - def _solder_changed(self): - print self._solder_changed - self.solder_changed.emit() - - - def setData(self, solder): - self.temp_level_model.setTempLevels(solder.temp_levels) - self.temp_level_view.resizeColumnsToContents() - self.temp_level_view.setCurrentIndex(self.temp_level_model.index(0, 0)) - - - def add_temp_level(self): - index = self.temp_level_view.currentIndex() - self.temp_level_model.add_temp_level(index, TempLevel("new " + str(self.temp_level_model.rowCount(None)), 0)) - self.temp_level_view.setCurrentIndex(self.temp_level_model.index(index.row() + 1, 0)) - self.temp_levels_changed.emit() - - - def remove_temp_level(self): - self.temp_level_removed.emit( - self.temp_level_model.remove_temp_level( - self.temp_level_view.currentIndex().row())) - self.solder_changed.emit() - - def temp_level_up(self): - dst_row = self.temp_level_view.currentIndex().row() - self.temp_level_model.temp_levels[dst_row - 1], self.temp_level_model.temp_levels[dst_row] = self.temp_level_model.temp_levels[dst_row], self.temp_level_model.temp_levels[dst_row - 1] - self.temp_level_model.reset() - self.solder_changed.emit() - - def temp_level_down(self): - dst_row = self.selected_temp_levels_view.currentIndex().row() - self.temp_level_model.temp_levels[dst_row], self.temp_level_model.temp_levels[dst_row + 1] = self.temp_level_model.temp_levels[dst_row + 1], self.temp_level_model.temp_levels[dst_row] - self.temp_level_model.reset() - self.solder_changed.emit() - - - def temp_level_selected(self, index): - if index.isValid(): - row = index.row() - if not self.readonly: - 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) - - -class Solder(QtCore.QObject): - - log_message = QtCore.pyqtSignal(str) - - def __init__(self, filename, name=str(), description=str(), parent=None): - super(Solder, self).__init__(parent) - self.filename = filename - self.name = name - self.description = description - self.temp_levels = list() - self.durations = list() - self.rates = list() - self.changed = False - - def __unicode__(self): - return unicode(self.name) - - def __str__(self): - return self.name - - 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]) - - def add_duration(self, states, duration): - self.durations.append([states, duration]) - - 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_rate(self, x1, y1, x2, y2): - return (y2 - y1) / (x2 - x1) - - def check_duration_constraints(self, temp_level, used, ix): - - x = list() - y = list() - temp_levels = None - value = None - dx = 0 - for temp_levels, value in self.durations: - tl_len = len(temp_levels) - print "check dur", dx, temp_level, repr(temp_levels), self.ud - if temp_levels and temp_levels[0] == temp_level and tl_len > 1 and dx not in self.ud: - if temp_level not in used: - used.add(temp_level) - x.append(self.time) - y.append(temp_level.temp) - - self.ud.add(dx) - print "using duration constraint", dx - if tl_len == 2: - ix = self.temp_levels.index(temp_levels[1]) - y.append(temp_levels[1].temp) - used.add(temp_levels[1]) - self.time += value - x.append(self.time) - elif tl_len >= 3: - ix = self.temp_levels.index(temp_levels[-1]) - part = value / (tl_len - 1) - for tl in temp_levels[1:]: - used.add(tl) - - self.time += part - x.append(self.time) - y.append(tl.temp) - - self.log.append("* Duration connection: TempLevel %r connected to TempLevels %r" % (temp_level.name, [tl.name for tl in temp_levels[1:]])) - return x, y, ix - dx += 1 - raise Exception() - - def check_rate_constraints(self, temp_level, used, ix): - x = list() - y = list() - rx = 0 - for temp_levels, value in self.rates: - tl_len = len(temp_levels) - print "check rate", rx, temp_level, repr(temp_levels), self.ur - if temp_levels and tl_len > 1 and rx not in self.ur and (temp_levels[0] == temp_level or temp_level.is_between(*temp_levels)): - if temp_level < temp_levels[1] and value < 0: - self.log.append("!RateError: negative rate for increasing temp levels is wrong") - elif temp_level > temp_levels[1] and value > 0: - self.log.append("!RateError: positive rate for decreasing temp levels is wrong") - if temp_level not in used: - used.add(temp_level) - x.append(self.time) - y.append(temp_level.temp) - - self.ur.add(rx) - print "using rate constraint", rx - self.time += (temp_levels[1].temp - temp_level.temp) / value - used.add(temp_levels[1]) - x.append(self.time) - y.append(temp_levels[1].temp) - ix = self.temp_levels.index(temp_levels[-1]) - self.log.append("* Rate connection: TempLevel %r connected to TempLevels %r" % (temp_level.name, [tl.name for tl in temp_levels[1:]])) - return x, y, ix - rx += 1 - raise Exception() - - def calc_profile(self): - print self.calc_profile - - self.log = list() - x = list() - y = list() - duration_points = dict() - rate_points = dict() - self.time = 0 - used = set() - unused = list() - ix = 0 - last = len(self.temp_levels) - self.ud = set() - self.ur = set() - count = len(self.durations) + len(self.rates) - print "max rounds", count - while 1: - print "while", ix, last, x, y, self.ud, self.ur - temp_level = self.temp_levels[ix] - count -= 1 - - try: - dur_x, dur_y, dur_new_ix = self.check_duration_constraints(temp_level, used, ix) - ix = dur_new_ix - print "dur_new_ix", dur_new_ix - x.extend(dur_x) - y.extend(dur_y) - except Exception, e: - print e - try: - rate_x, rate_y, rate_new_ix = self.check_rate_constraints(temp_level, used, ix) - ix = rate_new_ix - print "rate_new_ix", rate_new_ix - x.extend(rate_x) - y.extend(rate_y) - except Exception, e: - print e - ix+=1 - #if temp_level not in used: - #unused.append(temp_level) - - if (y and y[-1] == y[0]) or count <= 0: - break - - self.log.append("") - map(self.log.append, ["* Missing Connection: %r" % tl.name for tl in unused]) - self.log_message.emit("\n".join(self.log)) - del self.log - return array(map(float, x)), array(map(float, y)), min(x), max(x), min(y), max(y), used, unused - - @staticmethod - def unpack(filename, parent): - xmltree = etree.parse(filename) - root = xmltree.getroot() - solder_node = root[0] - s = Solder(filename, solder_node.attrib["name"], solder_node.attrib["description"], parent) - 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) - - set_colors(s.temp_levels) - - 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 save(self): - if self.changed: - solder_node = etree.Element("solder_type", {"name" : self.name, "description" : self.description}) - for temp_level in self.temp_levels: - temp = temp_level.is_env and "$ENV" or str(temp_level.temp) - solder_node.append(etree.Element("state", {"name" : temp_level.name, "temperature" : temp})) - for temp_levels, value in self.durations: - duration = etree.Element("duration", {"value" : str(value)}) - for temp_level in temp_levels: - duration.append(etree.Element("state", {"name" : temp_level.name})) - solder_node.append(duration) - for temp_levels, value in self.rates: - rate = etree.Element("rate", {"value" : str(value)}) - for temp_level in temp_levels: - rate.append(etree.Element("state", {"name" : temp_level.name})) - solder_node.append(rate) - - dirname = SolderListModel.dirname() - root = etree.Element("xml") - root.append(solder_node) - if self.filename is None: - self.filename = os.path.join(dirname, self.name + ".xml") - etree.ElementTree(root).write(self.filename, "UTF-8", True) - self.changed = False - - def setChanged(self): - print self.setChanged - self.changed = True - - - -class SolderListModel(QtCore.QAbstractListModel): - def __init__(self, parent=None, *args): - """ datain: a list where each item is a row - """ - super(SolderListModel, self).__init__(parent, *args) - - self.reload() - - def rowCount(self, parent=QtCore.QModelIndex()): - return len(self.listdata) - - def reload(self): - dirname = self.dirname() - dirlisting = filter(lambda x: os.path.splitext(x)[1] == ".xml", os.listdir(dirname)) - self.listdata = [] - for p in dirlisting: - self.listdata.append(Solder.unpack(os.path.join(dirname, p), self)) - self.listdata.sort(key=lambda x: x.name) - self.reset() - - 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 not index.isValid(): - return QtCore.QVariant() - - solder = self.listdata[index.row()] - if role == QtCore.Qt.DisplayRole: - return QtCore.QVariant(solder.name) - - elif role == QtCore.Qt.DecorationRole and solder.changed: - return QtGui.QIcon.fromTheme("document-save") - - def setData(self, index, variant, role): - if index.isValid() and role == QtCore.Qt.EditRole: - new_name = str(variant.toString()) - if new_name and new_name != "": - self.listdata[index.row()].name = new_name - self.listdata[index.row()].changed = True - return True - return False - - def flags(self, index): - if not index.isValid(): - return 0 - return QtCore.Qt.ItemFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable) - - def create_solder(self): - solder = Solder(None, datetime.now().strftime("%Y-%M-%D %H:%m:%s"), "") - - tl = solder.add_temp_level("environment temp", getTemperature(), True) - tl.color = QtGui.QColor(0, 0, 0) - self.listdata.append(solder) - self.reset() - - @staticmethod - def dirname(): - return os.path.join(os.path.dirname(__file__), "solder_types") - - def check_name(self, name): - for solder in self.listdata: - if name == solder.name: - return False - return True - - -class SolderWidget(QtGui.QWidget): - def __init__(self, parent, readonly=False): - super(SolderWidget, self).__init__(parent) - self.solder_model = SolderListModel(self) - self.solder_view = QtGui.QListView() - self.solder_view.setModel(self.solder_model) - self.solder_controls = AddRemoveWidget(self, False) - - layout = QtGui.QHBoxLayout(self) - layout.addWidget(self.solder_view, 3) - layout.addWidget(self.solder_controls, 1) - - - self.connect( - self.solder_controls.add_button, - QtCore.SIGNAL("clicked()"), - self.solder_model.create_solder) - - self.connect( - self.solder_controls.remove_button, - QtCore.SIGNAL("clicked()"), - self.remove_solder) - - self.solder_view.setCurrentIndex(self.solder_model.index(0,0)) - - def remove_solder(self): - index = self.solder_view.currentIndex() - solder = self.solder_model.listdata[index.row()] - try: - os.remove(solder.filename) - except OSError, e: - print e - pass - del self.solder_model.listdata[index.row()] - self.solder_model.reset() - new_index = self.solder_model.index(0) - self.solder_view.setCurrentIndex(new_index) - return new_index - - def save_solder(self, index): - self.solder_model.listdata[index.row()].save() - self.solder_model.reset() - new_index = self.solder_model.index(self.solder_model.listdata.index(self.plotter.solder)) - self.solder_widget.solder_view.setCurrentIndex(new_index) - class Plotter(FigureCanvas): """A canvas that updates itself every second with a new plot.""" + edge_picked = QtCore.pyqtSignal(int) + def __init__(self, parent, myapp, width, height, dpi): self.fig = Figure(figsize=(width, height), dpi=dpi) super(Plotter, self).__init__(self.fig) self.axes = self.fig.add_subplot(111) - #self.fig.subplots_adjust( - #left=0.1, - #bottom=0.05, - #right=0.9, - #top=0.95, - #wspace=0, - #hspace=0 - #) self.axes.set_axis_bgcolor('white') self.axes.set_title(u'reflow profile', size=12) @@ -681,13 +48,15 @@ class Plotter(FigureCanvas): self.axes.set_ylabel(u'temperature (°C)', size=12) self.axes.set_ymargin(0) self.axes.set_xmargin(0) + self.selx = list() + self.sely = list() 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.plot_data, = self.axes.plot([], linewidth=2.0, color=(0, 0, 1), zorder=10, picker=5) + self.selection_data, = self.axes.plot([], linewidth=5.0, color=(1,0,0), zorder=5) FigureCanvas.setSizePolicy(self, QtGui.QSizePolicy.Expanding, @@ -699,6 +68,22 @@ class Plotter(FigureCanvas): QtCore.QObject.connect(timer, QtCore.SIGNAL("timeout()"), self.update_figure) timer.start(1000) self.updated = False + self.fig.canvas.mpl_connect('pick_event', self.onpick) + + def onpick(self, event): + if isinstance(event.artist, Line2D): + self.set_picked(event.ind[0]) + self.edge_picked.emit(event.ind) + else: + print type(avent.artist) + + + def set_picked(self, ix): + if isinstance(ix, QtCore.QModelIndex): + ix = ix.row() + self.selx = numpy.array(map(float, self.x[ix:ix + 2])) + self.sely = numpy.array(map(float, self.y[ix:ix + 2])) + self.updated = True def update_figure(self): if self.updated: @@ -710,13 +95,19 @@ class Plotter(FigureCanvas): self.y = list() try: - self.x, self.y, self.xmin, self.xmax, self.ymin, self.ymax, self.used, self.unused = self.solder.calc_profile() + (self.x, self.y, self.xmin, self.xmax, self.ymin, self.ymax, + self.used, self.unused) = self.solder.calc_profile() except ValueError: - self.x, self.y, self.xmin, self.xmax, self.ymin, self.ymax, self.used, self.unused = [], [], 0., 300., 0., 300., [], self.solder.temp_levels + (self.x, self.y, self.xmin, self.xmax, self.ymin, self.ymax, + self.used, self.unused) = ([], [], 0., 300., 0., 300., [], + self.solder.temp_levels) self.plot_data.set_xdata(self.x) self.plot_data.set_ydata(self.y) + self.selection_data.set_xdata(self.selx) + self.selection_data.set_ydata(self.sely) + self.axes.set_xbound(lower=self.xmin, upper=self.xmax + 20) self.axes.set_ybound(lower=self.ymin, upper=self.ymax + 20) self.axes.set_xlim(self.xmin, self.xmax + 20, auto=True) @@ -724,6 +115,10 @@ class Plotter(FigureCanvas): self.axes.set_yticks(self.y) self.axes.set_xticks(self.x) + labels = self.axes.get_xticklabels() + for label in labels: + label.set_rotation(30) + for ix, (a, b) in enumerate(zip(self.x[:-1], self.y[:-1])): slope = (self.y[ix+1] - b) / (self.x[ix+1] - a) if not (numpy.isnan(slope) or numpy.isinf(slope)): @@ -734,8 +129,11 @@ class Plotter(FigureCanvas): 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) + 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.draw() @@ -749,304 +147,6 @@ class Plotter(FigureCanvas): self.updated = True -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(QtGui.QIcon.fromTheme("list-add"), "") - self.remove_button = QtGui.QPushButton(QtGui.QIcon.fromTheme("list-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(QtGui.QIcon.fromTheme("go-up"), "") - self.down_button = QtGui.QPushButton(QtGui.QIcon.fromTheme("go-down"), "") - self._layout.addWidget(self.up_button) - self._layout.addWidget(self.down_button) - self.up_button.setStyleSheet(sh) - self.down_button.setStyleSheet(sh) - 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): - solder_changed = QtCore.pyqtSignal() - def __init__(self, name): - super(ConstraintWidget, self).__init__() - self.name = name - - self.spinbox_block = False - - self.constraint_model = ConstraintListModel(self) # constraint selection - self.temp_levels_model = TempLevelModel(self) # temp_level selection pool - self.selected_temp_levels_model = TempLevelModel(self) # selected temp_levels - - self.controls = AddRemoveValueWidget(self) - self.constraint_controls = AddRemoveWidget(self, False) - - self.controls.add_button.setText(u"") - self.controls.remove_button.setText(u"") - - self.constraint_controls.add_button.setText(u"") - self.constraint_controls.remove_button.setText(u"") - - self.constraint_view = QtGui.QListView(self) - self.constraint_view.setModel(self.constraint_model) - - self.temp_levels_view = QtGui.QListView(self) - self.temp_levels_view.setModel(self.temp_levels_model) - - self.selected_temp_levels_view = QtGui.QListView(self) - self.selected_temp_levels_view.setModel(self.selected_temp_levels_model) - - h = QtGui.QHBoxLayout() - h.addWidget(self.constraint_view, 1) - h.addWidget(self.constraint_controls, 1) - h.addWidget(self.temp_levels_view, 3) - h.addWidget(self.controls, 1) - h.addWidget(self.selected_temp_levels_view, 3) - - self.controls.add_button.setIcon(QtGui.QIcon.fromTheme("go-next")) - self.controls.remove_button.setIcon(QtGui.QIcon.fromTheme("go-previous")) - self.setLayout(h) - - self.connect( - self.constraint_view, - QtCore.SIGNAL("clicked(QModelIndex)"), - self._constraint_selected) - - self.connect( - self.controls.add_button, - QtCore.SIGNAL("clicked()"), - self.add_temp_level_to_constraint) - - self.connect( - 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.add_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) - - self.connect( - self.selected_temp_levels_view, - QtCore.SIGNAL("clicked(QModelIndex)"), - self.selected_temp_levels_clicked) - - def setData(self, solder): - self.solder = solder - self.temp_levels_model.setTempLevels(solder.temp_levels) - self._set_data(solder) - self.set_controls() - self.temp_levels_view.setCurrentIndex(self.temp_levels_model.index(0,0)) - - self.controls.up_button.setEnabled(False) - self.controls.down_button.setEnabled(False) - - def set_controls(self): - if not self.constraint_model.constraint_list: - self.controls.value.setEnabled(False) - self.constraint_controls.remove_button.setEnabled(False) - self.controls.add_button.setEnabled(False) - self.controls.remove_button.setEnabled(False) - self.controls.up_button.setEnabled(False) - self.controls.down_button.setEnabled(False) - else: - self.controls.value.setEnabled(True) - self.constraint_controls.remove_button.setEnabled(True) - self.controls.add_button.setEnabled(True) - self.controls.remove_button.setEnabled(True) - self.controls.up_button.setEnabled(True) - self.controls.down_button.setEnabled(True) - - - def selected_temp_levels_clicked(self, index): - if index.isValid(): - self.controls.up_button.setEnabled(True) - self.controls.down_button.setEnabled(True) - - def add_constraint(self): - self.constraint_model.append_constraint() - new_index = self.constraint_model.rowCount() - 1 - self.constraint_view.setCurrentIndex(self.constraint_model.index(new_index, 0)) - self.set_controls() - self.solder_changed.emit() - - def _constraint_selected(self, index): - raise NotImplementedError() - - def _set_data(self, solder): - raise NotImplementedError() - - def add_temp_level_to_constraint(self): - src_row = self.temp_levels_view.currentIndex().row() - temp_level = self.temp_levels_model.temp_levels[src_row] - self.selected_temp_levels_model.temp_levels.append(temp_level) - self.selected_temp_levels_model.reset() - self.selected_temp_levels_view.setCurrentIndex(self.selected_temp_levels_model.index(len(self.selected_temp_levels_model.temp_levels)-1,0)) - self.solder_changed.emit() - - def remove_temp_level_from_constraint(self): - del self.selected_temp_levels_model.temp_levels[self.selected_temp_levels_view.currentIndex().row()] - self.selected_temp_levels_model.reset() - self.selected_temp_levels_view.clearSelection() - self.solder_changed.emit() - - def remove_constraint(self): - src_row = self.temp_levels_view.currentIndex().row() - del self.constraint_model.constraint_list[src_row] - self.constraint_model.reset() - self.selected_temp_levels_model.clear() - self.set_controls() - self.solder_changed.emit() - - def constraint_value_changed(self, value): - if self.spinbox_block: - self.spinbox_block = False - return - src_index = self.constraint_view.currentIndex().row() - self.constraint_model.constraint_list[src_index][1] = value - self.solder_changed.emit() - - def temp_levels_changed(self): - print self.temp_levels_changed - self.temp_levels_model.reset() - - def slot_temp_level_removed(self, temp_level): - deletes = deque() - ix = 0 - for temp_levels, v in self.constraint_model.constraint_list: - if temp_level in temp_levels: - deletes.appendleft(ix) - ix += 1 - for i in deletes: - del self.constraint_model.constraint_list[i] - self.reset() - self.solder_changed.emit() - - def temp_level_up(self): - dst_row = self.selected_temp_levels_view.currentIndex().row() - self.selected_temp_levels_model.temp_levels[dst_row - 1], self.selected_temp_levels_model.temp_levels[dst_row] = self.selected_temp_levels_model.temp_levels[dst_row], self.selected_temp_levels_model.temp_levels[dst_row - 1] - self.selected_temp_levels_model.reset() - self.solder_changed.emit() - - def temp_level_down(self): - dst_row = self.selected_temp_levels_view.currentIndex().row() - self.selected_temp_levels_model.temp_levels[dst_row], self.selected_temp_levels_model.temp_levels[dst_row + 1] = self.selected_temp_levels_model.temp_levels[dst_row + 1], self.selected_temp_levels_model.temp_levels[dst_row] - self.selected_temp_levels_model.reset() - self.solder_changed.emit() - - -class DurationConstraintWidget(ConstraintWidget): - - def _set_data(self, solder): - self.spinbox_block = True - self.constraint_model.constraint_list = solder.durations - self.constraint_model.reset() - ix = self.constraint_model.index(0, 0) - self._constraint_selected(ix) - self.constraint_view.setCurrentIndex(ix) - - def _constraint_selected(self, index): - if index.isValid(): - self.spinbox_block = True - temp_levels, value = self.constraint_model.constraint_list[index.row()] - self.selected_temp_levels_model.setTempLevels(temp_levels) - self.controls.value.setValue(value) - else: - self.spinbox_block = True - self.selected_temp_levels_model.setTempLevels(list()) - self.controls.value.setValue(0) - - -class RateConstraintWidget(ConstraintWidget): - - def _set_data(self, solder): - self.spinbox_block = True - self.constraint_model.constraint_list = solder.rates - self.constraint_model.reset() - ix = self.constraint_model.index(0, 0) - self._constraint_selected(ix) - self.constraint_view.setCurrentIndex(ix) - - - def _constraint_selected(self, index): - if index.isValid(): - self.spinbox_block = True - temp_levels, value = self.constraint_model.constraint_list[index.row()] - self.selected_temp_levels_model.setTempLevels(temp_levels) - self.controls.value.setValue(value) - else: - self.spinbox_block = True - self.selected_temp_levels_model.setTempLevels([]) - self.controls.value.setValue(0) - - class OvenControlsWidget(QtGui.QWidget): def __init__(self, parent=None): super(OvenControlsWidget, self).__init__(parent) @@ -1107,13 +207,10 @@ class OvenControlsWidget(QtGui.QWidget): self.start_button.setIcon(self.sicon) - - class Report(QtGui.QWidget): def __init__(self, parent=None): super(Report, self).__init__(parent) - self.operator = QtGui.QLineEdit(self) self.date_begin = QtGui.QDateTimeEdit(self) self.date_end = QtGui.QDateTimeEdit(self) @@ -1142,8 +239,7 @@ class ApplicationWindow(QtGui.QMainWindow): self.setWindowTitle("application main window") self.file_menu = QtGui.QMenu('&File', self) - self.file_menu.addAction('&Create profile header', self.create_header, - QtCore.Qt.CTRL + QtCore.Qt.Key_C) + self.file_menu.addAction('&Save solder', self.save_solder, QtCore.Qt.CTRL + QtCore.Qt.Key_S) self.file_menu.addAction('&Reload solder', self.reload_solder, @@ -1160,12 +256,13 @@ class ApplicationWindow(QtGui.QMainWindow): self.menuBar().addMenu(self.file_menu) self.view_menu = QtGui.QMenu('&View', self) - self.profile_view_action = self.view_menu.addAction("&Profile View", self.open_profile_view, + self.profile_view_action = self.view_menu.addAction("&Profile View", + self.open_profile_view, QtCore.Qt.CTRL + QtCore.Qt.Key_P) - self.controls_view_action = self.view_menu.addAction("&Oven Controls View", self.open_controls_view, + self.controls_view_action = self.view_menu.addAction( + "&Oven Controls View", self.open_controls_view, QtCore.Qt.CTRL + QtCore.Qt.Key_O) - self.view_group = QtGui.QActionGroup(self) self.view_group.setExclusive(True) @@ -1175,17 +272,17 @@ class ApplicationWindow(QtGui.QMainWindow): self.profile_view_action.setCheckable(True) self.controls_view_action.setCheckable(True) - self.report = Report(self) + #self.report = Report(self) - self.report.hide() + #self.report.hide() - #self.connect(self.profile_view_action, - #QtCore.SIGNAL("triggered()"), - #self.open_profile_view) + self.connect(self.profile_view_action, + QtCore.SIGNAL("triggered()"), + self.open_profile_view) - #self.connect(self.controls_view_action, - #QtCore.SIGNAL("triggered()"), - #self.open_controls_view) + self.connect(self.controls_view_action, + QtCore.SIGNAL("triggered()"), + self.open_controls_view) self.profile_view_action.setChecked(True) @@ -1197,44 +294,30 @@ class ApplicationWindow(QtGui.QMainWindow): self.help_menu.addAction('&About', self.about) - - self.tab_widget = QtGui.QTabWidget(self) self.temp_level_widget = TempLevelWidget(self) - self.duration_widget = DurationConstraintWidget(u"Duration (s)") - self.rate_widget = RateConstraintWidget(u"Rate (°C/s)") + self.constraint_widget = ConstraintWidget(u"Constraints") self.tab_widget.addTab(self.temp_level_widget, u"Temperature Levels (°C)") - self.tab_widget.addTab(self.duration_widget, u"Duration (s)") - self.tab_widget.addTab(self.rate_widget, u"Rate (°C/s)") + self.tab_widget.addTab(self.constraint_widget, u"Constraints") self.plotter = Plotter(self, self, width=5, height=4, dpi=self.dpi) self.controls_widget = OvenControlsWidget(self) self.controls_widget.setVisible(False) - self.connect( - self.duration_widget, - QtCore.SIGNAL("solder_changed()"), - self.plotter.solder_changed) - - self.connect( - self.rate_widget, - QtCore.SIGNAL("solder_changed()"), - self.plotter.solder_changed) - self.connect( self.temp_level_widget, QtCore.SIGNAL("solder_changed()"), self.plotter.solder_changed) self.connect( - self.duration_widget, - QtCore.SIGNAL("solder_changed()"), + self.constraint_widget, + QtCore.SIGNAL("edge_changed()"), self.plotter.solder_changed) self.connect( - self.rate_widget, - QtCore.SIGNAL("solder_changed()"), - self.plotter.solder_changed) + self.constraint_widget.edge_view, + QtCore.SIGNAL("clicked(QModelIndex)"), + self.plotter.set_picked) self.solder_widget = SolderWidget(self) @@ -1243,6 +326,15 @@ class ApplicationWindow(QtGui.QMainWindow): QtCore.SIGNAL("clicked(QModelIndex)"), self.solder_selected) + self.connect( + self.plotter, + QtCore.SIGNAL("edge_picked(int)"), + self.constraint_widget.edge_picked) + + self.connect( + self.plotter, + QtCore.SIGNAL("edge_picked(int)"), + self.edge_picked) self.settings_widget = QtGui.QWidget(self) pl = QtGui.QHBoxLayout(self.settings_widget) @@ -1254,7 +346,7 @@ class ApplicationWindow(QtGui.QMainWindow): self.plotter_splitter = QtGui.QSplitter(self) self.profile_log = QtGui.QTextEdit(self) - self.solder_selected(self.solder_widget.solder_model.index(0,0)) + self.solder_selected(self.solder_widget.solder_model.index(0, 0)) self.plotter_splitter.addWidget(self.plotter) @@ -1271,36 +363,22 @@ class ApplicationWindow(QtGui.QMainWindow): self.statusBar().showMessage("Reflow GORE!1!", 10000) - 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) - - self.temp_level_widget.temp_levels_changed.connect(self.duration_widget.temp_levels_changed) - self.temp_level_widget.temp_levels_changed.connect(self.rate_widget.temp_levels_changed) - def getPlotter(self): return self.plotter - def create_header(self): - x_list = list() - y_list = list() - for solder in self.solder_model.listdata: - x, y, xmax, ymax, duration_points, rate_points = solder.calc_profile() - x_list.append(x) - y_list.append(y) - #print "packet", repr(create_profile_packet(x, y)) - - create_profile_header(x_list, y_list) def solder_selected(self, index): if index.isValid(): - solder = self.solder_widget.solder_model.listdata[index.row()] + solder = self.solder_widget.solder_model.solder_list[index.row()] self.temp_level_widget.setData(solder) - self.duration_widget.setData(solder) - self.rate_widget.setData(solder) + self.constraint_widget.setData(solder) self.plotter.setData(solder) - self.controls_widget.temp_level_widget.setData(solder) solder.log_message.connect(self.profile_log.setPlainText) + def edge_picked(self, ix): + if self.tab_widget.currentIndex() != 1: + self.tab_widget.setCurrentIndex(1) + def remove_solder(self): self.solder_selected(self.solder_widget.remove_solder()) @@ -1313,7 +391,8 @@ class ApplicationWindow(QtGui.QMainWindow): self.settings_widget.show() def show_report(self): - self.report.exec() + pass + #self.report.exec() def save_plot(self): file_choices = "PNG (*.png)|*.png" @@ -1323,15 +402,15 @@ class ApplicationWindow(QtGui.QMainWindow): def save_solder(self): self.plotter.solder.save() - self.solder_widget.solder_model.listdata.sort(key=lambda x: x.name) + self.solder_widget.solder_model.solder_list.sort(key=lambda x: x.name) self.solder_widget.solder_model.reset() - new_index = self.solder_widget.solder_model.index(self.solder_widget.solder_model.listdata.index(self.plotter.solder)) + new_index = self.solder_widget.solder_model.index(self.solder_widget.solder_model.solder_list.index(self.plotter.solder)) self.solder_widget.solder_view.setCurrentIndex(new_index) def reload_solder(self): old_index = self.solder_widget.solder_view.currentIndex() solder = Solder.unpack(self.plotter.solder.filename, self.solder_widget.solder_model) - self.solder_widget.solder_model.listdata[old_index.row()] = solder + self.solder_widget.solder_model.solder_list[old_index.row()] = solder self.solder_widget.solder_model.reset() new_index = self.solder_widget.solder_model.index(old_index.row(), 0) self.solder_widget.solder_view.setCurrentIndex(new_index) @@ -1364,10 +443,5 @@ def main(): sys.exit(qApp.exec_()) -#if __name__ == '__main__': - #main() - - -tls = [TempLevel('peak', 260), TempLevel('environment temp', 20.0)] -a = TempLevel('tal', 200) -print a.is_between(*tls) +if __name__ == '__main__': + main() diff --git a/reflowctl/solder_types/lead_noclean.xml b/reflowctl/solder_types/lead_noclean.xml index d0bc8ec..46e1c98 100644 --- a/reflowctl/solder_types/lead_noclean.xml +++ b/reflowctl/solder_types/lead_noclean.xml @@ -1,34 +1,2 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + \ No newline at end of file diff --git a/reflowctl/solder_types/leadfree_noclean.xml b/reflowctl/solder_types/leadfree_noclean.xml deleted file mode 100644 index 3aeaa73..0000000 --- a/reflowctl/solder_types/leadfree_noclean.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/reflowctl/solder_types/test kotze.xml b/reflowctl/solder_types/test kotze.xml deleted file mode 100644 index d474aad..0000000 --- a/reflowctl/solder_types/test kotze.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file