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)
|
QtGui.QSizePolicy.Expanding)
|
||||||
FigureCanvas.updateGeometry(self)
|
FigureCanvas.updateGeometry(self)
|
||||||
|
|
||||||
timer = QtCore.QTimer(self)
|
|
||||||
|
|
||||||
QtCore.QObject.connect(timer, QtCore.SIGNAL("timeout()"), self.update_figure)
|
|
||||||
timer.start(1000)
|
|
||||||
self.updated = False
|
self.updated = False
|
||||||
self.fig.canvas.mpl_connect('pick_event', self.onpick)
|
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):
|
def onpick(self, event):
|
||||||
if isinstance(event.artist, Line2D):
|
if isinstance(event.artist, Line2D):
|
||||||
|
@ -145,6 +149,8 @@ class Plotter(FigureCanvas):
|
||||||
def solder_changed(self):
|
def solder_changed(self):
|
||||||
self.solder.setChanged()
|
self.solder.setChanged()
|
||||||
self.updated = True
|
self.updated = True
|
||||||
|
if not self.started:
|
||||||
|
self.update_figure()
|
||||||
|
|
||||||
def setData(self, solder):
|
def setData(self, solder):
|
||||||
self.solder = solder
|
self.solder = solder
|
||||||
|
@ -252,7 +258,7 @@ class ApplicationWindow(QtGui.QMainWindow):
|
||||||
QtCore.Qt.CTRL + QtCore.Qt.Key_D)
|
QtCore.Qt.CTRL + QtCore.Qt.Key_D)
|
||||||
self.file_menu.addAction('S&ave plot', self.save_plot,
|
self.file_menu.addAction('S&ave plot', self.save_plot,
|
||||||
QtCore.Qt.CTRL + QtCore.Qt.Key_A)
|
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)
|
QtCore.Qt.CTRL + QtCore.Qt.Key_T)
|
||||||
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)
|
||||||
|
@ -313,6 +319,8 @@ class ApplicationWindow(QtGui.QMainWindow):
|
||||||
QtCore.SIGNAL("solder_changed()"),
|
QtCore.SIGNAL("solder_changed()"),
|
||||||
self.plotter.solder_changed)
|
self.plotter.solder_changed)
|
||||||
|
|
||||||
|
self.temp_level_widget.temp_level_added.connect(self.constraint_widget.temp_level_added)
|
||||||
|
|
||||||
self.connect(
|
self.connect(
|
||||||
self.constraint_widget,
|
self.constraint_widget,
|
||||||
QtCore.SIGNAL("edge_changed()"),
|
QtCore.SIGNAL("edge_changed()"),
|
||||||
|
@ -366,11 +374,11 @@ class ApplicationWindow(QtGui.QMainWindow):
|
||||||
self.setCentralWidget(self.splitter)
|
self.setCentralWidget(self.splitter)
|
||||||
|
|
||||||
self.statusBar().showMessage("Reflow GORE!1!", 10000)
|
self.statusBar().showMessage("Reflow GORE!1!", 10000)
|
||||||
|
self.plotter.solder_changed()
|
||||||
|
|
||||||
def getPlotter(self):
|
def getPlotter(self):
|
||||||
return self.plotter
|
return self.plotter
|
||||||
|
|
||||||
|
|
||||||
def solder_selected(self, index):
|
def solder_selected(self, index):
|
||||||
if index.isValid():
|
if index.isValid():
|
||||||
solder = self.solder_widget.solder_model.solder_list[index.row()]
|
solder = self.solder_widget.solder_model.solder_list[index.row()]
|
||||||
|
@ -382,6 +390,8 @@ class ApplicationWindow(QtGui.QMainWindow):
|
||||||
def edge_picked(self, ix):
|
def edge_picked(self, ix):
|
||||||
if self.tab_widget.currentIndex() != 1:
|
if self.tab_widget.currentIndex() != 1:
|
||||||
self.tab_widget.setCurrentIndex(1)
|
self.tab_widget.setCurrentIndex(1)
|
||||||
|
if not self.plotter.started:
|
||||||
|
self.plotter.update_figure()
|
||||||
|
|
||||||
def remove_solder(self):
|
def remove_solder(self):
|
||||||
self.solder_selected(self.solder_widget.remove_solder())
|
self.solder_selected(self.solder_widget.remove_solder())
|
||||||
|
@ -433,12 +443,6 @@ class ApplicationWindow(QtGui.QMainWindow):
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|
||||||
# numpy bug workaround
|
|
||||||
try:
|
|
||||||
numpy.log10(0.0)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
qApp = QtGui.QApplication(sys.argv)
|
qApp = QtGui.QApplication(sys.argv)
|
||||||
|
|
||||||
aw = ApplicationWindow()
|
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