added missing files, replotting in config mode on demand and not via timer thread anymore
This commit is contained in:
parent
40b2130561
commit
ef6298cc44
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from PyQt4 import QtGui, QtCore
|
||||
|
||||
class AddRemoveWidget(QtGui.QWidget):
|
||||
def __init__(self, parent):
|
||||
#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)
|
||||
self._layout.addStretch(4)
|
|
@ -0,0 +1,159 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from PyQt4 import QtGui, QtCore
|
||||
|
||||
|
||||
class Edge(QtCore.QObject):
|
||||
def __init__(self, from_tl, to_tl, duration=None, rate=None):
|
||||
super(Edge, self).__init__(None)
|
||||
self.from_tl = from_tl
|
||||
self.to_tl = to_tl
|
||||
self.duration = duration
|
||||
self.rate = rate
|
||||
|
||||
def __repr__(self):
|
||||
return "Edge(%r, %r, %r, %r)" % (self.from_tl, self.to_tl, self.duration, self.rate)
|
||||
|
||||
|
||||
class EdgeModel(QtCore.QAbstractTableModel):
|
||||
edge_changed = QtCore.pyqtSignal()
|
||||
|
||||
def __init__(self, parent):
|
||||
super(EdgeModel, self).__init__(parent)
|
||||
self.edges = list()
|
||||
self.headerdata = [
|
||||
u"From Temperature (°C)",
|
||||
u"To Temperature (°C)",
|
||||
u"Duration (s)",
|
||||
u"Rate (°C/s)"]
|
||||
|
||||
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.edges)
|
||||
|
||||
def columnCount(self, parent):
|
||||
return 4
|
||||
|
||||
def data(self, index, role):
|
||||
if not index.isValid():
|
||||
return QtCore.QVariant()
|
||||
|
||||
col = index.column()
|
||||
if role == QtCore.Qt.DisplayRole:
|
||||
if col == 0:
|
||||
return QtCore.QVariant(self.edges[index.row()].from_tl.name)
|
||||
elif col == 1:
|
||||
return QtCore.QVariant(self.edges[index.row()].to_tl.name)
|
||||
elif col == 2:
|
||||
return QtCore.QVariant(self.edges[index.row()].duration)
|
||||
elif col == 3:
|
||||
return QtCore.QVariant(self.edges[index.row()].rate)
|
||||
|
||||
if index.column() < 2 and role == QtCore.Qt.DecorationRole:
|
||||
p = QtGui.QPixmap(10,10)
|
||||
if col == 0:
|
||||
p.fill(self.edges[index.row()].from_tl.color)
|
||||
elif col == 1:
|
||||
p.fill(self.edges[index.row()].to_tl.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 == 2:
|
||||
self.edges[index.row()].duration = variant.toFloat()[0]
|
||||
elif col == 3:
|
||||
self.edges[index.row()].rate = variant.toFloat()[0]
|
||||
self.edge_changed.emit()
|
||||
return True
|
||||
return False
|
||||
|
||||
def remove_edge(self, index):
|
||||
tmp = self.edges[index]
|
||||
del self.edges[index]
|
||||
self.reset()
|
||||
self.edge_changed.emit()
|
||||
return tmp
|
||||
|
||||
def add_edge(self, index, edge):
|
||||
self.edges.insert(index.row() + 1, edge)
|
||||
self.reset()
|
||||
self.edge_changed.emit()
|
||||
|
||||
def setTempLevels(self, edges):
|
||||
assert isinstance(edges, list)
|
||||
self.edges = edges
|
||||
self.reset()
|
||||
|
||||
def clear(self):
|
||||
self.edges = list()
|
||||
self.reset()
|
||||
|
||||
def get_edge(self, ix):
|
||||
return self.edges[ix]
|
||||
|
||||
class ConstraintWidget(QtGui.QWidget):
|
||||
edge_changed = QtCore.pyqtSignal()
|
||||
def __init__(self, name):
|
||||
super(ConstraintWidget, self).__init__()
|
||||
self.name = name
|
||||
|
||||
self.edge_model = EdgeModel(self) # temp_level selection pool
|
||||
|
||||
self.edge_view = QtGui.QTableView(self)
|
||||
self.edge_view.setModel(self.edge_model)
|
||||
self.edge_view.verticalHeader().setVisible(False)
|
||||
self.edge_view.resizeColumnsToContents()
|
||||
self.edge_view.horizontalHeader().setStretchLastSection(True)
|
||||
self.edge_view.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
|
||||
|
||||
|
||||
h = QtGui.QHBoxLayout(self)
|
||||
h.addWidget(self.edge_view)
|
||||
|
||||
def edge_picked(self, ix):
|
||||
print self.edge_picked
|
||||
self.edge_view.setCurrentIndex(self.edge_model.index(ix, 0))
|
||||
|
||||
def setData(self, solder):
|
||||
print self.setData
|
||||
self.solder = solder
|
||||
self.edge_model.setTempLevels(solder.edges)
|
||||
self.edge_view.setCurrentIndex(self.edge_model.index(0,0))
|
||||
|
||||
self.connect(
|
||||
self.edge_model,
|
||||
QtCore.SIGNAL("edge_changed()"),
|
||||
self._edge_changed)
|
||||
|
||||
def _edge_changed(self):
|
||||
print self.temp_level_added
|
||||
self.edge_changed.emit()
|
||||
|
||||
def temp_level_added(self, old_tl, new_tl):
|
||||
print self.temp_level_added, len(self.edge_model.edges)
|
||||
new_edge = None
|
||||
ix = 0
|
||||
for ix, edge in enumerate(self.edge_model.edges):
|
||||
print ix, edge
|
||||
if edge.from_tl == old_tl:
|
||||
duration = None if edge.duration is None else edge.duration / 2
|
||||
new_edge = Edge(new_tl, edge.to_tl, duration, edge.rate)
|
||||
edge.duration = duration
|
||||
edge.to_tl = new_tl
|
||||
break
|
||||
self.edge_model.edges.insert(ix+1, new_edge)
|
||||
self.edge_model.reset()
|
||||
print "end", self.temp_level_added
|
||||
|
|
@ -1,449 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import pprint
|
||||
import random
|
||||
import sys
|
||||
import wx
|
||||
|
||||
REFRESH_INTERVAL_MS = 1000
|
||||
|
||||
import matplotlib
|
||||
matplotlib.use('WXAgg')
|
||||
import matplotlib.lines
|
||||
from matplotlib.figure import Figure
|
||||
from matplotlib.pyplot import legend
|
||||
from matplotlib.backends.backend_wxagg import \
|
||||
FigureCanvasWxAgg as FigCanvas, \
|
||||
NavigationToolbar2WxAgg as NavigationToolbar
|
||||
from matplotlib.path import Path
|
||||
import matplotlib.patches as patches
|
||||
import wx.lib.buttons as buttons
|
||||
|
||||
import numpy as np
|
||||
import pylab
|
||||
|
||||
from Arduino_Monitor import SerialData as DataGen
|
||||
import Arduino_Monitor
|
||||
|
||||
|
||||
class GraphFrame(wx.Frame):
|
||||
""" The main frame of the application
|
||||
"""
|
||||
title = 'reflowctl gui'
|
||||
|
||||
def __init__(self):
|
||||
wx.Frame.__init__(self, None, -1, self.title)
|
||||
|
||||
self.datagen = DataGen()
|
||||
self.data = [self.datagen.next()]
|
||||
self.started = False
|
||||
|
||||
self.profile = []
|
||||
self.state = []
|
||||
self.count = 0
|
||||
|
||||
self.create_menu()
|
||||
self.create_status_bar()
|
||||
self.create_main_panel()
|
||||
|
||||
self.redraw_timer = wx.Timer(self)
|
||||
self.Bind(wx.EVT_TIMER, self.on_redraw_timer, self.redraw_timer)
|
||||
self.redraw_timer.Start(REFRESH_INTERVAL_MS)
|
||||
|
||||
|
||||
def create_menu(self):
|
||||
self.menubar = wx.MenuBar()
|
||||
|
||||
menu_file = wx.Menu()
|
||||
m_expt = menu_file.Append(-1, "&Save plot\tCtrl-S", "Save plot to file")
|
||||
self.Bind(wx.EVT_MENU, self.on_save_plot, m_expt)
|
||||
menu_file.AppendSeparator()
|
||||
m_exit = menu_file.Append(-1, "E&xit\tCtrl-X", "Exit")
|
||||
self.Bind(wx.EVT_MENU, self.on_exit, m_exit)
|
||||
|
||||
self.menubar.Append(menu_file, "&File")
|
||||
self.SetMenuBar(self.menubar)
|
||||
|
||||
def create_main_panel(self):
|
||||
self.panel = wx.Panel(self)
|
||||
|
||||
self.hbox1 = wx.BoxSizer(wx.HORIZONTAL)
|
||||
self.init_profile()
|
||||
self.init_log()
|
||||
self.init_oven_status()
|
||||
self.init_plot()
|
||||
self.canvas = FigCanvas(self.panel, -1, self.fig)
|
||||
|
||||
self.recv_config_button = wx.Button(self.panel, -1, "Receive Config")
|
||||
self.Bind(wx.EVT_BUTTON, self.on_recv_config_button, self.recv_config_button)
|
||||
|
||||
self.send_button = wx.Button(self.panel, -1, "Send Config")
|
||||
self.Bind(wx.EVT_BUTTON, self.on_send_button, self.send_button)
|
||||
|
||||
self.start_button = buttons.GenToggleButton(self.panel, -1, "Start")
|
||||
self.Bind(wx.EVT_BUTTON, self.on_start_button, self.start_button)
|
||||
|
||||
#self.on_bitmap = wx.Image('burn.png', wx.BITMAP_TYPE_PNG).Scale(32, 32, wx.IMAGE_QUALITY_HIGH).ConvertToBitmap()
|
||||
#self.off_bitmap = wx.Image('unburn.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap()
|
||||
|
||||
|
||||
self.ctrls = wx.BoxSizer(wx.VERTICAL)
|
||||
self.ctrls.Add(self.recv_config_button, border=5, flag=wx.ALL | wx.ALIGN_LEFT)
|
||||
self.ctrls.Add(self.send_button, border=5, flag=wx.ALL | wx.ALIGN_LEFT)
|
||||
self.ctrls.Add(self.start_button, border=5, flag=wx.ALL | wx.ALIGN_LEFT)
|
||||
self.hbox1.Add(self.ctrls, border=5, flag=wx.ALL | wx.ALIGN_TOP)
|
||||
|
||||
self.vbox = wx.BoxSizer(wx.VERTICAL)
|
||||
self.vbox.Add(self.hbox1, 0, flag=wx.ALIGN_LEFT | wx.ALL)
|
||||
self.vbox.Add(self.canvas, 1, flag=wx.LEFT | wx.TOP | wx.GROW)
|
||||
#self.vbox.Add(self.hbox1, 0, flag=wx.ALIGN_LEFT | wx.TOP)
|
||||
|
||||
|
||||
self.panel.SetSizer(self.vbox)
|
||||
self.vbox.Fit(self)
|
||||
|
||||
def profile_spin_changed(self, event):
|
||||
print dir(event)
|
||||
|
||||
|
||||
def add_profile_item(self, title, sizer, min_=1, max_=250):
|
||||
|
||||
mc = 8
|
||||
|
||||
item = wx.SpinCtrl(self.panel, -1, "", (30, 50))
|
||||
item.SetRange(min_, max_)
|
||||
item.SetValue(Arduino_Monitor.profile[self.count])
|
||||
|
||||
self.Bind(wx.EVT_SPIN, self.profile_spin_changed, item)
|
||||
|
||||
sizer.Add(wx.StaticText(self.panel, -1, title), (self.count, 0))
|
||||
sizer.Add(item, (self.count, 1))
|
||||
|
||||
self.count += 1
|
||||
self.profile.append(item)
|
||||
|
||||
def init_profile(self):
|
||||
self.preheat_sizer = wx.GridBagSizer(5, 5)
|
||||
self.rampup_sizer = wx.GridBagSizer(5, 5)
|
||||
self.peak_sizer = wx.GridBagSizer(5, 5)
|
||||
self.rampdown_sizer = wx.GridBagSizer(5, 5)
|
||||
|
||||
self.add_profile_item("Ts_min (°C)", self.preheat_sizer, 0, 300)
|
||||
self.add_profile_item("Ts_max (°C)", self.preheat_sizer, 0, 300)
|
||||
self.add_profile_item("Ts duration min (s)", self.preheat_sizer, 0, 300)
|
||||
self.add_profile_item("Ts duration max (s)", self.preheat_sizer, 0, 300)
|
||||
|
||||
self.add_profile_item("ts ramp up min (°C/s)", self.preheat_sizer, 1, 100)
|
||||
self.add_profile_item("ts ramp up max (°C/s)", self.preheat_sizer, 1, 100)
|
||||
self.add_profile_item("tp ramp up min (°C/s)", self.peak_sizer, 1, 100)
|
||||
self.add_profile_item("tp ramp up max (°C/s)", self.peak_sizer1, 100)
|
||||
|
||||
self.add_profile_item("Tl duration min (s)", self.rampup_sizer, 0, 300)
|
||||
self.add_profile_item("Tl duration max (s)", self.rampup_sizer, 0, 300)
|
||||
|
||||
self.add_profile_item("Tp (°C)", self.peak_sizer, 0, 300)
|
||||
self.add_profile_item("Tp duration min (s)", self.peak_sizer, 0, 300)
|
||||
self.add_profile_item("Tp duration max (s)", self.peak_sizer, 0, 300)
|
||||
|
||||
self.add_profile_item("ramp down min (°C/s)", self.rampdown_sizer, -100, 0)
|
||||
self.add_profile_item("ramp down max (°C/s)", self.rampdown_sizer, -100, 0)
|
||||
|
||||
self.add_profile_item("time max (s)", 0, 800)
|
||||
|
||||
self.box = wx.StaticBox(self.panel, -1, "Profile Settings")
|
||||
self.bsizer = wx.StaticBoxSizer(self.box, wx.VERTICAL)
|
||||
self.bsizer.Add(self.profile_sizer, 0, flag=wx.ALL, border=5)
|
||||
self.hbox1.Add(self.bsizer, border=5, flag=wx.ALL | wx.ALIGN_TOP)
|
||||
|
||||
def init_oven_status(self):
|
||||
self.oven_status_sizer = wx.GridBagSizer(5, 5)
|
||||
|
||||
#set_min = 0;
|
||||
#set_max = 0;
|
||||
#set_dt_min = 0;
|
||||
#set_dt_max = 0;
|
||||
|
||||
self.oven_status_sizer.Add(wx.StaticText(self.panel, -1, "Connected"), (0, 0))
|
||||
self.oven_connected = wx.StaticText(self.panel, -1, str(self.datagen.connected()))
|
||||
self.oven_status_sizer.Add(self.oven_connected, (0, 1))
|
||||
|
||||
self.oven_status_sizer.Add(wx.StaticText(self.panel, -1, "Temperature"), (1, 0))
|
||||
self.temperature = wx.TextCtrl(self.panel, -1, str(Arduino_Monitor.status[1]))
|
||||
self.oven_status_sizer.Add(self.temperature, (1, 1))
|
||||
|
||||
self.oven_status_sizer.Add(wx.StaticText(self.panel, -1, "Time"), (2, 0))
|
||||
self.time = wx.TextCtrl(self.panel, -1, str(Arduino_Monitor.status[0]))
|
||||
self.oven_status_sizer.Add(self.time, (2, 1))
|
||||
|
||||
|
||||
self.oven_status_sizer.Add(wx.StaticText(self.panel, -1, "State"), (3, 0))
|
||||
self.pstate = wx.TextCtrl(self.panel, -1, str(Arduino_Monitor.status[3]))
|
||||
self.oven_status_sizer.Add(self.pstate, (3, 1))
|
||||
|
||||
self.oven_status_sizer.Add(wx.StaticText(self.panel, -1, "Error"), (4, 0))
|
||||
self.perror = wx.TextCtrl(self.panel, -1, str(Arduino_Monitor.status[4]))
|
||||
self.oven_status_sizer.Add(self.perror, (4, 1))
|
||||
|
||||
self.oven_status_sizer.Add(wx.StaticText(self.panel, -1, "Heating"), (5, 0))
|
||||
self.is_oven_heating = wx.TextCtrl(self.panel, -1, str(Arduino_Monitor.status[5]))
|
||||
self.oven_status_sizer.Add(self.is_oven_heating, (5, 1))
|
||||
|
||||
self.obox = wx.StaticBox(self.panel, -1, "Oven status")
|
||||
self.osizer = wx.StaticBoxSizer(self.obox, wx.VERTICAL)
|
||||
self.osizer.Add(self.oven_status_sizer, 0, flag=wx.ALL, border=5)
|
||||
self.hbox1.Add(self.osizer, border=5, flag=wx.ALL | wx.ALIGN_TOP)
|
||||
|
||||
|
||||
def init_log(self):
|
||||
self.log_sizer = wx.GridBagSizer(5, 5)
|
||||
|
||||
self.log_sizer.Add(wx.StaticText(self.panel, -1, "Ts_time_start"), (0, 0))
|
||||
self.ts_time_start = wx.TextCtrl(self.panel, -1)
|
||||
self.log_sizer.Add(self.ts_time_start, (0, 1))
|
||||
|
||||
self.log_sizer.Add(wx.StaticText(self.panel, -1, "Ts_time_end"), (1, 0))
|
||||
self.ts_time_end = wx.TextCtrl(self.panel, -1)
|
||||
self.log_sizer.Add(self.ts_time_end, (1, 1))
|
||||
|
||||
self.log_sizer.Add(wx.StaticText(self.panel, -1, "Tl_time_start"), (2, 0))
|
||||
self.tl_time_start = wx.TextCtrl(self.panel, -1)
|
||||
self.log_sizer.Add(self.tl_time_start, (2, 1))
|
||||
|
||||
self.log_sizer.Add(wx.StaticText(self.panel, -1, "Tl_time_end"), (3, 0))
|
||||
self.tl_time_end = wx.TextCtrl(self.panel, -1)
|
||||
self.log_sizer.Add(self.tl_time_end, (3, 1))
|
||||
|
||||
|
||||
self.log_sizer.Add(wx.StaticText(self.panel, -1, "Tp_time_start"), (4, 0))
|
||||
self.tp_time_start = wx.TextCtrl(self.panel, -1)
|
||||
self.log_sizer.Add(self.tp_time_start, (4, 1))
|
||||
|
||||
self.log_sizer.Add(wx.StaticText(self.panel, -1, "Tp_time_end"), (5, 0))
|
||||
self.tp_time_end = wx.TextCtrl(self.panel, -1)
|
||||
self.log_sizer.Add(self.tp_time_end, (5, 1))
|
||||
|
||||
self.lbox = wx.StaticBox(self.panel, -1, "Profile Log")
|
||||
self.lsizer = wx.StaticBoxSizer(self.lbox, wx.VERTICAL)
|
||||
self.lsizer.Add(self.log_sizer, 0, flag=wx.ALL, border=5)
|
||||
self.hbox1.Add(self.lsizer, border=5, flag=wx.ALL | wx.ALIGN_TOP)
|
||||
|
||||
def create_status_bar(self):
|
||||
self.statusbar = self.CreateStatusBar()
|
||||
|
||||
def init_plot(self):
|
||||
self.dpi = 100
|
||||
self.fig = Figure((4.0, 4.0), dpi=self.dpi)
|
||||
|
||||
self.axes = self.fig.add_subplot(111)
|
||||
self.axes.set_axis_bgcolor('black')
|
||||
self.axes.set_title(u'Reflow Temperature', size=12)
|
||||
self.axes.set_xlabel(u'Time / seconds', size=12)
|
||||
self.axes.set_ylabel(u'Temperature (°C)', size=12)
|
||||
|
||||
pylab.setp(self.axes.get_xticklabels(), fontsize=8)
|
||||
pylab.setp(self.axes.get_yticklabels(), fontsize=8)
|
||||
|
||||
# no 1
|
||||
ts_min_x_min = Arduino_Monitor.profile[Arduino_Monitor.PI_TS_MIN] / Arduino_Monitor.profile[Arduino_Monitor.PI_RAMP_UP_MAX]
|
||||
ts_min_y_min = Arduino_Monitor.profile[Arduino_Monitor.PI_TS_MIN]
|
||||
|
||||
# no 2
|
||||
ts_max_x_min = ts_min_x_min + Arduino_Monitor.profile[Arduino_Monitor.PI_TS_DURATION_MIN]
|
||||
ts_max_y_min = Arduino_Monitor.profile[Arduino_Monitor.PI_TS_MAX]
|
||||
|
||||
# no t1
|
||||
ts_max_x_max = ts_min_x_min + Arduino_Monitor.profile[Arduino_Monitor.PI_TS_DURATION_MAX]
|
||||
ts_max_y_max = Arduino_Monitor.profile[Arduino_Monitor.PI_TS_MAX]
|
||||
|
||||
# no t2
|
||||
ts_min_x_max = ts_max_x_max - (ts_max_y_max - ts_min_y_min) / Arduino_Monitor.profile[Arduino_Monitor.PI_RAMP_UP_MAX]
|
||||
ts_min_y_max = Arduino_Monitor.profile[Arduino_Monitor.PI_TS_MIN]
|
||||
|
||||
# no 10
|
||||
t0_x_max = ts_min_x_max - (ts_max_x_max / Arduino_Monitor.profile[Arduino_Monitor.PI_RAMP_UP_MAX])
|
||||
t0_y_max = 0
|
||||
|
||||
# no 4
|
||||
tp_x_min = ts_max_x_min + (Arduino_Monitor.profile[Arduino_Monitor.PI_TP] - ts_max_y_min) / Arduino_Monitor.profile[Arduino_Monitor.PI_RAMP_UP_MAX]
|
||||
tp_y_min = Arduino_Monitor.profile[Arduino_Monitor.PI_TP]
|
||||
|
||||
# no 5
|
||||
tp_x_max = tp_x_min + Arduino_Monitor.profile[Arduino_Monitor.PI_TP_DURATION_MAX]
|
||||
tp_y_max = tp_y_min
|
||||
|
||||
# no 8
|
||||
tp5_x_max = tp_x_min + Arduino_Monitor.profile[Arduino_Monitor.PI_TP_DURATION_MIN]
|
||||
tp5_y_max = tp_y_max - 5
|
||||
|
||||
# no 9
|
||||
tp5_x_min = ts_max_x_max + (tp5_y_max - ts_max_y_max) / Arduino_Monitor.profile[Arduino_Monitor.PI_RAMP_UP_MAX]
|
||||
tp5_y_min = tp5_y_max
|
||||
|
||||
# no 6
|
||||
end_x_max = tp_x_max + tp_y_max / abs(Arduino_Monitor.profile[Arduino_Monitor.PI_RAMP_DOWN_MIN])
|
||||
end_y_max = 0
|
||||
|
||||
self.xmax = end_x_max + 20
|
||||
self.ymax = Arduino_Monitor.profile[Arduino_Monitor.PI_TP] + 20
|
||||
|
||||
# no 7
|
||||
end_x_min = tp5_x_max + tp5_y_max / abs(Arduino_Monitor.profile[Arduino_Monitor.PI_RAMP_DOWN_MAX])
|
||||
end_y_min = 0
|
||||
|
||||
tsmin = Arduino_Monitor.profile[Arduino_Monitor.PI_TS_MIN]
|
||||
tsmax = Arduino_Monitor.profile[Arduino_Monitor.PI_TS_MAX]
|
||||
tl = Arduino_Monitor.profile[Arduino_Monitor.PI_TL]
|
||||
tp = Arduino_Monitor.profile[Arduino_Monitor.PI_TP]
|
||||
self.ts_line_min = matplotlib.lines.Line2D([0, self.xmax], [tsmin, tsmin],
|
||||
transform=self.axes.transData, figure=self.fig, color='green')
|
||||
self.ts_line_max = matplotlib.lines.Line2D([0, self.xmax], [tsmax, tsmax],
|
||||
transform=self.axes.transData, figure=self.fig, label="Ts_max", color='lightgreen')
|
||||
self.tl_line = matplotlib.lines.Line2D([0, self.xmax], [tl, tl],
|
||||
transform=self.axes.transData, figure=self.fig, label="Tl", color='yellow')
|
||||
self.tp_line = matplotlib.lines.Line2D([0, self.xmax], [tp, tp],
|
||||
transform=self.axes.transData, figure=self.fig, label="Tp", color='blue')
|
||||
|
||||
self.ts_line_min.set_label("Ts_min")
|
||||
self.ts_line_min.set_label("Ts_max")
|
||||
self.tl_line.set_label("Tl")
|
||||
self.tp_line.set_label("Tp")
|
||||
self.fig.lines.extend([self.ts_line_min, self.ts_line_max, self.tl_line, self.tp_line])
|
||||
|
||||
verts = [
|
||||
[0.0, 0.0],
|
||||
[ts_min_x_min, ts_min_y_min],
|
||||
[ts_max_x_min, ts_max_y_min],
|
||||
[ts_max_x_max, ts_max_y_max],
|
||||
[ts_min_x_max, ts_min_y_max],
|
||||
#[tp_x_min, tp_y_min],
|
||||
#[tp_x_max, tp_y_max],
|
||||
#[end_x_max, end_y_max],
|
||||
#[end_x_min, end_y_min],
|
||||
#[tp5_x_max, tp5_y_max],
|
||||
#[tp5_x_min, tp5_y_min],
|
||||
[t0_x_max, t0_y_max],
|
||||
[0.0, 0.0]]
|
||||
|
||||
codes = [
|
||||
Path.MOVETO,
|
||||
Path.LINETO,
|
||||
Path.LINETO,
|
||||
Path.LINETO,
|
||||
Path.LINETO,
|
||||
Path.LINETO,
|
||||
#Path.LINETO,
|
||||
#Path.LINETO,
|
||||
#Path.LINETO,
|
||||
#Path.LINETO,
|
||||
Path.CLOSEPOLY]
|
||||
|
||||
self.plot_data = self.axes.plot(
|
||||
self.data,
|
||||
linewidth=1,
|
||||
color=(1, 1, 0),
|
||||
)[0]
|
||||
|
||||
print "verts", verts
|
||||
|
||||
|
||||
path = Path(verts, codes)
|
||||
self.patch = patches.PathPatch(path, edgecolor="red", facecolor='orange', lw=2)
|
||||
self.axes.add_patch(self.patch)
|
||||
self.axes.legend( (self.ts_line_min, self.ts_line_max, self.tl_line, self.tp_line), ('Ts_min', 'Ts_max', 'Tl', 'Tp'), loc=2)
|
||||
|
||||
def draw_plot(self):
|
||||
""" Redraws the plot
|
||||
"""
|
||||
|
||||
self.axes.set_xbound(lower=0, upper=self.xmax)
|
||||
self.axes.set_ybound(lower=0, upper=self.ymax)
|
||||
|
||||
self.axes.grid(True, color='gray')
|
||||
|
||||
pylab.setp(self.axes.get_xticklabels(), visible=True)
|
||||
|
||||
self.plot_data.set_xdata(np.arange(len(self.data)))
|
||||
self.plot_data.set_ydata(np.array(self.data))
|
||||
|
||||
self.canvas.draw()
|
||||
|
||||
def update_config(self):
|
||||
for ix, i in enumerate(self.profile):
|
||||
i.SetValue(str(Arduino_Monitor.profile[i]))
|
||||
|
||||
def update_state(self):
|
||||
if Arduino_Monitor.status[3] > 0:
|
||||
self.started = True
|
||||
|
||||
self.time.SetValue(str(Arduino_Monitor.status[0]))
|
||||
self.temperature.SetValue(str(Arduino_Monitor.status[1]))
|
||||
self.pstate.SetValue(str(Arduino_Monitor.status[3]))
|
||||
self.perror.SetValue(str(Arduino_Monitor.status[4]))
|
||||
self.is_oven_heating.SetValue(str(Arduino_Monitor.status[5]))
|
||||
|
||||
|
||||
def on_start_button(self, event):
|
||||
self.started = self.datagen.send_start()
|
||||
self.recv_config_button.Disable()
|
||||
self.send_button.Disable()
|
||||
self.profile = []
|
||||
for i in range(30):
|
||||
self.profile_sizer.Remove(i)
|
||||
self.profile_sizer.Layout()
|
||||
|
||||
def on_recv_config_button(self, event):
|
||||
if not self.started:
|
||||
self.datagen.recv_config()
|
||||
|
||||
|
||||
def on_send_button(self, event):
|
||||
if not self.started:
|
||||
self.datagen.send_config()
|
||||
|
||||
def on_save_plot(self, event):
|
||||
file_choices = "PNG (*.png)|*.png"
|
||||
|
||||
dlg = wx.FileDialog(
|
||||
self,
|
||||
message="Save plot as...",
|
||||
defaultDir=os.getcwd(),
|
||||
defaultFile="plot.png",
|
||||
wildcard=file_choices,
|
||||
style=wx.SAVE)
|
||||
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
path = dlg.GetPath()
|
||||
self.canvas.print_figure(path, dpi=self.dpi)
|
||||
self.flash_status_message("Saved to %s" % path)
|
||||
|
||||
def on_redraw_timer(self, event):
|
||||
|
||||
if self.started:
|
||||
self.data.append(self.datagen.next())
|
||||
|
||||
self.update_state()
|
||||
self.draw_plot()
|
||||
|
||||
def on_exit(self, event):
|
||||
self.Destroy()
|
||||
|
||||
def flash_status_message(self, msg, flash_len_ms=1500):
|
||||
self.statusbar.SetStatusText(msg)
|
||||
self.timeroff = wx.Timer(self)
|
||||
self.Bind(
|
||||
wx.EVT_TIMER,
|
||||
self.on_flash_status_off,
|
||||
self.timeroff)
|
||||
self.timeroff.Start(flash_len_ms, oneShot=True)
|
||||
|
||||
def on_flash_status_off(self, event):
|
||||
self.statusbar.SetStatusText('')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = wx.PySimpleApp()
|
||||
app.frame = GraphFrame()
|
||||
app.frame.Show()
|
||||
app.MainLoop()
|
||||
|
|
@ -65,12 +65,16 @@ class Plotter(FigureCanvas):
|
|||
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
|
||||
self.fig.canvas.mpl_connect('pick_event', self.onpick)
|
||||
self.update_figure()
|
||||
self.started = False
|
||||
|
||||
def start_timer(self):
|
||||
timer = QtCore.QTimer(self)
|
||||
QtCore.QObject.connect(timer, QtCore.SIGNAL("timeout()"), self.update_figure)
|
||||
timer.start(1000)
|
||||
|
||||
|
||||
def onpick(self, event):
|
||||
if isinstance(event.artist, Line2D):
|
||||
|
@ -145,6 +149,8 @@ class Plotter(FigureCanvas):
|
|||
def solder_changed(self):
|
||||
self.solder.setChanged()
|
||||
self.updated = True
|
||||
if not self.started:
|
||||
self.update_figure()
|
||||
|
||||
def setData(self, solder):
|
||||
self.solder = solder
|
||||
|
@ -252,7 +258,7 @@ class ApplicationWindow(QtGui.QMainWindow):
|
|||
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.show_report,
|
||||
self.file_menu.addAction('&Show Report', self.show_report,
|
||||
QtCore.Qt.CTRL + QtCore.Qt.Key_T)
|
||||
self.file_menu.addAction('&Quit', self.fileQuit,
|
||||
QtCore.Qt.CTRL + QtCore.Qt.Key_Q)
|
||||
|
@ -313,6 +319,8 @@ class ApplicationWindow(QtGui.QMainWindow):
|
|||
QtCore.SIGNAL("solder_changed()"),
|
||||
self.plotter.solder_changed)
|
||||
|
||||
self.temp_level_widget.temp_level_added.connect(self.constraint_widget.temp_level_added)
|
||||
|
||||
self.connect(
|
||||
self.constraint_widget,
|
||||
QtCore.SIGNAL("edge_changed()"),
|
||||
|
@ -366,11 +374,11 @@ class ApplicationWindow(QtGui.QMainWindow):
|
|||
self.setCentralWidget(self.splitter)
|
||||
|
||||
self.statusBar().showMessage("Reflow GORE!1!", 10000)
|
||||
self.plotter.solder_changed()
|
||||
|
||||
def getPlotter(self):
|
||||
return self.plotter
|
||||
|
||||
|
||||
def solder_selected(self, index):
|
||||
if index.isValid():
|
||||
solder = self.solder_widget.solder_model.solder_list[index.row()]
|
||||
|
@ -382,6 +390,8 @@ class ApplicationWindow(QtGui.QMainWindow):
|
|||
def edge_picked(self, ix):
|
||||
if self.tab_widget.currentIndex() != 1:
|
||||
self.tab_widget.setCurrentIndex(1)
|
||||
if not self.plotter.started:
|
||||
self.plotter.update_figure()
|
||||
|
||||
def remove_solder(self):
|
||||
self.solder_selected(self.solder_widget.remove_solder())
|
||||
|
@ -433,12 +443,6 @@ class ApplicationWindow(QtGui.QMainWindow):
|
|||
|
||||
def main():
|
||||
|
||||
# numpy bug workaround
|
||||
try:
|
||||
numpy.log10(0.0)
|
||||
except:
|
||||
pass
|
||||
|
||||
qApp = QtGui.QApplication(sys.argv)
|
||||
|
||||
aw = ApplicationWindow()
|
||||
|
|
|
@ -1,81 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import serial, struct, time
|
||||
|
||||
ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=2)
|
||||
|
||||
|
||||
buf = ""
|
||||
alles = []
|
||||
|
||||
#def parse():
|
||||
#buffer = list()
|
||||
#while 1:
|
||||
#try:
|
||||
#i = ser.read(1)
|
||||
#if ord(i) == 255:
|
||||
#except Exception, e:
|
||||
#print e
|
||||
#else:
|
||||
|
||||
def recv_config():
|
||||
ser.write(chr(255))
|
||||
ser.flush()
|
||||
read(30)
|
||||
ser.flushInput()
|
||||
data = struct.unpack("hhhhhhhhhhhhhhh", buf)
|
||||
print
|
||||
print "Profile:"
|
||||
print "ts_min:", data[0]
|
||||
print "ts_max:", data[1]
|
||||
print "tl:", data[2]
|
||||
print "tp:", data[3]
|
||||
print "time_max:", data[4]
|
||||
print "ramp_up_min:", data[5]
|
||||
print "ramp_up_max:", data[6]
|
||||
print "ramp_down_min:", data[7]
|
||||
print "ramp_down_max:", data[8]
|
||||
|
||||
print "ts_duration_min:", data[9]
|
||||
print "ts_duration_max:", data[10]
|
||||
print "tl_duration_min:", data[11]
|
||||
print "tl_duration_max:", data[12]
|
||||
print "tp_duration_min:", data[13]
|
||||
print "tp_duration_max:", data[14]
|
||||
print
|
||||
|
||||
|
||||
def recv_state():
|
||||
ser.write(chr(254))
|
||||
ser.flush()
|
||||
read(11)
|
||||
data = struct.unpack("hhhhhb", buf)
|
||||
print "time: %ds, temperature: %d°C, last temperature: %d°C, state: %d, error condition: %d, heating: %d" % data
|
||||
|
||||
|
||||
def send_config():
|
||||
ser.write(chr(253))
|
||||
ser.write(buf)
|
||||
ser.flushInput()
|
||||
|
||||
|
||||
def read(l):
|
||||
global buf
|
||||
global alles
|
||||
buf = ""
|
||||
while len(buf) < l:
|
||||
try:
|
||||
buf += ser.read(l)
|
||||
alles.append(buf)
|
||||
except Exception, e:
|
||||
print e
|
||||
ser.flushInput()
|
||||
|
||||
|
||||
time.sleep(2)
|
||||
recv_config()
|
||||
while 1:
|
||||
recv_state()
|
||||
time.sleep(1)
|
||||
|
|
@ -0,0 +1,259 @@
|
|||
|
||||
|
||||
import os
|
||||
import os.path
|
||||
|
||||
import xml.etree.ElementTree as etree
|
||||
|
||||
from PyQt4 import QtGui, QtCore
|
||||
|
||||
from numpy import arange, sin, pi, array, linspace, arange
|
||||
|
||||
from temp_level import TempLevel, set_colors
|
||||
from edge_widget import Edge
|
||||
from control_widgets import AddRemoveWidget
|
||||
|
||||
|
||||
def getTemperature():
|
||||
return 20.
|
||||
|
||||
|
||||
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.changed = False
|
||||
self.filename = filename
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.temp_levels = list()
|
||||
self.edges = list()
|
||||
|
||||
|
||||
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 get_temp_level_by_name(self, name):
|
||||
assert isinstance(name, basestring)
|
||||
for i in self.temp_levels:
|
||||
if i.name == name:
|
||||
return i
|
||||
return None
|
||||
|
||||
#def get_temp_level(self, ix):
|
||||
#assert isinstance(ix, int)
|
||||
#return self.temp_levels[ix]
|
||||
|
||||
|
||||
def calc_profile(self):
|
||||
print self.calc_profile
|
||||
self.log = list()
|
||||
x = list()
|
||||
y = list()
|
||||
duration_points = dict()
|
||||
rate_points = dict()
|
||||
time = 0
|
||||
|
||||
def calc(edge):
|
||||
if edge.duration:
|
||||
return time + edge.duration
|
||||
elif edge.rate:
|
||||
return time + (edge.to_tl.temp - edge.from_tl.temp) / edge.rate
|
||||
|
||||
for _edge in self.edges:
|
||||
x.append(time)
|
||||
y.append(_edge.from_tl.temp)
|
||||
time = calc(_edge)
|
||||
|
||||
x.append(time)
|
||||
y.append(_edge.to_tl.temp)
|
||||
print "end", self.calc_profile
|
||||
return array(map(float, x)), array(map(float, y)), min(x), max(x), min(y), max(y), set(), set()
|
||||
|
||||
|
||||
@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 edge in solder_node.findall("edge"):
|
||||
from_tl = s.get_temp_level_by_name(edge.attrib["from"])
|
||||
to_tl = s.get_temp_level_by_name(edge.attrib["to"])
|
||||
duration = None
|
||||
rate = None
|
||||
try:
|
||||
duration = float(edge.attrib["duration"])
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
rate = float(edge.attrib["rate"])
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
e = Edge(from_tl, to_tl, duration, rate)
|
||||
s.edges.append(e)
|
||||
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 edge in self.edges:
|
||||
element = etree.Element("edge", {
|
||||
"from" : edge.from_tl.name,
|
||||
"to" : edge.to_tl.name,
|
||||
"duration" : str(edge.duration),
|
||||
"rate" : str(edge.rate)})
|
||||
solder_node.append(element)
|
||||
|
||||
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):
|
||||
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._load_solder_list()
|
||||
|
||||
def rowCount(self, parent=QtCore.QModelIndex()):
|
||||
return len(self.solder_list)
|
||||
|
||||
def _load_solder_list(self):
|
||||
dirname = self.dirname()
|
||||
dirlisting = filter(lambda x: os.path.splitext(x)[1] == ".xml", os.listdir(dirname))
|
||||
self.solder_list = []
|
||||
for p in dirlisting:
|
||||
self.solder_list.append(Solder.unpack(os.path.join(dirname, p), self))
|
||||
self.solder_list.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.solder_list[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.solder_list[index.row()].name = new_name
|
||||
self.solder_list[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.solder_list.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.solder_list:
|
||||
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)
|
||||
|
||||
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.solder_list[index.row()]
|
||||
try:
|
||||
os.remove(solder.filename)
|
||||
except OSError:
|
||||
pass
|
||||
del self.solder_model.solder_list[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.solder_list[index.row()].save()
|
||||
self.solder_model.reset()
|
||||
new_index = self.solder_model.index(self.solder_model.solder_list.index(self.plotter.solder))
|
||||
self.solder_widget.solder_view.setCurrentIndex(new_index)
|
||||
|
|
@ -0,0 +1,278 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from PyQt4 import QtGui, QtCore
|
||||
from control_widgets import AddRemoveWidget
|
||||
|
||||
|
||||
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")
|
||||
|
||||
|
||||
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 "<TempLevel: name=%r, temp=%r>" % (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]
|
||||
|
||||
|
||||
def set_next(self, temp_level):
|
||||
if temp_level is self:
|
||||
raise ValueError("same temp_level")
|
||||
self.next = temp_level
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return "TempLevel(%r, %r, %r)" % (self.name, self.temp, self.is_env)
|
||||
|
||||
|
||||
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
|
||||
|
||||
if index.row() != 0:
|
||||
return QtCore.Qt.ItemFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable)
|
||||
else:
|
||||
return QtCore.Qt.ItemFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable)
|
||||
|
||||
|
||||
def neigbors(self, temp_level):
|
||||
ix = self.temp_levels.index(temp_level)
|
||||
return self.temp_levels[ix-1], self.temp_levels[ix+1]
|
||||
|
||||
|
||||
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:
|
||||
temp, res = variant.toInt()
|
||||
tl = self.temp_levels[index.row()]
|
||||
try:
|
||||
tl0 = self.temp_levels[index.row() - 1]
|
||||
if tl0.temp >= temp:
|
||||
return False
|
||||
except Exception, e:
|
||||
pass
|
||||
try:
|
||||
tl1 = self.temp_levels[index.row() + 1]
|
||||
if tl1.temp <= temp:
|
||||
return False
|
||||
except Exception, e:
|
||||
pass
|
||||
tl.temp = temp
|
||||
|
||||
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()
|
||||
|
||||
|
||||
def get_temp_level(self, ix):
|
||||
return self.temp_levels[ix]
|
||||
|
||||
|
||||
class TempLevelWidget(QtGui.QWidget):
|
||||
temp_level_removed = QtCore.pyqtSignal(TempLevel)
|
||||
temp_level_added = QtCore.pyqtSignal(TempLevel, 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.temp_level_model,
|
||||
QtCore.SIGNAL("solder_changed()"),
|
||||
self._solder_changed)
|
||||
|
||||
def _solder_changed(self):
|
||||
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()
|
||||
old_tl = self.temp_level_model.temp_levels[index.row()]
|
||||
print "next temp", self.temp_level_model.temp_levels[index.row() + 1].temp
|
||||
new_temp = old_tl.temp + (self.temp_level_model.temp_levels[index.row() + 1].temp - old_tl.temp) / 2
|
||||
print "new_temp", new_temp
|
||||
new_tl = TempLevel("new " + str(self.temp_level_model.rowCount(None)), new_temp)
|
||||
self.temp_level_model.add_temp_level(index, new_tl)
|
||||
self.temp_level_view.setCurrentIndex(self.temp_level_model.index(index.row() + 1, 0))
|
||||
self.temp_levels_changed.emit()
|
||||
print "TempLevelWidget.add_temp_level 1", old_tl, new_tl
|
||||
self.temp_level_added.emit(old_tl, new_tl)
|
||||
print "TempLevelWidget.add_temp_level 2"
|
||||
|
||||
|
||||
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_selected(self, index):
|
||||
if index.isValid():
|
||||
row = index.row()
|
||||
if not self.readonly:
|
||||
self.controls.remove_button.setEnabled(not self.temp_level_model.temp_levels[row].is_env)
|
Loading…
Reference in New Issue