reflow/reflowctl/reflowctl_gui.py

1019 lines
34 KiB
Python
Raw Normal View History

#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys, os, random, copy, struct
from collections import deque
from operator import attrgetter
import xml.etree.ElementTree as etree
import pylab
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
#from mpltools import annotation-
progname = os.path.basename(sys.argv[0])
progversion = "0.1"
2012-11-20 15:55:22 +00:00
def calc_colors(count):
if count == 1:
return [QtGui.QColor(0, 255, 0),]
2012-11-20 15:55:22 +00:00
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)
2012-11-20 15:55:22 +00:00
r = 255
g = max(0, g)
2012-11-20 15:55:22 +00:00
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 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):
tmp.append(a)
tmp.append(b)
tpl = "H" + (len(x) * 2) * "H"
res = struct.pack(tpl, *tmp)
return res
2012-11-20 15:55:22 +00:00
def getTemperature():
return 20.
2012-11-20 15:55:22 +00:00
class TempLevel(object):
def __init__(self, name, temp, is_env=False):
self.name = name
self.temp = temp
2012-11-20 15:55:22 +00:00
self.is_env = is_env
self.color = None
class Solder(object):
2012-11-20 15:55:22 +00:00
def __init__(self, name=str(), description=str()):
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
2012-11-20 15:55:22 +00:00
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):
2012-11-20 15:55:22 +00:00
self.rates.append([states, rate])
def add_duration(self, states, duration):
2012-11-20 15:55:22 +00:00
self.durations.append([states, duration])
2012-11-20 15:55:22 +00:00
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
2012-11-20 15:55:22 +00:00
def get_temp_level(self, ix):
assert isinstance(ix, int)
return self.temp_levels[ix]
def calc_profile(self):
x = list()
y = list()
duration_points = dict()
rate_points = dict()
self.time = 0
2012-11-20 15:55:22 +00:00
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" % (self.temp_levels[ix-1].name, self.temp_levels[ix].name))
2012-11-20 15:55:22 +00:00
temp_levels = None
duration = None
for sts, dur in self.durations:
if sts and sts[0] == temp_level:
duration = dur
2012-11-20 15:55:22 +00:00
temp_levels = sts
break
2012-11-20 15:55:22 +00:00
if temp_level not in used_temp_levels:
used_temp_levels.add(temp_level)
x.append(self.time)
2012-11-20 15:55:22 +00:00
y.append(temp_level.temp)
if duration is not None:
2012-11-20 15:55:22 +00:00
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)
2012-11-20 15:55:22 +00:00
y.append(temp_levels[1].temp)
self.time += duration / 2
x.append(self.time)
2012-11-20 15:55:22 +00:00
y.append(temp_levels[2].temp)
duration_points[ix] = (x[-3:], y[-3:])
else:
2012-11-20 15:55:22 +00:00
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:])
else:
for ex, (sts, rate) in enumerate(self.rates):
if sts and sts[0] == temp_level:
2012-11-20 15:55:22 +00:00
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)
rate_points[ex] = (x[-2:], y[-2:])
return array(map(float, x)), array(map(float, y)), max(x), max(y), duration_points, rate_points
@staticmethod
def unpack(filename):
xmltree = etree.parse(filename)
root = xmltree.getroot()
2012-11-20 15:55:22 +00:00
solder_node = root[0]
2012-11-20 20:33:15 +00:00
s = Solder(solder_node.attrib["name"], solder_node.attrib["description"])
2012-11-20 15:55:22 +00:00
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)
2012-11-20 15:55:22 +00:00
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
2012-11-20 20:33:15 +00:00
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)
dirname = os.path.join(os.path.dirname(__file__), "solder_types")
2012-11-20 15:55:22 +00:00
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)))
2012-11-20 15:55:22 +00:00
#except Exception, e:
#print e
#pass
2012-11-20 15:55:22 +00:00
def rowCount(self, parent=QtCore.QModelIndex()):
return len(self.listdata)
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(self.listdata[index.row()].name)
else:
return QtCore.QVariant()
2012-11-20 20:33:15 +00:00
def create_solder(self):
solder = Solder("new", "")
tl = solder.add_temp_level("environment temp", getTemperature(), True)
tl.color = QtGui.QColor(0, 0, 0)
2012-11-20 20:33:15 +00:00
self.listdata.append(solder)
self.reset()
2012-11-20 15:55:22 +00:00
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):
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
return QtCore.QVariant(self.headerdata[col])
return QtCore.QVariant()
def rowCount(self, parent):
2012-11-20 15:55:22 +00:00
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:
2012-11-20 15:55:22 +00:00
return QtCore.QVariant(self.temp_levels[index.row()].name)
else:
2012-11-20 15:55:22 +00:00
return QtCore.QVariant(self.temp_levels[index.row()].temp)
if index.column() == 0 and role == QtCore.Qt.DecorationRole:
p = QtGui.QPixmap(10,10)
2012-11-20 15:55:22 +00:00
color = self.temp_levels[index.row()].color
p.fill(color)
return p
return QtCore.QVariant()
2012-11-20 15:55:22 +00:00
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]
self._changed = True
return True
return False
def remove_temp_level(self, index):
tmp = self.temp_levels[index]
del self.temp_levels[index]
self.reset()
2012-11-20 15:55:22 +00:00
self._changed = True
return tmp
2012-11-20 15:55:22 +00:00
def add_temp_level(self, index, temp_level):
self.temp_levels.insert(index.row() + 1, temp_level)
set_colors(self.temp_levels)
self.reset()
2012-11-20 15:55:22 +00:00
self._changed = True
2012-11-20 15:55:22 +00:00
def setTempLevels(self, temp_levels):
assert isinstance(temp_levels, list)
self.temp_levels = temp_levels
self.reset()
2012-11-20 15:55:22 +00:00
def clear(self):
self.temp_levels = list()
self.reset()
2012-11-20 15:55:22 +00:00
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)
2012-11-20 15:55:22 +00:00
super(Plotter, self).__init__(self.fig)
self.axes = self.fig.add_subplot(111)
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)
2012-11-20 15:55:22 +00:00
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)
2012-11-20 15:55:22 +00:00
#self.selection_data, = self.axes.plot([], linewidth=1.0, color=(1,1,1), zorder=5)
FigureCanvas.setSizePolicy(self,
QtGui.QSizePolicy.Expanding,
QtGui.QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
timer = QtCore.QTimer(self)
self.counter = list()
QtCore.QObject.connect(timer, QtCore.SIGNAL("timeout()"), self.update_figure)
timer.start(1000)
2012-11-20 15:55:22 +00:00
self.updated = True
def update_figure(self):
2012-11-20 15:55:22 +00:00
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.axes.set_xbound(lower=0, upper=self.xmax + 20)
self.axes.set_ybound(lower=0, upper=self.ymax + 20)
self.axes.set_yticks([state.temp for state in self.solder.temp_levels])
self.axes.set_xticks(self.x)
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
#self.selection_data.set_xdata(array(da))
#self.selection_data.set_ydata(array(db))
2012-11-20 15:55:22 +00:00
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()
def setData(self, solder):
self.solder = solder
self.updated = True
2012-11-20 15:55:22 +00:00
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")
2012-11-20 15:55:22 +00:00
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.up_button.setStyleSheet(sh)
self.down_button.setStyleSheet(sh)
2012-11-20 15:55:22 +00:00
self._layout.addStretch(4)
2012-11-20 15:55:22 +00:00
class AddRemoveValueWidget(AddRemoveWidget):
def __init__(self, parent):
super(AddRemoveValueWidget, self).__init__(parent)
self.value = QtGui.QSpinBox(self)
2012-11-20 15:55:22 +00:00
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)
2012-11-20 15:55:22 +00:00
def headerData(self, col, orientation, role):
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
return QtCore.QVariant("Solder Paste")
return QtCore.QVariant()
2012-11-20 15:55:22 +00:00
def data(self, index, role):
if index.isValid() and role == QtCore.Qt.DisplayRole:
return QtCore.QVariant(index.row() + 1)
else:
return QtCore.QVariant()
2012-11-20 15:55:22 +00:00
def append_constraint(self):
self.constraint_list.append([[], 0])
self.reset()
def remove_constraint(self):
del self.constraint_list[self.currentIndex().row()]
self.reset()
2012-11-20 15:55:22 +00:00
class ConstraintWidget(QtGui.QWidget):
def __init__(self, name):
2012-11-20 15:55:22 +00:00
super(ConstraintWidget, self).__init__()
self.name = name
2012-11-20 15:55:22 +00:00
self.spinbox_block = False
2012-11-20 15:55:22 +00:00
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
2012-11-20 15:55:22 +00:00
self.controls = AddRemoveValueWidget(self)
self.constraint_controls = AddRemoveWidget(self, False)
2012-11-20 15:55:22 +00:00
self.controls.add_button.setText(u"Add TempLevel")
self.controls.remove_button.setText(u"Remove TempLevel")
2012-11-20 15:55:22 +00:00
self.constraint_controls.add_button.setText(u"Add Constraint")
self.constraint_controls.remove_button.setText(u"Remove Constraint")
2012-11-20 15:55:22 +00:00
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.selected_temp_levels_view = QtGui.QListView(self)
self.selected_temp_levels_view.setModel(self.selected_temp_levels)
h = QtGui.QHBoxLayout()
2012-11-20 15:55:22 +00:00
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(
2012-11-20 15:55:22 +00:00
self.constraint_view,
QtCore.SIGNAL("clicked(QModelIndex)"),
2012-11-20 15:55:22 +00:00
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)
2012-11-18 14:38:57 +00:00
self.connect(
2012-11-20 15:55:22 +00:00
self.controls.up_button,
2012-11-18 14:38:57 +00:00
QtCore.SIGNAL("clicked()"),
2012-11-20 15:55:22 +00:00
self.temp_level_up)
2012-11-18 14:38:57 +00:00
self.connect(
2012-11-20 15:55:22 +00:00
self.controls.down_button,
QtCore.SIGNAL("clicked()"),
self.temp_level_down)
self.connect(
self.constraint_controls.add_button,
QtCore.SIGNAL("clicked()"),
self.add_constraint)
2012-11-20 15:55:22 +00:00
self.connect(
self.constraint_controls.remove_button,
2012-11-18 14:38:57 +00:00
QtCore.SIGNAL("clicked()"),
self.remove_constraint)
2012-11-20 15:55:22 +00:00
self.connect(
self.controls.value,
QtCore.SIGNAL("valueChanged(int)"),
self.constraint_value_changed)
def setData(self, solder):
self.solder = solder
self.all_temp_levels.setTempLevels(solder.temp_levels)
self._set_data(solder)
self.set_controls()
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 add_constraint(self):
self.constraint_model.append_constraint()
self.set_controls()
2012-11-20 15:55:22 +00:00
def _constraint_selected(self, index):
2012-11-18 14:38:57 +00:00
raise NotImplementedError()
def _set_data(self, solder):
raise NotImplementedError()
2012-11-20 15:55:22 +00:00
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()
2012-11-18 14:38:57 +00:00
def remove_constraint(self):
2012-11-20 15:55:22 +00:00
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()
self.set_controls()
2012-11-20 15:55:22 +00:00
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
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]
2012-11-20 15:55:22 +00:00
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()
2012-11-20 15:55:22 +00:00
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()
2012-11-18 14:38:57 +00:00
class DurationConstraintWidget(ConstraintWidget):
def _set_data(self, solder):
2012-11-20 15:55:22 +00:00
self.spinbox_block = True
self.constraint_model.constraint_list = solder.durations
2012-11-20 15:55:22 +00:00
ix = self.constraint_model.index(0, 0)
self._constraint_selected(ix)
self.constraint_view.setCurrentIndex(ix)
2012-11-20 15:55:22 +00:00
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.setTempLevels(temp_levels)
self.controls.value.setValue(value)
else:
self.spinbox_block = True
self.selected_temp_levels.setTempLevels([])
self.controls.value.setValue(0)
class OvenControlsWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(OvenControlsWidget, self).__init__(parent)
self.left = QtGui.QWidget(self)
self.temp_lcd = QtGui.QLCDNumber(self)
self.time_lcd = QtGui.QLCDNumber(self)
self.time_lcd.setDigitCount(3)
self.temp_lcd.setDigitCount(3)
palette = QtGui.QPalette()
palette.setColor(QtGui.QPalette.WindowText, QtCore.Qt.black)
self.temp_lcd.setPalette(palette)
self.time_lcd.setPalette(palette)
self.time_lcd.setSmallDecimalPoint(False)
self.temp_lcd.setSmallDecimalPoint(False)
self.temp_lcd.setSegmentStyle(2)
self.time_lcd.setSegmentStyle(2)
self.time_lcd.display("000")
self.temp_lcd.display("142")
self.temp_label = QtGui.QLabel("Time", self)
self.time_label = QtGui.QLabel("Temp", self)
self.temp_label.setBuddy(self.temp_lcd)
self.time_label.setBuddy(self.time_lcd)
self.sicon = QtGui.QIcon.fromTheme("media-playback-start")
self.picon = QtGui.QIcon.fromTheme("media-playback-pause")
self.start_button = QtGui.QPushButton(self.sicon, "start")
self.start_button.setCheckable(True)
layout = QtGui.QHBoxLayout(self.left)
layout.addWidget(self.time_label)
layout.addWidget(self.time_lcd)
layout.addWidget(self.temp_label)
layout.addWidget(self.temp_lcd)
layout.addWidget(self.start_button)
layout2 = QtGui.QHBoxLayout()
self.temp_level_widget = TempLevelWidget(self, True)
layout2.addWidget(self.temp_level_widget, 2)
layout2.addWidget(self.left, 4)
self.setLayout(layout2)
self.connect(
self.start_button,
QtCore.SIGNAL("clicked()"),
self.toggle_button)
def toggle_button(self):
if self.start_button.isChecked():
self.start_button.setIcon(self.picon)
else:
self.start_button.setIcon(self.sicon)
2012-11-20 15:55:22 +00:00
class RateConstraintWidget(ConstraintWidget):
def _set_data(self, solder):
2012-11-20 15:55:22 +00:00
self.spinbox_block = True
self.constraint_model.constraint_list = solder.rates
2012-11-20 15:55:22 +00:00
ix = self.constraint_model.index(0, 0)
self._constraint_selected(ix)
self.constraint_view.setCurrentIndex(ix)
2012-11-20 15:55:22 +00:00
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.setTempLevels(temp_levels)
self.controls.value.setValue(value)
else:
self.spinbox_block = True
self.selected_temp_levels.setTempLevels([])
self.controls.value.setValue(0)
2012-11-20 15:55:22 +00:00
class TempLevelWidget(QtGui.QWidget):
temp_level_removed = QtCore.pyqtSignal(TempLevel)
def __init__(self, parent, readonly=False):
2012-11-20 15:55:22 +00:00
super(TempLevelWidget, self).__init__(parent)
self.temp_level_model = TempLevelModel(self)
2012-11-20 15:55:22 +00:00
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)
2012-11-20 15:55:22 +00:00
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)
def setData(self, solder):
self.temp_level_model.setTempLevels(solder.temp_levels)
self.temp_level_view.resizeColumnsToContents()
2012-11-20 15:55:22 +00:00
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.plotter.solder.changed = True
2012-11-20 15:55:22 +00:00
def remove_temp_level(self):
self.temp_level_removed.emit(
self.temp_level_model.remove_temp_level(
self.temp_level_view.currentIndex().row()))
self.plotter.solder.changed = True
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.plotter.solder.changed = True
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.plotter.solder.changed = True
2012-11-20 15:55:22 +00:00
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)
2012-11-20 15:55:22 +00:00
self.controls.remove_button.setEnabled(not is_env)
class ApplicationWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
2012-11-20 15:55:22 +00:00
self.dpi = 100
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
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 plot', self.save_plot,
QtCore.Qt.CTRL + QtCore.Qt.Key_S)
self.file_menu.addAction('&Quit', self.fileQuit,
QtCore.Qt.CTRL + QtCore.Qt.Key_Q)
self.menuBar().addMenu(self.file_menu)
self.view_menu = QtGui.QMenu('&View', self)
self.profile_view_action = self.view_menu.addAction("&Profile View")
self.controls_view_action = self.view_menu.addAction("&Oven Controls View")
self.view_group = QtGui.QActionGroup(self)
self.view_group.setExclusive(True)
self.view_group.addAction(self.controls_view_action)
self.view_group.addAction(self.profile_view_action)
self.profile_view_action.setCheckable(True)
self.controls_view_action.setCheckable(True)
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.profile_view_action.setChecked(True)
self.menuBar().addMenu(self.view_menu)
self.help_menu = QtGui.QMenu('&Help', self)
self.menuBar().addSeparator()
self.menuBar().addMenu(self.help_menu)
self.help_menu.addAction('&About', self.about)
self.solder_model = SolderListModel(self)
self.solder_view = QtGui.QListView()
self.solder_view.setModel(self.solder_model)
2012-11-20 15:55:22 +00:00
self.solder_controls = AddRemoveWidget(self, False)
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)")
2012-11-20 15:55:22 +00:00
self.tab_widget.addTab(self.temp_level_widget, u"Temperature Levels")
2012-11-18 14:38:57 +00:00
self.tab_widget.addTab(self.duration_widget, u"Duration (s)")
self.tab_widget.addTab(self.rate_widget, u"Rate (°C/s)")
self.plotter = Plotter(self, self, width=5, height=4, dpi=self.dpi)
self.controls_widget = OvenControlsWidget(self)
self.controls_widget.setVisible(False)
2012-11-20 15:55:22 +00:00
self.connect(
self.solder_view,
QtCore.SIGNAL("clicked(QModelIndex)"),
self.solder_selected)
2012-11-18 14:38:57 +00:00
2012-11-20 20:33:15 +00:00
self.connect(
self.solder_controls.add_button,
QtCore.SIGNAL("clicked()"),
self.solder_model.create_solder)
2012-11-20 15:55:22 +00:00
self.settings_widget = QtGui.QWidget(self)
pl = QtGui.QHBoxLayout(self.settings_widget)
pl.addWidget(self.solder_view, 1)
2012-11-20 15:55:22 +00:00
pl.addWidget(self.solder_controls, 1)
pl.addWidget(self.tab_widget, 6)
2012-11-20 15:55:22 +00:00
self.splitter = QtGui.QSplitter(QtCore.Qt.Vertical, self)
self.solder_view.setCurrentIndex(self.solder_model.index(0,0))
self.solder_selected(self.solder_model.index(0,0))
2012-11-20 15:55:22 +00:00
self.splitter.addWidget(self.settings_widget)
self.splitter.addWidget(self.controls_widget)
2012-11-20 15:55:22 +00:00
self.splitter.addWidget(self.plotter)
self.splitter.setStretchFactor(0, 2)
self.splitter.setStretchFactor(1, 2)
self.splitter.setStretchFactor(2, 8)
self.solder_controls.hide()
2012-11-20 15:55:22 +00:00
self.setCentralWidget(self.splitter)
self.statusBar().showMessage("I'm in reflow heaven", 2000)
2012-11-20 15:55:22 +00:00
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 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_model.listdata[index.row()]
self.temp_level_widget.setData(solder)
2012-11-20 15:55:22 +00:00
self.duration_widget.setData(solder)
self.rate_widget.setData(solder)
self.plotter.setData(solder)
self.controls_widget.temp_level_widget.setData(solder)
def open_controls_view(self):
print self.open_controls_view
self.controls_widget.show()
self.settings_widget.hide()
def open_profile_view(self):
print self.open_profile_view
self.controls_widget.hide()
self.settings_widget.show()
def save_plot(self):
file_choices = "PNG (*.png)|*.png"
filename = QtGui.QFileDialog.getSaveFileName(self, 'Save File', 'qtplot.png')
2012-11-20 15:55:22 +00:00
self.plotter.print_figure(str(filename), dpi=self.dpi)
def fileQuit(self):
self.close()
def closeEvent(self, ce):
self.fileQuit()
def about(self):
QtGui.QMessageBox.about(self, "About %s" % progname,
u"%(prog)s version %(version)s\n" \
u"Copyright \N{COPYRIGHT SIGN} 2012 Stefan Kögl\n\n" \
u"reflowctl frontend" % {"prog": progname, "version": progversion})
def main():
qApp = QtGui.QApplication(sys.argv)
aw = ApplicationWindow()
aw.setWindowTitle("%s" % progname)
aw.show()
sys.exit(qApp.exec_())
if __name__ == '__main__':
main()