I cutted out code for better understanding my issue. The question is if there is some way how to create something like public method and how or whether I'm totally misunderstood OOP concept.
I have a major class PlayerWindow which only plays video. Then there is class ControlsWindow serving only for my developing, testing and maintenance purposes (launched when fullscreen off). Therefore, I want to be it particularly. Can not figure out, how to call method play() from the ControlsWindow class as well like from inside because when I initialize ControlsWindow with instance of PlayerWindow then I get infinite loop.
class ControlsWindow(QtGui.QWidget):
def __init__(self):
super(ControlsWindow, self).__init__()
self.playPauseButton = QtGui.QPushButton('Play', self)
self.show()
class PlayerWindow(QtGui.QWidget):
def __init__(self):
super(PlayerWindow, self).__init__()
# ...
self.mediaPlayer = self.playerInstance.media_player_new()
# ...
self.initUI()
self.play()
def initUI(self):
# ...
self.show()
self.controls_window = ControlsWindow()
def keyPressEvent(self, e):
if e.key() == QtCore.Qt.Key_Return:
self.toggleControlsWindow()
def toggleControlsWindow(self):
if self.isFullScreen():
self.showNormal()
self.controls_window = ControlsWindow()
else:
self.controls_window.close()
self.showFullScreen()
def play(self):
self.mediaPlayer.play()
def main():
app = QtGui.QApplication(sys.argv)
player_window = PlayerWindow()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
You can pass an instance of PlayerWindow to the class constructor of ControlsWindow:
class ControlsWindow(QtGui.QWidget):
def __init__(self, parent): # Notice the new parent argument
super(ControlsWindow, self).__init__()
self.parent = parent
self.playPauseButton = QtGui.QPushButton('Play', self)
self.show()
# Now you can call the parent's (PlayerWindow) play() function:
self.parent.play()
class PlayerWindow(QtGui.QWidget):
def __init__(self):
super(PlayerWindow, self).__init__()
# ...
self.mediaPlayer = self.playerInstance.media_player_new()
# ...
self.initUI()
self.play()
def initUI(self):
# ...
self.show()
self.controls_window = ControlsWindow(self) # Pass a reference of PlayerWindow
def keyPressEvent(self, e):
if e.key() == QtCore.Qt.Key_Return:
self.toggleControlsWindow()
def toggleControlsWindow(self):
if self.isFullScreen():
self.showNormal()
self.controls_window = ControlsWindow(self) # Pass a reference of PlayerWindow
else:
self.controls_window.close()
self.showFullScreen()
def play(self):
self.mediaPlayer.play()
def main():
app = QtGui.QApplication(sys.argv)
player_window = PlayerWindow()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Hope this helps!
Assuming that I understand you correctly and your code can be simplified to something like:
# QtGui.QWidget
class B:
pass
# ControlsWindow
class Test(B):
def __init__(self):
# You want to call Actual.play here?
pass
# PlayerWindow
class Actual(B):
def __init__(self):
# Your self.mediaPlayer = self.playerInstance.media_player_new()
self.some_variable = 42
def play(self):
print(self.some_variable)
If you'd like to call Actual.play method from inside Test class you can either:
make Actual.play static and self.mediaPlayer a class variable
class Test(B):
def __init__(self):
# Here you go!
Actual.play()
class Actual(B):
# mediaPlayer = self.playerInstance.media_player_new()
some_variable = 42
def __init__(self):
pass
#staticmethod
def play():
print(Actual.some_variable)
or pass a reference to PlayerWindow object to your ControlsWindow class instance
class B:
pass
class Test(B):
def __init__(self, actual: Actual):
# Here you go!
actual.play()
class Actual(B):
def __init__(self):
self.some_variable = 42
def play(self):
print(self.some_variable)
Related
This is the current code I am using:
class Opening(QDialog):
def __init__(self):
super(Opening, self).__init__()
loadUi("reminder2.ui", self)
self.startbutton.clicked.connect(self.gotomain)
def gotomain(self):
main = MainWindow()
widget.addWidget(main)
widget.setCurrentIndex(widget.currentIndex()+1)
class MainWindow(QDialog):
def __init__(self):
super(MainWindow, self).__init__()
loadUi("reminder.ui",self)
self.typebutton.clicked.connect(self.med)
self.searchbutton.clicked.connect(self.medd)
self.med2.hide()
self.med3.hide()
self.med4.hide()
self.med5.hide()
self.med6.hide()
self.med7.hide()
self.med8.hide()
self.addbutton.clicked.connect(self.clickAdd)
def med(self):
self.stackedWidget.setCurrentWidget(self.typemed)
def medd(self):
self.stackedWidget.setCurrentWidget(self.searchmed)
def clickAdd(self):
self.med2.show()
I want to use multiple imported function with arguments that takes some while to run. I want a 'working' progress bar that track the processes of that function. I have followed 2 questions already here.
Connect an imported function to Qt5 progress bar without dependencies
Report progress to QProgressBar using variable from an imported module
The difference is that the thread can take any function which can have arguments. The function also not needs to yield the percent to return to the progressbar. The progressbar always start at 0%.
I copied a snippet from first link and modified it for example purpose.
from external_script import long_running_function
class Actions(QDialog):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle('Progress Bar')
self.progress = QProgressBar(self)
self.button = QPushButton('Start', self)
self.show()
self.button.clicked.connect(self.onButtonClick)
def onButtonClick(self):
long_running_function(**kwargs) # This can be any function that takes argument/s
self.progress.setValue(value)
Do not get too complicated with the answers as they are limited to a very particular context. In general the logic is to pass a QObject to it that updates the percentage value and then emits a signal with that value. For example a simple solution is to use the threading module:
import sys
import threading
from PyQt5 import QtCore, QtWidgets
class PercentageWorker(QtCore.QObject):
started = QtCore.pyqtSignal()
finished = QtCore.pyqtSignal()
percentageChanged = QtCore.pyqtSignal(int)
def __init__(self, parent=None):
super().__init__(parent)
self._percentage = 0
#property
def percentage(self):
return self._percentage
#percentage.setter
def percentage(self, value):
if self._percentage == value:
return
self._percentage = value
self.percentageChanged.emit(self.percentage)
def start(self):
self.started.emit()
def finish(self):
self.finished.emit()
class FakeWorker:
def start(self):
pass
def finish(self):
pass
#property
def percentage(self):
return 0
#percentage.setter
def percentage(self, value):
pass
import time
def long_running_function(foo, baz="1", worker=None):
if worker is None:
worker = FakeWorker()
worker.start()
while worker.percentage < 100:
worker.percentage += 1
print(foo, baz)
time.sleep(1)
worker.finish()
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.progress = QtWidgets.QProgressBar()
self.button = QtWidgets.QPushButton("Start")
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.button)
lay.addWidget(self.progress)
self.button.clicked.connect(self.launch)
def launch(self):
worker = PercentageWorker()
worker.percentageChanged.connect(self.progress.setValue)
threading.Thread(
target=long_running_function,
args=("foo",),
kwargs=dict(baz="baz", worker=worker),
daemon=True,
).start()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
Attempting to write a class that will show the progress of a threaded process. I need to use this class for all "file load" operations; however I am having trouble making it global.
fileloader.py:
from PyQt5.QtCore import pyqtSlot, pyqtSignal
from PyQt5.QtWidgets import QDialog
from PyQt5.uic import loadUi
class FileLoader(QDialog):
completeSig = pyqtSignal()
def __init__(self, parent=None):
super(FileLoader, self).__init__(parent)
self.filename = ""
self.clientcode = ""
self.thread = ""
loadUi("GlobalUI/fileloader.ui", self)
self.prgLoader.setValue(0)
#pyqtSlot()
def on_btnCancel_clicked(self):
self.close()
def closeEvent(self, e):
self.thread.stop()
def loadData(self):
self.thread.totalSig.connect(self.prgLoader.setMaximum)
self.thread.countSig.connect(self.prgLoader.setValue)
self.thread.finished.connect(self.completed)
self.thread.start()
def completed(self):
self.completeSig.emit()
self.close()
loader.py
from PyQt5.QtCore import pyqtSignal, QThread
from fileloader import FileLoader
class DataLoader(QThread):
totalSig = pyqtSignal(int)
countSig = pyqtSignal(int)
def __init__(self, parent=None):
super(DataLoader, self).__init__(parent)
self.threadactive = True
self.commitsize = 300
self.rowdata = []
def run(self):
print("Code Will Go Here For Loading the File")
def stop(self):
self.threadactive = False
self.wait()
class PatDataLoader():
def load(self, clientcode, filename):
fl = FileLoader()
fl.clientcode = clientcode
fl.filename = filename
fl.thread = DataLoader()
fl.loadData()
I am calling PatDataLoader.load("test","test.txt") from another module. The problem I am running into is the application crashes with QThread: Destroyed while thread is still running as there seems to be a problem with the thread process I am passing to the fileloader. Am I not putting these pieces together properly?
main.py:
from lmdb.patloader import PatDataLoader
class PPSReportsApp(QMainWindow):
def __init__(self, *args):
super(PPSReportsApp, self).__init__(*args)
loadUi("GlobalUI/ppsreportswindow.ui", self)
#self.showMaximized()
#pyqtSlot()
def on_actionTest_triggered(self):
pl = PatDataLoader()
pl.load("TEST","testfile.txt")
In your code pl is a local variable so it will be deleted when it finishes executing on_actionTest_triggered which is an instant possibly generating that problem. On the other hand, no load should be a static method because it does not use the self. self.thread must be None, it is better than ""
How can you prevent pl from being deleted before it is finished processing?
fl is a QDialog so you can use exec_().
fileloader.py
class FileLoader(QDialog):
completeSig = pyqtSignal()
def __init__(self, parent=None):
super(FileLoader, self).__init__(parent)
self.filename = ""
self.clientcode = ""
self.thread = None
loadUi("GlobalUI/fileloader.ui", self)
self.prgLoader.setValue(0)
#pyqtSlot()
def on_btnCancel_clicked(self):
self.close()
def closeEvent(self, e):
if self.thread:
self.thread.stop()
def loadData(self):
if self.thread:
self.thread.totalSig.connect(self.prgLoader.setMaximum)
self.thread.countSig.connect(self.prgLoader.setValue)
self.thread.finished.connect(self.completed)
self.thread.start()
def completed(self):
self.completeSig.emit()
self.close()
loader.py
class DataLoader(QThread):
totalSig = pyqtSignal(int)
countSig = pyqtSignal(int)
def __init__(self, parent=None):
super(DataLoader, self).__init__(parent)
self.threadactive = True
self.commitsize = 300
self.rowdata = []
def run(self):
self.totalSig.emit(1000)
print("Code Will Go Here For Loading the File")
# emulate process
for i in range(1000):
if self.threadactive:
QThread.msleep(10)
self.countSig.emit(i)
def stop(self):
self.threadactive = False
self.quit()
self.wait()
class PatDataLoader():
#staticmethod
def load(clientcode, filename):
fl = FileLoader()
fl.clientcode = clientcode
fl.filename = filename
fl.thread = DataLoader()
fl.loadData()
fl.exec_() # <---
main.py
#pyqtSlot()
def on_actionTest_triggered(self):
PatDataLoader.load("TEST","testfile.txt")
The issue that I'm facing is when I want to split the functionality of the menubar into multiple files (classes), each of them specific for handling options (File/Help/Edit and so on).
In the Main UI class I have:
class MyFrame(QMainWindow):
def __init__(self):
super().__init__()
self.menu_bar = self.menuBar()
# Create menu
self.add_menu()
def add_menu(self):
help_menu = MenuHelp(self)
def getMenuBar(self):
return self.menu_bar
In the MenuHelp (class):
class MenuHelp(QMenu):
def __init__(self, parrent_widget):
super(MenuHelp, self).__init__()
self.menu_variable = parrent_widget.getMenuBar().addMenu('Help')
about_action = self.menu_variable.addAction('About')
about_action.setStatusTip('About')
about_action.triggered.connect(self.handle_trigger)
def handle_trigger(self):
print('Im here')
The menubar is correctly shown, but handle_trigger method is never called, any ideas on what am I doing wrong?
You must pass a parent to your QMenu. You must change:
class MenuHelp(QMenu):
def __init__(self, parrent_widget):
super(MenuHelp, self).__init__()
to:
class MenuHelp(QMenu):
def __init__(self, parrent_widget):
super(MenuHelp, self).__init__(parrent_widget)
I have very common question but it is not easy for me.
I'm trying to connect a PyQt signal from the MainWindow class to the CheckExcel class, but It doesn't work. I think some of people know how to do this, but some of them do not. Most people who doesn't know pointed out the code... but please don't do it. My point is how could I connect this?
Here is some code below:
class CheckExcel(QtCore.QThread):
updated = QtCore.pyqtSignal(int)
updateLab = QtCore.pyqtSignal(str)
running = False
def __init__(self, parent=None):
super(CheckExcel, self).__init__(parent)
self.progPercent = 0
self.running = True
def run(self):
pythoncom.CoInitialize()
try:
while self.running == True:
self.updated.emit(int(self.progPercent))
self.updateLab.emit(str(va_TC))
print(self.progPercent)
except :
print('Excel is not executed')
CheckExcel().run()
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent=parent)
self.setupUi(self)
def startBtnClicked(self):
self.chk_excel = CheckExcel(self)
self.chk_excel.start()
self.chk_excel.updated.connect(self.updateValue)
self.chk_excel.updateLab.connect(self.updateLabel)
def updateValue(self, data):
self.progressBar.setValue(data)
def updateLabel(self, text):
self.label.setText(text)
def stop(self):
self.event.set()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())