1231 lines
42 KiB
Python
Executable File
1231 lines
42 KiB
Python
Executable File
#!/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
|
|
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
|
|
|
|
from mpltools import annotation
|
|
|
|
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 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
|
|
|
|
def getTemperature():
|
|
return 20.
|
|
|
|
|
|
class TempLevel(QtCore.QObject):
|
|
def __init__(self, name, temp, is_env=False):
|
|
self.name = name
|
|
self.temp = temp
|
|
self.is_env = is_env
|
|
self.color = None
|
|
|
|
|
|
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):
|
|
|
|
x = list()
|
|
y = list()
|
|
temp_levels = None
|
|
value = None
|
|
for temp_levels, value in self.durations:
|
|
tl_len = len(temp_levels)
|
|
if temp_levels and temp_levels[0] == temp_level and tl_len > 1:
|
|
if temp_level not in used:
|
|
used.add(temp_level)
|
|
x.append(self.time)
|
|
y.append(temp_level.temp)
|
|
|
|
if tl_len == 2:
|
|
y.append(temp_levels[1].temp)
|
|
used.add(temp_levels[1])
|
|
self.time += value
|
|
x.append(self.time)
|
|
elif tl_len >= 3:
|
|
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
|
|
|
|
def check_rate_constraints(self, temp_level, used):
|
|
x = list()
|
|
y = list()
|
|
for temp_levels, value in self.rates:
|
|
tl_len = len(temp_levels)
|
|
if temp_levels and temp_levels[0] == temp_level and tl_len > 1:
|
|
if temp_level not in used:
|
|
used.add(temp_level)
|
|
x.append(self.time)
|
|
y.append(temp_level.temp)
|
|
|
|
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)
|
|
|
|
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
|
|
|
|
def calc_profile(self):
|
|
|
|
self.log = list()
|
|
x = list()
|
|
y = list()
|
|
duration_points = dict()
|
|
rate_points = dict()
|
|
self.time = 0
|
|
used = set()
|
|
unused = list()
|
|
for temp_level in self.temp_levels:
|
|
dur_x, dur_y = self.check_duration_constraints(temp_level, used)
|
|
rate_x, rate_y = self.check_rate_constraints(temp_level, used)
|
|
|
|
if len(dur_x) > 0:
|
|
x.extend(dur_x)
|
|
y.extend(dur_y)
|
|
elif len(rate_x) > 0:
|
|
x.extend(rate_x)
|
|
y.extend(rate_y)
|
|
elif temp_level not in used:
|
|
unused.append(temp_level)
|
|
|
|
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 = os.path.join(os.path.dirname(__file__), "solder_types")
|
|
root = etree.Element("xml")
|
|
root.append(solder_node)
|
|
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 = os.path.join(os.path.dirname(__file__), "solder_types")
|
|
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("new", "")
|
|
|
|
tl = solder.add_temp_level("environment temp", getTemperature(), True)
|
|
tl.color = QtGui.QColor(0, 0, 0)
|
|
self.listdata.append(solder)
|
|
self.reset()
|
|
|
|
|
|
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 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(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)
|
|
self.axes.set_xlabel(u'time (seconds)', size=12)
|
|
self.axes.set_ylabel(u'temperature (°C)', size=12)
|
|
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)
|
|
|
|
FigureCanvas.setSizePolicy(self,
|
|
QtGui.QSizePolicy.Expanding,
|
|
QtGui.QSizePolicy.Expanding)
|
|
FigureCanvas.updateGeometry(self)
|
|
|
|
timer = QtCore.QTimer(self)
|
|
|
|
QtCore.QObject.connect(timer, QtCore.SIGNAL("timeout()"), self.update_figure)
|
|
timer.start(1000)
|
|
self.updated = False
|
|
|
|
def update_figure(self):
|
|
self.fig.lines = lines = list()
|
|
if self.updated:
|
|
self.updated = False
|
|
self.axes.patches = list()
|
|
self.axes.texts = list()
|
|
self.x = list()
|
|
self.y = list()
|
|
|
|
self.x, self.y, self.xmin, self.xmax, self.ymin, self.ymax, self.used, self.unused = self.solder.calc_profile()
|
|
|
|
self.plot_data.set_xdata(self.x)
|
|
self.plot_data.set_ydata(self.y)
|
|
|
|
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_yticks(self.y)
|
|
self.axes.set_xticks(self.x)
|
|
|
|
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)):
|
|
origin = (a + 10, b)
|
|
annotation.slope_marker(origin, slope, ax=self.axes)
|
|
|
|
self.axes.legend(("Estimated profile",), loc=2)
|
|
|
|
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.draw()
|
|
|
|
def solder_changed(self):
|
|
print self.solder_changed
|
|
self.solder.setChanged()
|
|
self.updated = True
|
|
|
|
def setData(self, solder):
|
|
self.solder = solder
|
|
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.all_temp_levels = TempLevelModel(self) # temp_level selection pool
|
|
self.selected_temp_levels = 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.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()
|
|
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.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.all_temp_levels.setTempLevels(solder.temp_levels)
|
|
self._set_data(solder)
|
|
self.set_controls()
|
|
|
|
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()
|
|
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.all_temp_levels_view.currentIndex().row()
|
|
temp_level = self.all_temp_levels.temp_levels[src_row]
|
|
self.selected_temp_levels.temp_levels.append(temp_level)
|
|
self.selected_temp_levels.reset()
|
|
self.selected_temp_levels_view.setCurrentIndex(self.selected_temp_levels.index(len(self.selected_temp_levels.temp_levels)-1,0))
|
|
self.solder_changed.emit()
|
|
|
|
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()
|
|
self.solder_changed.emit()
|
|
|
|
def remove_constraint(self):
|
|
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()
|
|
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 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.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()
|
|
self.solder_changed.emit()
|
|
|
|
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()
|
|
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.setTempLevels(temp_levels)
|
|
self.controls.value.setValue(value)
|
|
else:
|
|
self.spinbox_block = True
|
|
self.selected_temp_levels.setTempLevels(list())
|
|
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)
|
|
|
|
|
|
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.setTempLevels(temp_levels)
|
|
self.controls.value.setValue(value)
|
|
else:
|
|
self.spinbox_block = True
|
|
self.selected_temp_levels.setTempLevels([])
|
|
self.controls.value.setValue(0)
|
|
|
|
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.solder_view.setCurrentIndex(self.solder_model.index(0,0))
|
|
|
|
|
|
class TempLevelWidget(QtGui.QWidget):
|
|
temp_level_removed = QtCore.pyqtSignal(TempLevel)
|
|
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()
|
|
|
|
|
|
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
|
|
self.solder_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 Report(QtGui.QWidget):
|
|
def __init__(self, parent=None):
|
|
super(Report, self).__init__(parent)
|
|
|
|
|
|
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")
|
|
|
|
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,
|
|
QtCore.Qt.CTRL + QtCore.Qt.Key_R)
|
|
self.file_menu.addAction('&Delete solder', self.remove_solder,
|
|
QtCore.Qt.CTRL + QtCore.Qt.Key_D)
|
|
self.file_menu.addAction('S&ave plot', self.save_plot,
|
|
QtCore.Qt.CTRL + QtCore.Qt.Key_A)
|
|
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.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,
|
|
QtCore.Qt.CTRL + QtCore.Qt.Key_O)
|
|
|
|
|
|
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.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.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.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.plotter.solder_changed)
|
|
|
|
self.connect(
|
|
self.rate_widget,
|
|
QtCore.SIGNAL("solder_changed()"),
|
|
self.plotter.solder_changed)
|
|
|
|
self.solder_widget = SolderWidget(self)
|
|
|
|
self.connect(
|
|
self.solder_widget.solder_view,
|
|
QtCore.SIGNAL("clicked(QModelIndex)"),
|
|
self.solder_selected)
|
|
|
|
|
|
self.settings_widget = QtGui.QWidget(self)
|
|
pl = QtGui.QHBoxLayout(self.settings_widget)
|
|
pl.addWidget(self.solder_widget, 1)
|
|
pl.addWidget(self.tab_widget, 3)
|
|
|
|
self.splitter = QtGui.QSplitter(QtCore.Qt.Vertical, self)
|
|
|
|
self.plotter_splitter = QtGui.QSplitter(self)
|
|
self.profile_log = QtGui.QTextEdit(self)
|
|
|
|
self.solder_selected(self.solder_widget.solder_model.index(0,0))
|
|
|
|
|
|
self.plotter_splitter.addWidget(self.plotter)
|
|
self.plotter_splitter.addWidget(self.profile_log)
|
|
|
|
self.splitter.addWidget(self.settings_widget)
|
|
self.splitter.addWidget(self.controls_widget)
|
|
self.splitter.addWidget(self.plotter_splitter)
|
|
self.splitter.setStretchFactor(0, 2)
|
|
self.splitter.setStretchFactor(1, 2)
|
|
self.splitter.setStretchFactor(2, 8)
|
|
|
|
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 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()]
|
|
self.temp_level_widget.setData(solder)
|
|
self.duration_widget.setData(solder)
|
|
self.rate_widget.setData(solder)
|
|
self.plotter.setData(solder)
|
|
self.controls_widget.temp_level_widget.setData(solder)
|
|
solder.log_message.connect(self.profile_log.setPlainText)
|
|
|
|
def remove_solder(self):
|
|
index = self.solder_widget.solder_view.currentIndex()
|
|
solder = self.solder_widget.solder_model.listdata[index.row()]
|
|
del self.solder_widget.solder_model.listdata[index.row()]
|
|
self.solder_widget.solder_model.reset()
|
|
new_index = self.solder_widget.solder_model.index(0)
|
|
self.solder_widget.solder_view.setCurrentIndex(new_index)
|
|
self.solder_selected(new_index)
|
|
os.remove(solder.filename)
|
|
|
|
def open_controls_view(self):
|
|
self.controls_widget.show()
|
|
self.settings_widget.hide()
|
|
|
|
def open_profile_view(self):
|
|
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')
|
|
self.plotter.print_figure(str(filename), dpi=self.dpi)
|
|
|
|
def save_solder(self):
|
|
self.plotter.solder.save()
|
|
self.solder_widget.solder_model.reload()
|
|
|
|
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.reset()
|
|
new_index = self.solder_widget.solder_model.index(old_index.row(), 0)
|
|
self.solder_widget.solder_view.setCurrentIndex(new_index)
|
|
self.solder_selected(new_index)
|
|
|
|
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():
|
|
|
|
# numpy bug workaround
|
|
try:
|
|
numpy.log10(0.0)
|
|
except:
|
|
pass
|
|
|
|
qApp = QtGui.QApplication(sys.argv)
|
|
|
|
aw = ApplicationWindow()
|
|
aw.setWindowTitle("%s" % progname)
|
|
aw.show()
|
|
sys.exit(qApp.exec_())
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|