-1

I recently started learning Python + PyQt5. Please help me understand how calling of class function inside another class in python.

I have the following code

from PyQt5 import QtGui, QtWidgets, QtCore, uic
from PyQt5.Qt import *

class mywindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        QtWidgets.QMainWindow.__init__(self)
        self.ui = uic.loadUi('test.ui', self)
        self.resize(820, 300)
        self.setFixedSize(self.size())
        self.pushButton.clicked.connect(self.getValue)
        self.thread = {}
        self.pushButtonStart.clicked.connect(self.start_worker_1)
        self.pushButtonStop.clicked.connect(self.stop_worker_1)

    def getValue(self):
        self.value = self.spinBox.value()
        i = 1
        while i <= self.value:
            os.system('test1.py')
            i += 1
        else:
            print('End, i =', i)

    def start_worker_1(self):
        self.thread[1] = ThreadClass(parent=None, index=1)
        self.thread[1].start()
        self.pushButtonStart.setEnabled(False)
        self.pushButtonStop.setEnabled(True)

    def stop_worker_1(self):
        self.thread[1].stop()
        self.pushButtonStart.setEnabled(True)
        self.pushButtonStop.setEnabled(False)

class ThreadClass(QtCore.QThread):
    any_signal = QtCore.pyqtSignal(int)

    def __init__(self, parent=None, index=0):
        super(ThreadClass, self).__init__(parent)
        self.index = index
        self.is_running = True


    def run(self):
        print('Start...', self.index)
        a = mywindow()
        a.getValue()

    def stop(self):
        self.is_running = False
        print('Stop...', self.index)
        self.terminate()


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    application = mywindow()
    application.show()

    sys.exit(app.exec())

I need the test.py file to be executed as many times as specified in the spinBox

Start... 1
End, i = 1

If I do while while i <= 4: then it works. But doesn't work if I pass self.value from SpinBox. What am I doing wrong?

  • 1
    What are you trying to achieve? Consider that your approach won't work for two reasons: 1. you're creating a **new** instance of `mywindow`, instead of using the existing one; 2. access to UI objects is *forbidden* from external threads, that's why signals should be used instead: even if you were able to call the method of the current instance, it would probably cause your program to crash. – musicamante May 13 '21 at 19:03
  • I am not creating a new instance of mywindow. I just run another script, test1.py, which is located in the same directory. It has nothing but a print("Test") – user15868446 May 13 '21 at 19:09
  • Yes, you are creating a new instance, in `run()`: `a = mywindow()`. – musicamante May 13 '21 at 19:20
  • You are creating different widgets (it seems that you have not noticed): `a = mywindow()` in each thread. Besides that it is forbidden in Qt – eyllanesc May 13 '21 at 19:21

1 Answers1

1

You are complicating your application unnecessarily, besides that you have the error that you are creating a window object in each thread which is illogical in addition to the fact that it is forbidden to create widgets in another thread.

In this case it is better to use QProcess and the signals to know when the script is finished executing.

Note: since the .ui is not provided then I will show a trivial example:

test.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>180</width>
    <height>98</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QHBoxLayout" name="horizontalLayout">
    <item>
     <widget class="QSpinBox" name="spinBox"/>
    </item>
    <item>
     <widget class="QPushButton" name="pushButton">
      <property name="text">
       <string>Start</string>
      </property>
     </widget>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>180</width>
     <height>28</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>

main.py

import os
import sys
from functools import cached_property
from pathlib import Path

from PyQt5 import QtGui, QtWidgets, QtCore, uic

CURRENT_DIRECTORY = Path(__file__).resolve().parent


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.ui = uic.loadUi(os.fspath(CURRENT_DIRECTORY / "test.ui"), self)

        self.ui.pushButton.clicked.connect(self.handle_clicked)

    @cached_property
    def manager(self):
        return Manager()

    def handle_clicked(self):
        number_of_processes = self.ui.spinBox.value()
        script = os.fspath(CURRENT_DIRECTORY / "test1.py")
        for i in range(number_of_processes):
            self.manager.execute(script, dict(i=i))


class Manager(QtCore.QObject):
    @cached_property
    def processes(self):
        return list()

    def execute(self, script, metadata=None):
        process = QtCore.QProcess()
        process.setProperty("metadata", metadata or dict())
        process.finished.connect(self.handle_finished)
        process.setProgram(sys.executable)
        process.setArguments([script])
        process.start()
        self.processes.append(process)

    def handle_finished(self):
        process = self.sender()
        self.processes.remove(process)
        metadata = process.property("metadata")
        print(f"{metadata} finished")


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)

    w = MainWindow()
    w.show()

    sys.exit(app.exec())
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Thanks for your solution, this is what I needed. Please tell me how to use Qprocess in this case to start the next process at the end of the first. In your example, they run parallel – user15868446 May 14 '21 at 02:12
  • @user15868446 see https://stackoverflow.com/questions/53461496/trying-to-get-qprocess-to-work-with-a-queue/53463275#53463275 and https://stackoverflow.com/questions/51364552/how-do-i-queue-qprocesses-in-pyqt5/51378298#51378298 – eyllanesc May 14 '21 at 02:15
  • Could you tell me with my example, I really tried. But it didn't work out for me :( – user15868446 May 14 '21 at 10:06