2

First of all sorry for the lenght. I want to explain my problem as good as possible. I am quite new to Python and trying to make a plotting app using PyQtGraph embedded in PyQt4. Some days ago I got a really nice answer to my plotting problem, and my next step is to have two PyQtGraphs plot Widgets simoultaneously plotting in the same PyQt4's CentralWidget. By the same approach as in the link described, both plots work fine, but the GUI gets unresponsive. For overcoming this, I am aiming to use QThread, and for that, I need to have my plotting functions in different classes. But I am messing with my variables, and can't see how:

from PyQt4 import QtCore, QtGui
import pyqtgraph as pg
import random


class MainWindow(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.central_widget = QtGui.QStackedWidget()
        self.setCentralWidget(self.central_widget)
        self.login_widget = LoginWidget(self)
        self.login_widget.button.clicked.connect(Plots.plotter)
        self.central_widget.addWidget(self.login_widget)


class LoginWidget(QtGui.QWidget):
    def __init__(self, parent=None):
        super(LoginWidget, self).__init__(parent)
        layout = QtGui.QHBoxLayout()
        self.button = QtGui.QPushButton('Start Plotting')
        layout.addWidget(self.button)
        self.plot = pg.PlotWidget()
        layout.addWidget(self.plot)
        self.setLayout(layout)


class Plots(MainWindow):

    def print(self):
        print('hello World')

    def plotter(self):
        self.data = [0]
        self.curve = self.login_widget.plot.getPlotItem().plot()
        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.updater)
        self.timer.start(0)

    def updater(self):
        self.data.append(self.data[-1]+0.2*(0.5-random.random()))
        self.curve.setData(self.data)

if __name__ == '__main__':
    app = QtGui.QApplication([])
    window = MainWindow()
    window.show()
    app.exec_()

That gives me an error at Plots.plotter:

self.data = [0]
AttributeError: 'bool' object has no attribute 'data'

If in the MainWindow class I substitute the 'button.connect' argument with Plots.print, it works fine. So I can see there is something wrong with the fact that I make an LoginWidget object in MainWindow, and then Plots inherits from MainWindow, calling that same LoginWidget object again.

I have tried a proposed solution where the father (MainWindow) does not access the methods of the children (Plots). But if, from Plots, I want to call the class where I aim placing my thread in, I get the same error again....

import sys
from PyQt4 import QtCore, QtGui
import pyqtgraph as pg
import random


class LoginWidget(QtGui.QWidget):
    def __init__(self, parent=None):
        super(LoginWidget, self).__init__(parent)

        layout = QtGui.QHBoxLayout()
        self.button = QtGui.QPushButton('Start Plotting')
        layout.addWidget(self.button)
        self.plot = pg.PlotWidget()
        layout.addWidget(self.plot)
        self.setLayout(layout)


class MainWindow(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.central_widget = QtGui.QStackedWidget()
        self.setCentralWidget(self.central_widget)
        self.login_widget = LoginWidget(self)
        self.central_widget.addWidget(self.login_widget)


class Plots(MainWindow):
    def __init__(self, parent=None):
        super(Plots, self).__init__(parent=parent)
        self.login_widget.button.clicked.connect(MyThread.plotter)



class MyThread(MainWindow):
    def __init__(self, parent=None):
        super(Aname, self).__init__(parent=parent)

    def print(self):
        print('hello World')

    def plotter(self):
        self.data = [0]
        self.curve = self.login_widget.plot.getPlotItem().plot()
        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.updater)
        self.timer.start(0)

    def updater(self):
        self.data.append(self.data[-1]+0.2*(0.5-random.random()))
        self.curve.setData(self.data)


if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    w = Plots()
    w.show()
    sys.exit(app.exec_())
Community
  • 1
  • 1
Ivy
  • 175
  • 2
  • 14
  • 1
    Your code gives me a different error. `self.login_widget.button.clicked.connect(Plots.plotter)` AttributeError: 'function' object has no attribute '__pyqtSignature__' – NoDataDumpNoContribution Jan 18 '17 at 11:58
  • does it give the same error when you substitute Plots.plotter by Plots.print in self.login_widget.button.clicked.connect(Plots.plotter)? – Ivy Jan 18 '17 at 13:02

1 Answers1

3

In inheritance the father should not access the methods of the children, It is better to inherit and implement new methods that have nothing to do with the parent in the child class.

Timer Version

import random
import sys

import pyqtgraph as pg
from PyQt4 import QtGui, QtCore


class LoginWidget(QtGui.QWidget):
    def __init__(self, parent=None):
        super(LoginWidget, self).__init__(parent)
        layout = QtGui.QHBoxLayout()
        self.button = QtGui.QPushButton('Start Plotting')
        layout.addWidget(self.button)
        self.plot = pg.PlotWidget()
        layout.addWidget(self.plot)
        self.setLayout(layout)
        self.button.clicked.connect(self.plotter)

    def plotter(self):
        self.data = [0]
        self.curve = self.plot.getPlotItem().plot()
        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.updater)
        self.timer.start(0)

    def updater(self):
        self.data.append(self.data[-1] + 0.2 * (0.5 - random.random()))
        self.curve.setData(self.data)


class MainWindow(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.centralwidget = QtGui.QWidget(self)
        self.setCentralWidget(self.centralwidget)
        self.horizontalLayout = QtGui.QHBoxLayout(self.centralwidget)
        self.login_widget_1 = LoginWidget(self)
        self.horizontalLayout.addWidget(self.login_widget_1)

        self.login_widget_2 = LoginWidget(self)
        self.horizontalLayout.addWidget(self.login_widget_2)

        self.setCentralWidget(self.centralwidget)


if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Thanks, but that does not solve my problem. "_I need to have my plotting functions in different classes_", in order to use QThread. So, if I just create a new class, also inheriting from MainWindow, and call it, I do get the same error. And in this case I would not have my slot call place at the class from which I inherit (MainWindow), but in Plots, as you proposed. – Ivy Jan 19 '17 at 08:06
  • 1
    @Ivy Could you put a picture of what you want? – eyllanesc Jan 19 '17 at 13:13
  • I want, in the same widget, two buttons and two graphs. Each button triggers a graph as for the single graph example above, and that the app is still responsive, as in the single graph example above. – Ivy Jan 19 '17 at 15:44
  • 1
    @Ivy To use the threads it is not necessary to have different classes. – eyllanesc Jan 19 '17 at 16:46
  • Thanks @eyllanesc, I appreciate it. The threading one looks very interesting: the GUI is responsive! :-) But it updates with varying delays, and one of the Threads is destroyed after a short time. I'll work on QThread to make it work properly. – Ivy Jan 20 '17 at 10:27
  • do you know why are my plots only updating when I minimize /maximize? – Ivy Jan 20 '17 at 12:53
  • @eyllanesc The second example is bad. You call it thread but it really isn't since it shares part of the GUI (the plot widget) with the main thread. Not keeping the GUI and the thread separate can cause all kinds of problems, exactly like [the one Ivy now has](http://stackoverflow.com/questions/41808667/qthreading-pyqtgraph-plotwidgets-in-pyqt4). – ImportanceOfBeingErnest Jan 23 '17 at 23:03
  • @ImportanceOfBeingErnest I put the second example because Ivy asked me. Also I have not had problems with that implementation. – eyllanesc Jan 24 '17 at 04:08
  • Neither the fact that Ivy asked you for it, nor the fact that it does run make it correct. Having this code stick around will lead to a lot of people copying and using it, and they will all sooner or later experience problems, asking new questions and giving everyone a hard time to find the error. I would really suggest to remove that code. If you want you can of course provide a new threading example using signals and slots. – ImportanceOfBeingErnest Jan 24 '17 at 08:05