Updated Question
I think my original quandary might be a result of the structure of my PyQt app. The way I've approached creating a GUI is to divide the larger widget into smaller pieces, each given their own class until the parts are simple enough. Because of this, I end up with a ton of nesting, as a large widget holds instances of smaller widgets, and those hold their own even smaller widgets. It makes it hard to navigate data around the app.
How should a PyQt app be structured so that it is simple to understand in code and yet has a structure containing very little nesting? I haven't found many examples of this around, so I'm sort of stuck. The code example in my original question shows a pretty good example of the structure I'm currently using, which has a large amount of nesting.
Info on program
The GUI is used to create a set of parameters for running a test. The options in each setting should correspond to a binary number, and all of the binary numbers indicated by each set of options are collected, formed into a single sequence of binary numbers, and passed on. Changes to settings do not have to be carried over between sessions, as each new session will most likely correspond to a new test (and thus a new set of choices for settings).
The basic flow of the app should be that upon opening it, all available settings (about 20 total) are set to their default values. A user can go through and change whatever settings they would like, and once they're done they can press a "Generate" button to gather all of the binary numbers corresponding to the settings and create the command. It would be very helpful to have a live preview of individual bits that updates as settings are changed, which is why updates must be immediate.
Some settings are dependent on other; for instance, Setting A has 4 options, and if option 3 is selected, Setting B should be made visible, otherwise it is invisible.
Original Question
I'm definitely a beginner to PyQt, so I don't quite know if I've worded my question correctly, but here goes. I've got a GUI wherein I'm attempting to take a bunch of different settings, keep track of what number was selected from each setting, and then pass the number up to an object that keeps track of all of the numbers from all of the settings. The trouble is that I don't know the best way to get all the individual settings values up my tree of classes, so to speak. Here's the structure of my GUI so far:
Bottom: individual custom QWidgets, each responsible for a single setting. Each has a signal that fires whenever the value it returns changes.
Middle: a QWidget containing ~7-10 individual settings each. These collect settings into related groups.
Top: a QTabWidget that places each instance of a setting group into an individual tab. This widget also contains an object that should ideally collect all of the settings from individual groups into it.
My question is how do I get the values from the bottom layer signals to the top layer widget? My only idea is to connect all of the signals from those small setting widgets to a signal in the middle layer, and connect the middle layer signal to something in the top layer. This sort of chaining seems crazy, though.
I'm running PyQt5 and Python 3.7.
Here's some stripped down code which hopefully shows what I want to do.
class TabWindow(QTabWidget):
def __init__(self):
super().__init__()
self.tabs = [SettingsGroup1, SettingsGroup2, SettingsGroup3]
self.setting_storage = { # dictionary is where I'd like to store all settings values
# 'setting name': setting value
}
for tab in self.tabs:
self.addTab(tab, 'Example')
class SettingsGroup(QWidget):
def __init__(self):
super().__init__()
# not shown: layout created for widget
self.settings = []
def add_to_group(self, new_setting):
self.settings.append(new_setting)
# not shown: add setting to the layout
class SettingsGroup1(SettingsGroup):
def __init__(self):
super().__init__()
self.add_to_group([Setting1, Setting2, Setting3])
class SettingsGroup2(SettingsGroup):...
class SettingsGroup3(SettingsGroup):...
class Setting(QWidget):
val_signal = pyqtSignal([int], name='valChanged')
def __init__(self, name):
self.val = None
self.name = name
def set_val(self, new_val):
self.val = new_val
self.val_signal.emit(self.val) # <-- the signal I want to pass up
class Setting1(Setting):
def __init__(self, name):
super().__init__(name)
# not shown: create custom setting layout/interface
class Setting2(Setting):...
class Setting3(Setting):...
I use a lot of inheritance (SettingsGroup -> SettingsGroup1, 2, 3) because each subclass will have its own functions and internal dependencies that are unique to it. For each Setting subclass, for instance, there is a different user interface.
Thanks for any help provided!
EDIT: The question has been updated in the meantime, I've added a solution that's more specific at the bottom of this answer.
I feel like this question is slightly "opinion based", but since I've had my share of similar situations I'd like to propose my suggestions. In these situations it's important to understand that there's not one good way to do things, but many ways to do it wrong.
Original answer
An idea could be to create a common signal interface for every "level", which will get that signal and send it back to its parent by adding its own name to keep track of the setting "path"; the topmost widget will then evaluate the changes accordingly.
In this example every tab "group" has its own valueChanged signal, which includes the group name, setting name and value; the source signal is fired from the "source" (a spinbox, in this case), then it follows its parents which, in turn "add" their name in turn.
Keep in mind that you can also just use a generalized pyqtSignal(object) for every parent and connect it with widget.valueChanged.connect(self.valueChanged), and then track its group and setting by walking by self.sender() parents backwards.
As a final notice, if you are using these values for application settings, remember that Qt already provides the QSettings API, which can be used as a common and OS-transparent interface for every configuration you need to set (and remember between sessions) in your application. I implemented it in the example, but I suggest you to read its documentation to better understand how it works.
import sys
from PyQt5 import QtCore, QtWidgets
class SettingWidget(QtWidgets.QWidget):
valueChanged = QtCore.pyqtSignal(int)
def __init__(self, name):
super().__init__()
self.settings = QtCore.QSettings()
self.val = 0
self.name = name
layout = QtWidgets.QVBoxLayout()
self.setLayout(layout)
layout.addWidget(QtWidgets.QLabel(self.name))
self.spinBox = QtWidgets.QSpinBox()
layout.addWidget(self.spinBox)
self.spinBox.valueChanged.connect(self.set_val)
def set_val(self, new_val):
if self.val != new_val:
self.val = new_val
self.valueChanged.emit(self.val)
# enter a setting group, ensuring that same name settings won't
# be mismatched; this allows a single sub level setting only
self.settings.beginGroup(self.parent().name)
self.settings.setValue(self.name, new_val)
# leave the setting group. THIS IS IMPORTANT!!!
self.settings.endGroup()
class SettingWidget1(SettingWidget):
def __init__(self):
super().__init__('Setting1')
class SettingWidget2(SettingWidget):
def __init__(self):
super().__init__('Setting2')
class SettingWidget3(SettingWidget):
def __init__(self):
super().__init__('Setting3')
class SettingsGroup(QtWidgets.QWidget):
# create two signal signatures, the first sends the full "path",
# while the last will just send the value
valueChanged = QtCore.pyqtSignal([str, str, int], [int])
def __init__(self, name):
super().__init__()
self.name = name
layout = QtWidgets.QHBoxLayout()
self.setLayout(layout)
def add_to_group(self, new_setting):
widget = new_setting()
# emit both signal signatures
widget.valueChanged.connect(
lambda value, name=widget.name: self.valueChanged.emit(
self.name, name, value))
widget.valueChanged.connect(self.valueChanged[int])
self.layout().addWidget(widget)
class SettingsGroup1(SettingsGroup):
def __init__(self):
super().__init__('Group1')
self.add_to_group(SettingWidget1)
self.add_to_group(SettingWidget2)
class SettingsGroup2(SettingsGroup):
def __init__(self):
super().__init__('Group2')
self.add_to_group(SettingWidget3)
class TabWidget(QtWidgets.QTabWidget):
def __init__(self):
QtWidgets.QTabWidget.__init__(self)
self.settings = QtCore.QSettings()
self.tabs = [SettingsGroup1, SettingsGroup2]
self.settingsDict = {}
for tab in self.tabs:
widget = tab()
self.addTab(widget, widget.__class__.__name__)
widget.valueChanged[str, str, int].connect(self.valueChangedFullPath)
widget.valueChanged[int].connect(self.valueChangedOnly)
def valueChangedFullPath(self, group, setting, value):
# update the settings dict; if the group key doesn't exist, create it
try:
self.settingsDict[group][setting] = value
except:
self.settingsDict[group] = {setting: value}
settingsData = [group, setting, value]
print('Full path result: {}'.format(settingsData))
# Apply setting from here, instead of using the SettingWidget
# settings.setValue() option; this allows a single sub level only
# self.applySetting(data)
def valueChangedOnly(self, value):
parent = sender = self.sender()
# sender() returns the last signal sender, so we need to track down its
# source; keep in mind that this is *not* a suggested approach, as
# tracking the source might result in recursion if the sender's sender
# is not one of its children; this system also has issues if you're
# using a Qt.DirectConnection from a thread different from the one that
# emitted it
while parent.sender() in sender.children():
parent = sender.sender()
widgetPath = []
while parent not in self.children():
widgetPath.insert(0, parent)
parent = parent.parent()
settingsData = [w.name for w in widgetPath] + [value]
print('Single value result: {}'.format(settingsData))
# similar to valueChangedFullPath(), but with this implementation more
# nested "levels" can be used instead
# self.applySetting(settingsData)
def applySetting(self, settingsData):
# walk up to the next to last of settingsData levels, assuming they are
# all parent group section names
for count, group in enumerate(settingsData[:-2], 1):
self.settings.beginGroup(group)
# set the setting name settingsData[-2] to its value settingsData[-1]
self.settings.setValue(*settingsData[-2:])
for g in range(count):
self.settings.endGroup()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
# set both Organization and Application name to make settings persistent
app.setOrganizationName('StackOverflow')
app.setApplicationName('Example')
w = TabWidget()
w.show()
sys.exit(app.exec_())
Alternate solution, based on updated answer
Since the answer has become more specific in its update, I'm adding another suggestion.
As far as we can understand now, you don't need that level of "nested" classes, but more specifically designed code that can be reused according to your purposes. Also, since you're using binary based data, it makes things a bit (pun intended) easier, as long as you know how bit operation works (which I assume you do) and the setting "widgets" don't require specific GUI customization.
In this example I created just one "setting" class and one "group" class, and their instancies are created only according to their names and default values.
import sys
from PyQt5 import QtCore, QtWidgets
defaultValues = '0010101', '1001010', '000111'
# set bit lengths for each setting; be careful in ensuring that each
# setting group has the full default value bit length!
groups = [
['Group 1', [1, 3, 2, 1]],
['Group 2', [1, 2, 2, 1, 1]],
['Group 1', [2, 1, 2, 1]],
]
class BinaryWidget(QtWidgets.QFrame):
changed = QtCore.pyqtSignal()
def __init__(self, name, index, defaults='0'):
QtWidgets.QFrame.__init__(self)
self.setFrameShape(self.StyledPanel|self.Sunken)
layout = QtWidgets.QGridLayout()
self.setLayout(layout)
self.index = index
self.defaults = defaults
self.buttons = []
# use the "defaults" length to create buttons
for i in range(len(defaults)):
value = int(defaults[i], 2) & 1
# I used QToolButtons as they're usually smaller than QPushButtons
btn = QtWidgets.QToolButton()
btn.setText(str(value))
layout.addWidget(btn, 1, i)
btn.setCheckable(True)
btn.setChecked(value)
btn.toggled.connect(self.changed)
# show the binary value on change, just for conveniency
btn.toggled.connect(lambda v, btn=btn: btn.setText(str(int(v))))
self.buttons.append(btn)
layout.addWidget(QtWidgets.QLabel(name), 0, 0, 1, layout.columnCount())
def value(self):
# return the correct value of all widget's buttons; they're reversed
# because of how bit shifting works
v = 0
for i, btn in enumerate(reversed(self.buttons)):
v += btn.isChecked() << i
# bit shift again, according to the actual "setting" bit index
return v << self.index
def resetValues(self):
oldValue = self.value()
self.blockSignals(True)
for i, value in enumerate(self.defaults):
self.buttons[i].setChecked(int(self.defaults[i], 2) & 1)
self.blockSignals(False)
newValue = self.value()
# emit the changed signal only once, and only if values actually changed
if oldValue != newValue:
self.changed.emit()
class Group(QtWidgets.QWidget):
changed = QtCore.pyqtSignal()
def __init__(self, name, defaults=None, lenghts=None):
QtWidgets.QWidget.__init__(self)
layout = QtWidgets.QHBoxLayout()
self.setLayout(layout)
self.name = name
self.bitLength = 0
self.widgets = []
if defaults is not None:
self.addOptions(defaults, lenghts)
def value(self):
v = 0
for widget in self.widgets:
v += widget.value()
return v
def addOption(self, name, index, default='0'):
widget = BinaryWidget(name, index, default)
self.layout().addWidget(widget)
self.widgets.append(widget)
widget.changed.connect(self.changed)
self.bitLength += len(default)
def addOptions(self, defaults, lenghts = None):
if lenghts is None:
lenghts = [1] * len(defaults)
# reverse bit order for per-setting indexing
defaultsIndex = 0
bitIndex = len(defaults)
for i, l in enumerate(lenghts):
self.addOption(
'Setting {}'.format(i + 1),
bitIndex - l,
defaults[defaultsIndex:defaultsIndex + l])
bitIndex -= l
defaultsIndex += l
def resetValues(self):
for widget in self.widgets:
widget.resetValues()
class Tester(QtWidgets.QWidget):
def __init__(self):
QtWidgets.QWidget.__init__(self)
layout = QtWidgets.QGridLayout()
self.setLayout(layout)
self.tabWidget = QtWidgets.QTabWidget()
layout.addWidget(self.tabWidget)
resultLayout = QtWidgets.QHBoxLayout()
layout.addLayout(resultLayout, layout.rowCount(), 0, 1, layout.columnCount())
self.tabs = []
self.labels = []
for (group, lenghts), defaults in zip(groups, defaultValues):
tab = Group(group, defaults, lenghts)
self.tabWidget.addTab(tab, group)
tab.changed.connect(self.updateResults)
self.tabs.append(tab)
tabLabel = QtWidgets.QLabel()
self.labels.append(tabLabel)
resultLayout.addWidget(tabLabel)
self.resetButton = QtWidgets.QPushButton('Reset values')
layout.addWidget(self.resetButton)
self.resetButton.clicked.connect(lambda: [tab.resetValues() for tab in self.tabs])
self.updateResults()
def values(self):
return [tab.value() for tab in self.tabs]
def updateResults(self):
for value, tab, label in zip(self.values(), self.tabs, self.labels):
label.setText('''
{0}: <span style="font-family:monospace;">{1} <b>{1:0{2}b}</b></span>
'''.format(tab.name, value, tab.bitLength))
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = Tester()
w.show()
sys.exit(app.exec_())
In the docs for wx.Slider (wxPython for py2, wxPython for py3, wxWidgets), there is listed a widget control named wx.SL_SELRANGE, defined to allow "the user to select a range on the slider (MSW only)". To me, this speaks of a twin-control, two sliders on the same axis in order to define a low/high range. I can't get it to show two controls.
Basic code to get it started. I'm not even worried yet about methods, events, or whatnot at this point, just to show something.
class MyFrame(wx.Frame):
def __init__(self, *args, **kwds):
# ... sizers and other stuff
self.myslider = wx.Slider(self.notebook_1_pane_2, wx.ID_ANY, 0, -100, 100, style=wx.SL_SELRANGE)
# ...
self.myslider.SetSelection(10, 90)
With all of that, the most I've been able to get it to show is a blue line spanning about where I would expect things to be.
The wxPython docs all talk about it but how is the user supposed to be able to "select a range on the slider", like shown here (taken from shiny)?
What am I missing? Are there any reasonable public examples of a wxPython wx.Slider in the wild with this functionality?
PS:
One page I found speaks of WinXP only, but since that page hasn't been updated in seven years, I don't consider it authoritative on the version restriction.
I've been using wxGlade for gui layout, but I'm certainly willing/able to go into the code after export and muck around.
System: win81_64, python-2.7.10, wxPython-3.0.2.0
I have made a custom implementation for this, partly using a code from this question. Left click on the slider area sets the left border of the range, right click sets the right border. Dragging the slider moves the selection. left_gap and right_gap indicates what is the empty space between edges of the widget and actual start of the drawn slider. As in the source, these must be found out by experimentation.
class RangeSlider(wx.Slider):
def __init__(self, left_gap, right_gap, *args, **kwargs):
wx.Slider.__init__(self, *args, **kwargs)
self.left_gap = left_gap
self.right_gap = right_gap
self.Bind(wx.EVT_LEFT_UP, self.on_left_click)
self.Bind(wx.EVT_RIGHT_UP, self.on_right_click)
self.Bind(wx.EVT_SCROLL_PAGEUP, self.on_pageup)
self.Bind(wx.EVT_SCROLL_PAGEDOWN, self.on_pagedown)
self.Bind(wx.EVT_SCROLL_THUMBTRACK, self.on_slide)
self.slider_value=self.Value
self.is_dragging=False
def linapp(self, x1, x2, y1, y2, x):
proportion=float(x - x1) / (x2 - x1)
length = y2 - y1
return round(proportion*length + y1)
# if left click set the start of selection
def on_left_click(self, e):
if not self.is_dragging: #if this wasn't a dragging operation
position = self.get_position(e)
if position <= self.SelEnd:
self.SetSelection(position, self.SelEnd)
else:
self.SetSelection(self.SelEnd, position)
else:
self.is_dragging = False
e.Skip()
# if right click set the end of selection
def on_right_click(self, e):
position = self.get_position(e)
if position >= self.SelStart:
self.SetSelection(self.SelStart, position)
else:
self.SetSelection(position, self.SelStart)
e.Skip()
# drag the selection along when sliding
def on_slide(self, e):
self.is_dragging=True
delta_distance=self.Value-self.slider_value
self.SetSelection(self.SelStart+delta_distance, self.SelEnd+delta_distance)
self.slider_value=self.Value
# disable pageup and pagedown using following functions
def on_pageup(self, e):
self.SetValue(self.Value+self.PageSize)
def on_pagedown(self, e):
self.SetValue(self.Value-self.PageSize)
# get click position on the slider scale
def get_position(self, e):
click_min = self.left_gap #standard size 9
click_max = self.GetSize()[0] - self.right_gap #standard size 55
click_position = e.GetX()
result_min = self.GetMin()
result_max = self.GetMax()
if click_position > click_min and click_position < click_max:
result = self.linapp(click_min, click_max,
result_min, result_max,
click_position)
elif click_position <= click_min:
result = result_min
else:
result = result_max
return result
I had this same problem before and couldn't find a good solution. What I ended up doing was creating my own custom RangeSlider widget with two actual thumbs.
Code is available in this answer, or in this GitHub gist.
Better late than never: to answer the original question, wxSL_SELRANGE does work but it only results in the expected appearance if it's combined with wxSL_LABELS. With both of these styles (and the selection set to 20..80 for the total range 0..100) the control appears like this:
I'm new to Qt (PySide), and I'm trying to draw a 'grid map' efficiently. However my solution slows down to a halt with 10k+ QGraphicsRectItem.
Currently it works like so:
class GridMapView(QObject, QGraphicsItemGroup):
def __init__(self, mapWidth, mapHeight, cellSize):
QObject.__init__(self)
QGraphicsItemGroup.__init__(self)
self.mapWidth = mapWidth
self.mapHeight = mapHeight
self.cellSize = cellSize
self.graphicCells = []
#Create cells.
for x in range(self.mapWidth / self.cellSize):
self.graphicCells.append([])
for y in range(self.mapHeight / self.cellSize):
self.graphicCells[x].append(QGraphicsRectItem(x * self.cellSize, y * self.cellSize, self.cellSize, self.cellSize))
self.graphicCells[x][-1].setBrush(QBrush(QColor('grey')))
self.addToGroup(self.graphicCells[x][-1])
self.setPos(-mapWidth/2, -mapHeight/2)
#Slot(Point, int)
def onCellUpdated(self, index, state):
cell = self.graphicCells[index.x][index.y]
if state == CellStates.UNKNOWN:
cell.setBrush(QBrush(QColor('grey')))
cell.setVisible(True)
elif state == CellStates.FREE:
cell.setVisible(False)
elif state == CellStates.OCCUPIED:
cell.setBrush(QBrush(QColor('black')))
cell.setVisible(True)
The initial grid is populated during creation. When the appropriate signal is fired, a specific cell will be updated. This updating is fairly infrequent, and my assumption was that Qt only draws what changes.
The entire 'map' is visible in my viewport, and disabling the rendering makes my application run perfectly fine.
I've tried setting QGraphicsView.NoViewportUpdate, yet it still updates the entire view. I hoped it would require me to call '.update()'.
Is this approach flawed from the start? Thanks in advance.
I have some unusual question :
For visualization of packing progress i think about qprogressbar with two values in one bar - one showing bytes read, and another showing write-out bytes, which gives also imagine about compress ratio.
It is possible with QT4 ?
Also, I have very little experience with C++ coding, my current work is based on Python, PyQT4,
Yes it's possible, but you will have to implement your own "DualValueProgressbar" here you have an example, is not complete production code but it will point to you in the right direction.
A note before continue:
Will this you will be able to show two values in the bar, but show two colours in the same bar is a very different thing. So I'll recomend you to use two prograssbar for doing what you want, keep it simple.
Before see any code let me explain what I did.
Subclass QProgressBar
Add a variable member called self.__value_1. This will be the second value.
Override the method paintEvent in order to draw self.__value_1 inside the bar.
Recomendations:
Write code for establishing limits on the second value. (Minimun and maximun)
Write code for handle the format property.
Write code for habdle the aligment property.
This is the result:
Here is the code:
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class DualValueProgressBar(QProgressBar):
def __init__(self, parent=None):
super(DualValueProgressBar, self).__init__(parent)
# The other value you want to show
self.__value_1 = 0
def paintEvent(self, event):
# Paint the parent.
super(DualValueProgressBar, self).paintEvent(event)
# In the future versions if your custom object you
# should use this to set the position of the value_1
# in the progressbar, right now I'm not using it.
aligment = self.alignment()
geometry = self.rect() # You use this to set the position of the text.
# Start to paint.
qp = QPainter()
qp.begin(self)
qp.drawText(geometry.center().x() + 20, geometry.center().y() + qp.fontMetrics().height()/2.0, "{0}%".format(str(self.value1)))
qp.end()
#property
def value1(self):
return self.__value_1
#pyqtSlot("int")
def setValue1(self, value):
self.__value_1 = value
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = QWidget()
hlayout = QHBoxLayout(window)
dpb = DualValueProgressBar(window)
dpb.setAlignment(Qt.AlignHCenter)
# This two lines are important.
dpb.setValue(20)
dpb.setValue1(10) # Look you can set another value.
hlayout.addWidget(dpb)
window.setLayout(hlayout)
window.show()
sys.exit(app.exec())
Finally the code sample:
I am trying to construct a little GUI that has a plot which updates every time a new data sample is read. I would prefer not to run it with a timer, since the data will be arriving at differing intervals. Instead, I'm trying to make an implementation using signals, where the data collection function will emit a signal when data is read, and then the painting function will emit a signal when the painting is completed.
The problem, as it appears right now, is that the canvas is not updating as soon as I call canvas.draw(). When this program runs, data_collect() and paint() alternate sending signals, but the figure is not updated until after I stop the process. How can I force matplotlib to update the figure whenever paint() is called?
What follows is a relatively simple piece of example code which is not optimal, but hopefully will convey the flavor of what I'm trying to do...
N_length = 150;
count = [0];
def sinval(delay):
k = 0;
x = [];
# set up data vector with sinusoidal data in it.
while k < N_length:
x.append(math.sin(2*math.pi*k/N_length));
k += 1;
def next():
time.sleep(delay);
outstring = "%0.3e" % (x[count[0]]);
if (count[0] == (N_length-1)):
count[0] = 0;
else:
count[0] += 1;
return outstring;
return next;
class DesignerMainWindow(QtGui.QMainWindow, Ui_mplMainWindow):
def __init__(self, parent = None):
super(DesignerMainWindow, self).__init__(parent)
self.setupUi(self)
QtCore.QObject.connect(self.mplStartButton, QtCore.SIGNAL("clicked()"), self.start_graph);
QtCore.QObject.connect(self.mplStopButton, QtCore.SIGNAL("clicked()"), self.stop_graph);
QtCore.QObject.connect(self.mplQuitButton, QtCore.SIGNAL("clicked()"), QtGui.qApp, QtCore.SLOT("quit()"));
QtCore.QObject.connect(self, QtCore.SIGNAL("data_collect()"), self.data_collect);
QtCore.QObject.connect(self, QtCore.SIGNAL("paint()"), self.paint);
def start_graph(self):
# generates first "empty" plots
self.user = [];
self.l_user, = self.mpl.canvas.ax.plot([], self.user, label='sine wave');
# set up the axes.
self.mpl.canvas.ax.set_xlim(0, 300);
self.mpl.canvas.ax.set_ylim(-1.1, 1.1);
self.mpl.canvas.draw();
# start the data collection process.
self.delay = 0.05;
self.next = sinval(self.delay);
self.emit(QtCore.SIGNAL('data_collect()'));
def data_collect(self):
outstring = self.next();
self.user.append(float(outstring.split()[0]));
self.l_user.set_data(range(len(self.user)), self.user);
self.emit(QtCore.SIGNAL('paint()'));
def paint(self):
self.mpl.canvas.draw();
self.emit(QtCore.SIGNAL('data_collect()'));
I'd guess that calling QCoreApplication::processEvents after paint() will help. More elegant would be to have a separate QThread for the reading. Take a look at this thread.