started qt frontend with solder type xml storage

This commit is contained in:
Stefan Kögl 2012-11-15 00:25:01 +01:00
parent 44485eac01
commit 2dba6d5bac
3 changed files with 312 additions and 70 deletions

312
qtplot.py
View File

@ -1,9 +1,16 @@
# -*- coding: utf-8 -*-
import sys, os, random import sys, os, random
import xml.etree.ElementTree as etree
import pylab
from PyQt4 import QtGui, QtCore from PyQt4 import QtGui, QtCore
from numpy import arange, sin, pi, array, linspace from numpy import arange, sin, pi, array, linspace, arange
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure from matplotlib.figure import Figure
from matplotlib.lines import Line2D
from matplotlib.path import Path from matplotlib.path import Path
import matplotlib.patches as patches import matplotlib.patches as patches
@ -13,106 +20,257 @@ progname = os.path.basename(sys.argv[0])
progversion = "0.1" progversion = "0.1"
class MyMplCanvas(FigureCanvas): class State(object):
"""Ultimately, this is a QWidget (as well as a FigureCanvasAgg, etc.).""" def __init__(self, name, temp):
self.name = name
self.temp = temp
class Solder(object):
def __init__(self):
self.psteps = []
self.durations = dict()
self.rates = dict()
#start = self.add_state("start", 25)
#ps = self.add_state("preheat start", 150)
#pe = self.add_state("preheat end", 185)
#tal = self.add_state("tal", 220)
#peak = self.add_state("peak", 250)
#end = self.add_state("end", 25)
#self.add_duration((ps, pe), 100)
#self.add_duration((tal, peak, tal), 100)
#self.add_rate((start, ps), 1)
#self.add_rate((ps, pe), 1)
#self.add_rate((pe, tal), 1)
#self.add_rate((tal, end), -2)
def add_state(self, name, temp):
s = State(name, temp)
self.psteps.append(s)
return s
def add_rate(self, states, rate):
self.rates[states] = rate
def add_duration(self, states, duration):
self.durations[states] = duration
def get_state(self, name):
for i in self.psteps:
if i.name == name:
return i
return None
def calc_profile(self):
x = list()
y = list()
self.time = 0
used_steps = set()
for pstep in self.psteps:
#print "-- ", repr(pstep.name), pstep.temp, pstep.used, self.time, x, y
if pstep != self.psteps[0] and pstep not in used_steps:
ix = self.psteps.index(pstep)
raise Exception("step %r not connected to step %r or step %r" % (pstep.name, self.psteps[ix-1].name, self.psteps[ix+1].name))
psteps = None
duration = None
for sts, dur in self.durations.iteritems():
if sts[0] == pstep:
duration = dur
psteps = sts
break
if pstep not in used_steps:
used_steps.add(pstep)
x.append(self.time)
y.append(pstep.temp)
if duration is not None:
if len(psteps) == 3:
used_steps.add(psteps[1])
used_steps.add(psteps[2])
self.time += duration / 2
x.append(self.time)
y.append(psteps[1].temp)
#print "3er duration", (self.time, psteps[1].temp)
self.time += duration / 2
x.append(self.time)
y.append(psteps[2].temp)
#print "3er duration", (self.time, psteps[2].temp)
else:
y.append(psteps[1].temp)
used_steps.add(psteps[1])
self.time += duration
x.append(self.time)
#print "2er duration", (self.time, psteps[1].temp)
else:
for sts, rate in self.rates.iteritems():
if sts[0] == pstep:
used_steps.add(sts[1])
duration = (sts[1].temp - pstep.temp) / rate
self.time += duration
x.append(self.time)
y.append(sts[1].temp)
#print "rate", (self.time, sts[1].temp)
return array(map(float, x)), array(map(float, y)), max(x), max(y)
@staticmethod
def unpack(filename):
xmltree = etree.parse(filename)
root = xmltree.getroot()
s = Solder()
for state in root[0].findall("state"):
s.add_state(state.attrib["name"], int(state.attrib["temperature"]))
for duration in root[0].findall("duration"):
states = list()
for state in duration:
states.append(s.get_state(state.attrib["name"]))
s.add_duration(tuple(states), int(duration.attrib["value"]))
for rate in root[0].findall("rate"):
#print rate
states = list()
for state in rate:
states.append(s.get_state(state.attrib["name"]))
s.add_rate(tuple(states), int(rate.attrib["value"]))
return s
class MyDynamicMplCanvas(FigureCanvas):
"""A canvas that updates itself every second with a new plot."""
def __init__(self, parent=None, width=5, height=4, dpi=100): def __init__(self, parent=None, width=5, height=4, dpi=100):
fig = Figure(figsize=(width, height), dpi=dpi) self.fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = fig.add_subplot(111) self.axes = self.fig.add_subplot(111)
# We want the axes cleared every time plot() is called # We want the axes cleared every time plot() is called
self.axes.hold(False) 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)
self.compute_initial_figure() pylab.setp(self.axes.get_xticklabels(), fontsize=8)
pylab.setp(self.axes.get_yticklabels(), fontsize=8)
# super(MyDynamicMplCanvas, self).__init__(self.fig)
FigureCanvas.__init__(self, fig)
self.setParent(parent) self.setParent(parent)
self.solder = Solder.unpack('/home/hotshelf/dev/reflow/solder_types/leadfree_noclean.xml')
self.compute_initial_figure()
FigureCanvas.setSizePolicy(self, FigureCanvas.setSizePolicy(self,
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding,
QtGui.QSizePolicy.Expanding) QtGui.QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self) FigureCanvas.updateGeometry(self)
def compute_initial_figure(self):
pass
class MyDynamicMplCanvas(MyMplCanvas):
"""A canvas that updates itself every second with a new plot."""
def __init__(self, *args, **kwargs):
MyMplCanvas.__init__(self, *args, **kwargs)
timer = QtCore.QTimer(self) timer = QtCore.QTimer(self)
self.counter = list()
QtCore.QObject.connect(timer, QtCore.SIGNAL("timeout()"), self.update_figure) QtCore.QObject.connect(timer, QtCore.SIGNAL("timeout()"), self.update_figure)
timer.start(1000) timer.start(1000)
def compute_initial_figure(self): def compute_initial_figure(self):
x1_min = 150-20.
y1_min = 150.
x2_min = x1_min + 100. """test foo bar"""
y2_min = 200.
x3_min = x2_min + 20. #start_rate = 1
y3_min = 220. #start_temp = 25
#start_period = None
x4_min = x3_min + 249. - y3_min ## warmup
y4_min = 249. #warmup_rate = 1
#warmup_temp = 155
#preheat_period = None
x5_min = x4_min + (y4_min - y3_min) / 3 * 2 ## preheat start
y5_min = y3_min #preheat_start_rate = 1
#preheat_start_temp = 155
#preheat_start_period = 100
x6_min = x5_min + y5_min-20. ## preheat end
y6_min = 20. #preheat_end_rate = 1
#preheat_end_temp = 185
#preheat_end_period = None
p1x = array([0., x1_min, x2_min, x3_min, x4_min, x5_min, x6_min]) #tal_rate = 1
p1y = array([20., y1_min, y2_min, y3_min, y4_min, y5_min, y6_min]) #tal_temp = 220
#tal_duration = 60
interp = pchip(p1x, p1y) #peak_temp = 250
#peak_rate = 1
xx = linspace(0., x6_min, x6_min) #x, y = self.solder.calc_profile()
ynew = interp(xx) #print "x", repr(x)
#print "y", repr(y)
print "len xx", len(xx) #dtp_min = 99999.
print "len p1x", len(p1x) #dtp_max = 0.
print "xy", zip(p1x, p1y) #dtpi = -1
print "ynew", ynew
dtp_min = 99999. #dtn_min = -99999.
dtp_max = 0. #dtn_max = 0.
dtpi = -1 #dtni = -1
#for i in xrange(1, len(y)):
#tmp = (y[i] - y[i-1]) / (x[i] - x[i-1])
#if tmp > 0:
#if tmp < dtp_min:
#dtp_min = tmp
#dtpi = i
#elif tmp > dtp_max:
#dtp_max = tmp
#dtpi = i
#elif tmp < 0:
#if tmp > dtn_min:
#dtn_min = tmp
#dtni = i
#elif tmp < dtn_max:
#dtn_max = tmp
#dtni = i
#print "max negative", dtn_min, dtn_max, dtni
#print "max positive", dtp_min, dtp_max, dtpi
dtn_min = -99999. self.plot_data, = self.axes.plot([], linewidth=1.0, color=(0,0,1))
dtn_max = 0.
dtni = -1
for i in xrange(1, len(ynew)):
tmp = ynew[i] - ynew[i-1]
if tmp > 0:
if tmp < dtp_min:
dtp_min = tmp
dtpi = i
elif tmp > dtp_max:
dtp_max = tmp
dtpi = i
elif tmp < 0:
if tmp > dtn_min:
dtn_min = tmp
dtni = i
elif tmp < dtn_max:
dtn_max = tmp
dtni = i
print "max negative", dtn_min, dtn_max, dtni
print "max positive", dtp_min, dtp_max, dtpi
self.axes.plot(p1x, p1y, "bo", xx, ynew, "r-")
#self.axes.plot(p1x, p1y, 'r-o') #self.axes.plot(p1x, p1y, 'r-o')
def update_figure(self): def update_figure(self):
# Build a list of 4 random integers between 0 and 10 (both inclusive) # Build a list of 4 random integers between 0 and 10 (both inclusive)
#l = [ random.randint(0, 10) for i in xrange(4) ] x, y, xmax, ymax = self.solder.calc_profile()
#self.axes.plot([0, 1, 2, 3], l, 'r') lines = list()
#self.draw() legend = list()
pass
cols = ("lightgreen", "yellow", "orange", "red")
for ix, i in enumerate(self.solder.psteps[1:-1]):
line = Line2D([0, xmax + 20], [i.temp, i.temp],
transform=self.axes.transData, figure=self.fig, color=cols[ix], label="name")
lines.append(line)
self.fig.lines = lines
self.axes.set_xbound(lower=0, upper=xmax + 20)
self.axes.set_ybound(lower=0, upper=ymax + 20)
#self.axes.grid(True, color='gray')
pylab.setp(self.axes.get_xticklabels(), visible=True)
self.plot_data.set_xdata(x)
self.plot_data.set_ydata(y)
self.axes.legend(("Estimated profile",))
self.draw()
class ApplicationWindow(QtGui.QMainWindow): class ApplicationWindow(QtGui.QMainWindow):
@ -124,6 +282,8 @@ class ApplicationWindow(QtGui.QMainWindow):
self.file_menu = QtGui.QMenu('&File', self) self.file_menu = QtGui.QMenu('&File', self)
self.file_menu.addAction('&Quit', self.fileQuit, self.file_menu.addAction('&Quit', self.fileQuit,
QtCore.Qt.CTRL + QtCore.Qt.Key_Q) QtCore.Qt.CTRL + QtCore.Qt.Key_Q)
self.file_menu.addAction('&Save plot', self.save_plot,
QtCore.Qt.CTRL + QtCore.Qt.Key_S)
self.menuBar().addMenu(self.file_menu) self.menuBar().addMenu(self.file_menu)
self.help_menu = QtGui.QMenu('&Help', self) self.help_menu = QtGui.QMenu('&Help', self)
@ -134,17 +294,29 @@ class ApplicationWindow(QtGui.QMainWindow):
self.main_widget = QtGui.QWidget(self) self.main_widget = QtGui.QWidget(self)
self.dpi = 100
#pl = QtGui.QVBoxLayout(self.main_widget)
#self.p
l = QtGui.QVBoxLayout(self.main_widget) l = QtGui.QVBoxLayout(self.main_widget)
#sc = MyStaticMplCanvas(self.main_widget, width=5, height=4, dpi=100) #sc = MyStaticMplCanvas(self.main_widget, width=5, height=4, dpi=100)
dc = MyDynamicMplCanvas(self.main_widget, width=5, height=4, dpi=100) self.dc = MyDynamicMplCanvas(self.main_widget, width=5, height=4, dpi=self.dpi)
#l.addWidget(sc) #l.addWidget(sc)
l.addWidget(dc) l.addWidget(self.dc)
self.main_widget.setFocus() self.main_widget.setFocus()
self.setCentralWidget(self.main_widget) self.setCentralWidget(self.main_widget)
self.statusBar().showMessage("All hail matplotlib!", 2000) self.statusBar().showMessage("All hail matplotlib!", 2000)
def save_plot(self):
file_choices = "PNG (*.png)|*.png"
filename = QtGui.QFileDialog.getSaveFileName(self, 'Save File', 'qtplot.png')
print type(filename), dir(filename)
self.dc.print_figure(str(filename), dpi=self.dpi)
def fileQuit(self): def fileQuit(self):
self.close() self.close()

View File

@ -0,0 +1,35 @@
<xml>
<solder_type name="lead noclean" description="">
<state name="start" temperature="25" />
<state name="preheat start" temperature="150" />
<state name="preheat end" temperature="185" />
<state name="tal" temperature="220" />
<state name="peak" temperature="260" />
<state name="end" temperature="25" />
<duration value="100" >
<state name="preheat start" />
<state name="preheat end" />
</duration>
<duration value="100" >
<state name="tal" />
<state name="peak" />
<state name="tal" />
</duration>
<rate value="1">
<state name="start" />
<state name="preheat start" />
</rate>
<rate value="1">
<state name="preheat start" />
<state name="preheat end" />
</rate>
<rate value="1">
<state name="preheat end" />
<state name="tal" />
</rate>
<rate value="-2">
<state name="peak" />
<state name="end" />
</rate>
</solder_type>
</xml>

View File

@ -0,0 +1,35 @@
<xml>
<solder_type name="leadfree noclean" description="">
<state name="start" temperature="25" />
<state name="preheat start" temperature="150" />
<state name="preheat end" temperature="185" />
<state name="tal" temperature="220" />
<state name="peak" temperature="250" />
<state name="end" temperature="25" />
<duration value="100" >
<state name="preheat start" />
<state name="preheat end" />
</duration>
<duration value="100" >
<state name="tal" />
<state name="peak" />
<state name="tal" />
</duration>
<rate value="1">
<state name="start" />
<state name="preheat start" />
</rate>
<rate value="1">
<state name="preheat start" />
<state name="preheat end" />
</rate>
<rate value="1">
<state name="preheat end" />
<state name="tal" />
</rate>
<rate value="-2">
<state name="peak" />
<state name="end" />
</rate>
</solder_type>
</xml>