How to update QWidget's content? - python

There is a QWidget, which is called with the button "Start" in the main widget.
It shows some text, which should be updated every minute during the work of the QWidget.
How can I make this infinity updating within this code?
class ExampleWidget(QWidget):
def __init__(self, parent=None):
super().__init__()
self.setWindowTitle('Example Widget ScrollArea')
self.initUi()
def initUi(self):
area = QScrollArea(self)
area.setWidgetResizable(True)
self.scrollAreaWidgetContents = QLabel(some_text, self)
area.setWidget(self.scrollAreaWidgetContents)
button = QPushButton("Close")
button.clicked.connect(self.goMainWindow)
layoutV = QVBoxLayout()
layoutV.addWidget(area)
layoutV.addWidget(button)
self.setLayout(layoutV)
def goMainWindow(self):
self.hide()
def sizeHint(self):
return QSize(400, 200)
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
start_main_button = QPushButton('Start', self)
start_main_button.move(40, 40)
start_main_button.clicked.connect(self.start)
self.setGeometry(300, 300, 290, 150)
self.setWindowTitle('Test')
def start(self):
global some_text
some_text = 'some text'
self.result_widget = ExampleWidget()
self.result_widget.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
One of my tries:
def start(self):
global some_text
some_text = 'some text'
self.result_widget = ExampleWidget()
self.result_widget.show()
i = 0
while True:
i+=1
some_text+=str(i)
self.result_widget = ExampleWidget()
self.result_widget.show()

Forget the global variables because they are considered bad practice, on the other hand in a GUI you should avoid having loops that consume a lot of time, and in your case a while True blocks the GUI. In Qt if you want to do periodic tasks you must use a QTimer:
from PyQt5 import QtCore, QtWidgets
class ExampleWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__()
self.setWindowTitle('Example Widget ScrollArea')
self.initUi()
def initUi(self):
area = QtWidgets.QScrollArea()
area.setWidgetResizable(True)
self.scrollAreaWidgetContents = QtWidgets.QLabel("some_text")
area.setWidget(self.scrollAreaWidgetContents)
button = QtWidgets.QPushButton("Close")
button.clicked.connect(self.hide)
layoutV = QtWidgets.QVBoxLayout(self)
layoutV.addWidget(area)
layoutV.addWidget(button)
def update_text(self, text):
# update the text
self.scrollAreaWidgetContents.setText(text)
def sizeHint(self):
return QtCore.QSize(400, 200)
class MainWindow(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
start_main_button = QtWidgets.QPushButton('Start', self)
start_main_button.move(40, 40)
start_main_button.clicked.connect(self.start)
self.setGeometry(300, 300, 290, 150)
self.setWindowTitle('Test')
self.result_widget = ExampleWidget()
self.timer = QtCore.QTimer(self, interval=60*1000)
self.timer.timeout.connect(self.on_timeout)
self.counter = 0
self.initial_text = "some_text"
def on_timeout(self):
# this method will be called every 60 * 1000 ms
self.initial_text += str(self.counter)
self.result_widget.update_text(self.initial_text)
self.counter += 1
#QtCore.pyqtSlot()
def start(self):
self.result_widget.show()
# start timer
self.timer.start()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())

Related

PyQt5 Switch between window after time in a loop

i use the code below, found on stackoverflow. i want to build a loop which getting started with a button on the main window. After clicked window2 should open for 30 seconds. then window3 should open for 30 seconds. then window2 should open and so on.
i make a slide methode but it wont work.
i try it with the window2() and window3() and slide() methode
what is my target?
i want a slideshow with the two window(window2 & window3) that alternate.
not more but it is an endless loop
import sys
from PyQt5 import QtGui
from PyQt5.QtWidgets import (QApplication, QMainWindow, QPushButton,
QToolTip, QMessageBox, QLabel)
class Window2(QMainWindow): # <===
def __init__(self):
super().__init__()
self.setWindowTitle("Window22222")
class Window3(QMainWindow): # <===
def __init__(self):
super().__init__()
self.setWindowTitle("Window333333")
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.title = "First Window"
self.top = 100
self.left = 100
self.width = 680
self.height = 500
self.pushButton = QPushButton("Start", self)
self.pushButton.move(275, 200)
self.pushButton.setToolTip("<h3>Start the Session</h3>")
self.pushButton.clicked.connect(self.window2)
self.pushButton1 = QPushButton("Start", self)
self.pushButton1.move(20, 20)
self.pushButton1.setToolTip("<h3>Start the Session</h3>")
self.pushButton1.clicked.connect(self.slide)
self.main_window()
def main_window(self):
self.label = QLabel("Manager", self)
self.label.move(285, 175)
self.setWindowTitle(self.title)
self.setGeometry(self.top, self.left, self.width, self.height)
self.show()
def window2(self): # <===
self.w2 = Window2()
self.w2.show()
time.sleep(5)
self.w3 = Window3()
self.w3.show()
self.w2.hide()
self.window3()
#self.hide()
def window3(self): # <===
self.w3 = Window3()
self.w3.show()
time.sleep(5)
self.w2 = Window2()
self.w2.show()
self.w3.hide()
self.window2()
#self.hide()
def slide(self):
print ("slide")
self.window2()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
sys.exit(app.exec())
Try it:
import sys
#import time
from PyQt5 import QtGui, QtCore
from PyQt5.QtWidgets import (QApplication, QMainWindow, QPushButton,
QToolTip, QMessageBox, QLabel)
class Window2(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Window 22222")
class Window3(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Window 333333")
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.title = "First Window"
self.top, self.left, self.width, self.height = 100, 100, 680, 500
self.flag = ...
self.pushButton = QPushButton("Start", self)
self.pushButton.move(275, 200)
self.pushButton.setToolTip("<h3>Start the Session</h3>")
self.pushButton.clicked.connect(lambda: self.start_slide(True)) #(self.window2)
self.pushButton1 = QPushButton("Start", self)
self.pushButton1.move(20, 20)
self.pushButton1.setToolTip("<h3>Start the Session</h3>")
self.pushButton1.clicked.connect(lambda: self.start_slide(False))
# +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
self.main_window()
self.w2 = Window2()
self.w3 = Window3()
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self.viewWindows)
def start_slide(self, flag):
self.timer.stop()
self.w3.hide()
self.w2.hide()
if flag: self.w2.show()
else: self.w3.show()
self.flag = not flag
self.timer.start(5000)
def viewWindows(self):
if self.flag:
self.w2.show()
self.w3.hide()
else:
self.w3.show()
self.w2.hide()
self.flag = not self.flag
def closeEvent(self, event):
self.w2.close()
self.w3.close()
self.close()
# +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
def main_window(self):
self.label = QLabel("Manager", self)
self.label.move(285, 175)
self.setWindowTitle(self.title)
self.setGeometry(self.top, self.left, self.width, self.height)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())

PyQt5, QThread: How to keep GUI responsive while running non-loop function in thread? [duplicate]

I want to access the parent class widgets in the QThread class
This line gives hangs GUI "Example().setWindowTitle("Window")"
How can I do that?
class Example(QWidget):
def __init__(self):
super().__init__()
self.myclass2 = myclass2()
self.myclass2.start()
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 300, 220)
self.setWindowTitle('Icon')
self.setWindowIcon(QIcon('web.png'))
self.show()
class myclass2(QThread):
def __init__(self, parent=None):
super(myclass2, self).__init__(parent)
def run(self):
while True:
time.sleep(.1)
print(" in thread \n")
Example().setWindowTitle("Window")
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
You must understand the following expression:
Example().setWindowTitle("Window")
it is equivalent to:
obj = Example()
obj.setWindowTitle("Window")
That is, you are creating another Example object other than ex = Example(), and that object is creating another myclass2 object, and that other myclass2 object is creating another Example, and an infinite loop is clearly being created.
Another thing that in the future could cause you problems is to establish the same name for different things, although in this case it is not a problem but in future occasions it could bring you problems, the code to which I refer is:
self.myclass2 = myclass2()
For example, it is recommended that classes should start with capital letters.
Another error that is valid only in Qt is that the GUI can not be created or manipulated in a thread other than the main thread. So you can not change the title directly in the other thread but there are 2 methods:
1. QMetaObject::invokeMethod(...)
But for this we must pass the GUI through a property:
class Example(QWidget):
def __init__(self):
super().__init__()
self.myclass2 = MyClass()
self.myclass2.gui = self
self.myclass2.start()
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 300, 220)
self.setWindowTitle('Icon')
self.setWindowIcon(QIcon('web.png'))
self.show()
class MyClass(QThread):
def run(self):
while True:
time.sleep(.1)
print(" in thread \n")
QMetaObject.invokeMethod(self.gui, "setWindowTitle", Qt.QueuedConnection, Q_ARG(str, "Window"))
2. signals & slots
class Example(QWidget):
def __init__(self):
super().__init__()
self.myclass2 = MyClass()
self.myclass2.titleChanged.connect(self.setWindowTitle)
self.myclass2.start()
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 300, 220)
self.setWindowTitle('Icon')
self.setWindowIcon(QIcon('web.png'))
self.show()
class MyClass(QThread):
titleChanged = pyqtSignal(str)
def run(self):
while True:
time.sleep(.1)
print(" in thread \n")
self.titleChanged.emit("Window")
PLUS:
You should not modify the GUI from the thread directly but send the data through a signal:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Example(QtWidgets.QWidget):
def __init__(self):
super().__init__()
lay = QtWidgets.QVBoxLayout(self)
le = QtWidgets.QLineEdit()
lay.addWidget(le)
self.myclass2 = MyClass()
self.myclass2.titleChanged.connect(self.setWindowTitle)
self.myclass2.infoChanged.connect(le.setText) # <-- connect signal
self.myclass2.start()
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 300, 220)
self.setWindowTitle('Icon')
self.setWindowIcon(QtGui.QIcon('web.png'))
self.show()
class MyClass(QtCore.QThread):
titleChanged = QtCore.pyqtSignal(str)
infoChanged = QtCore.pyqtSignal(str) # <-- create signal
def run(self):
counter = 0
while True:
QtCore.QThread.msleep(100)
print(" in thread \n")
self.titleChanged.emit("Window")
self.infoChanged.emit("{}".format(counter)) # <-- emit signal
counter += 1
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

PySide switching widgets with events?

I can't figure it. I want to be able to swap out QWidgets according to events like button clicks but I am missing something and I haven't been able to search out any example along the lines of the code below. What I want to do is click one of the top buttons and get the widget below to switch between either the QCalendar or QtextEdit. Where am I going wrong?
Thanks!
#!/usr/bin/python
import sys
from PySide import QtGui, QtCore
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
hbox = QtGui.QHBoxLayout(self)
widget = QtGui.QCalendarWidget()
button1 = QtGui.QPushButton('Calendar', self)
button1.setCheckable(True)
button1.clicked[bool].connect(self.setWidget)
button2 = QtGui.QPushButton('TextEdit', self)
button2.setCheckable(True)
button2.clicked[bool].connect(self.setWidget)
splitter1 = QtGui.QSplitter(QtCore.Qt.Vertical)
splitter1.addWidget(button1)
splitter1.addWidget(button2)
splitter1.addWidget(widget)
hbox.addWidget(splitter1)
self.setLayout(hbox)
self.setGeometry(0, 0, 600, 600)
self.setWindowTitle('Switching QWidgets')
self.show()
def setWidget(self, pressed):
source = self.sender()
val1 = QtGui.QCalendarWidget()
val2 = QtGui.QTextEdit()
if source.text() == "Calendar":
widget = val1
QtGui.QWidget.update(Example.hbox)
elif source.text() == "TextEdit":
widget = val2
QtGui.QWidget.update(Example.hbox)
else:
widget = val1
QtGui.QWidget.update(Example.hbox)
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
You have to use QStackedWidget, where you update the indexes.
class Example(QtGui.QWidget):
def __init__(self, *args, **kwargs):
super(Example, self).__init__(*args, **kwargs)
self.initUI()
def initUI(self):
hbox = QtGui.QHBoxLayout(self)
self.stacked = QtGui.QStackedWidget(self)
self.stacked.addWidget(QtGui.QCalendarWidget())
self.stacked.addWidget(QtGui.QTextEdit())
splitter1 = QtGui.QSplitter(QtCore.Qt.Vertical)
for text in ["Calendar", "TextEdit"]:
btn = QtGui.QPushButton(text, self)
btn.clicked.connect(self.setWidget)
splitter1.addWidget(btn)
splitter1.addWidget(self.stacked)
hbox.addWidget(splitter1)
self.setLayout(hbox)
self.setGeometry(0, 0, 600, 600)
self.setWindowTitle('Switching QWidgets')
self.show()
def setWidget(self):
source = self.sender()
if source.text() == "Calendar":
self.stacked.setCurrentIndex(0)
elif source.text() == "TextEdit":
self.stacked.setCurrentIndex(1)
If you change your code to keep a reference to the calendar and textedit widgets, then add them both to the splitter in init... You can just use the show and hide functions in your setWidget().
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.hbox = QtGui.QHBoxLayout(self)
self.widget = QtGui.QCalendarWidget()
self.widget2 = QtGui.QTextEdit()
button1 = QtGui.QPushButton('Calendar', self)
button1.setCheckable(True)
button1.clicked[bool].connect(self.setWidget)
button2 = QtGui.QPushButton('TextEdit', self)
button2.setCheckable(True)
button2.clicked[bool].connect(self.setWidget)
splitter1 = QtGui.QSplitter(QtCore.Qt.Vertical)
splitter1.addWidget(button1)
splitter1.addWidget(button2)
splitter1.addWidget(self.widget)
splitter1.addWidget(self.widget2)
self.widget2.hide()
self.hbox.addWidget(splitter1)
self.setLayout(self.hbox)
self.setGeometry(59, 59, 600, 600)
self.setWindowTitle('Switching QWidgets')
self.show()
def setWidget(self, pressed):
source = self.sender()
if source.text() == "Calendar":
self.widget.show()
self.widget2.hide()
elif source.text() == "TextEdit":
self.widget.hide()
self.widget2.show()

PyQT5 Moving focus between list boxes using arrow keys

I have a QT widget that has two list boxes that are wrapped in QFrames along with a label and placed side-by-side on QWidget. I can move between them using the tab key but I would like to move between them using the left and right arrow keys. What is the best way to do this?
My QWidget and ListWidgets look like the following
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
hbox = QHBoxLayout()
hbox.addWidget(Left())
hbox.addWidget(Right())
self.setLayout(hbox)
self.show()
class Left(QFrame):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
header = QLabel()
header.setText('Left')
l = QListWidget()
items = ['Item %s' % (i + 1) for i in range(10)]
l.addItems(items)
vbox = QVBoxLayout()
vbox.addWidget(header)
vbox.addWidget(l)
self.setLayout(vbox)
class Right(QFrame):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
header = QLabel()
header.setText('Right')
l = QListWidget()
items = ['Item %s' % (i + 1) for i in range(10)]
l.addItems(items)
vbox = QVBoxLayout()
vbox.addWidget(header)
vbox.addWidget(l)
self.setLayout(vbox)
def main():
app = QApplication(sys.argv)
main_window = MainWindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
You can install an event-filter on the list-widgets and then use the focusNextPrevChild method of their parent widget to move the focus:
class MainWindow(QWidget):
...
def init_ui(self):
hbox = QHBoxLayout()
self.left = Left()
self.left.installEventFilter(self)
hbox.addWidget(self.left)
self.right = Right()
self.right.installEventFilter(self)
hbox.addWidget(self.right)
self.setLayout(hbox)
self.show()
def eventFilter(self, source, event):
if (event.type() == QEvent.KeyPress and
(event.key() == Qt.Key_Left or event.key() == Qt.Key_Right) and
event.modifiers() == Qt.NoModifier and
(source is self.left or source is self.right)):
self.focusNextPrevChild(event.key() == Qt.Key_Right)
return True
return super(MainWindow, self).eventFilter(source, event)

How to control QProgressBar with Signal

Pushing the button starts 100 rounds cycle. With QLabel.setText() we update self.label from inside of scope of clicked() function.
Aside from updating self.label we would like to update the progressbar as well.
But since progressbar is a local variable we can't update it from inside of onClick() function.
import time
class ProgressBar(QtGui.QProgressBar):
def __init__(self, parent=None, total=20):
super(ProgressBar, self).__init__(parent=parent)
self.setMinimum(1)
self.setMaximum(105)
self.setTextVisible(True)
def set_to_value(self, value):
self.setValue(value)
QtGui.qApp.processEvents()
def closeEvent(self, event):
self._active=False
class Dialog(QtGui.QDialog):
def __init__(self):
super(QtGui.QDialog,self).__init__()
layout = QtGui.QVBoxLayout()
self.setLayout(layout)
self.label = QtGui.QLabel('idle...')
layout.addWidget(self.label)
progressbar = ProgressBar(self)
layout.addWidget(progressbar)
button = QtGui.QPushButton('Process')
button.clicked.connect(self.onClick)
layout.addWidget(button)
def onClick(self):
for i in range(101):
message = '...processing %s of 100'%i
self.label.setText(message)
QtGui.qApp.processEvents()
time.sleep(1)
if __name__ == '__main__':
app = QtGui.QApplication([])
dialog = Dialog()
dialog.resize(300, 100)
dialog.show()
app.exec_()
Declare the progress bar as:
self.progressbar = ProgressBar(self)
The code declares a local progressbar object connected to a custom 'customSignal` using:
QtCore.QObject.connect(self, QtCore.SIGNAL("customSignal(int)"), progressbar, QtCore.SLOT("setValue(int)"))
with four arguments passed to QtCore.QObject.connect().
The first argument self is the object that will be emitting the signal. Since the function that will do the "sleep-every-second-processing" is declared under the main Dialog instance we pass self here.
The second argument is the name of the signal itself: 'customSignal'.
The progressbar object is used as third and its method setValue as fourth last argument.
class ProgressBar(QtGui.QProgressBar):
def __init__(self, parent=None):
super(ProgressBar, self).__init__(parent=parent)
class Dialog(QtGui.QDialog):
def __init__(self):
super(QtGui.QDialog,self).__init__()
layout = QtGui.QVBoxLayout()
self.setLayout(layout)
self.label = QtGui.QLabel('idle...')
layout.addWidget(self.label)
progressbar = ProgressBar(self)
QtCore.QObject.connect(self, QtCore.SIGNAL("customSignal(int)"), progressbar, QtCore.SLOT("setValue(int)"))
layout.addWidget(progressbar)
button = QtGui.QPushButton('Process')
button.clicked.connect(self.clicked)
layout.addWidget(button)
def clicked(self):
for value in range(101):
message = '...processing %s of 100'%value
self.label.setText(message)
self.emit(QtCore.SIGNAL("customSignal(int)"), value)
QtGui.qApp.processEvents()
time.sleep(1)
if __name__ == '__main__':
app = QtGui.QApplication([])
dialog = Dialog()
dialog.resize(300, 100)
dialog.show()
app.exec_()
--------------------
Here is variation of the same solution except linking to the progressbar method.
import time
class ProgressBar(QtGui.QProgressBar):
def __init__(self, parent=None):
super(ProgressBar, self).__init__(parent=parent)
def set_to_value(self, value):
self.setValue(value)
QtGui.qApp.processEvents()
return True
def closeEvent(self, event):
self._active=False
class Dialog(QtGui.QDialog):
def __init__(self):
QtGui.QDialog.__init__(self, parent=None)
layout = QtGui.QVBoxLayout()
self.setLayout(layout)
self.label = QtGui.QLabel('idle...')
layout.addWidget(self.label)
progressbar = ProgressBar(self)
QtCore.QObject.connect(self, QtCore.SIGNAL("customSignal(int)"), progressbar.set_to_value )
layout.addWidget(progressbar)
button = QtGui.QPushButton('Process')
button.clicked.connect(self.clicked)
layout.addWidget(button)
def clicked(self):
for value in range(101):
message = '...processing %s of 100'%value
self.label.setText(message)
self.emit(QtCore.SIGNAL("customSignal(int)"), value)
QtGui.qApp.processEvents()
time.sleep(1)
if __name__ == '__main__':
app = QtGui.QApplication([])
dialog = Dialog()
dialog.resize(300, 100)
dialog.show()
app.exec_()
======================
This code now links a custom signal to a function referred as Slot in Qt.
from PySide import QtCore, QtGui
import time
class ProgressBar(QtGui.QProgressBar):
def __init__(self, parent=None):
super(ProgressBar, self).__init__(parent=parent)
#QtCore.Slot(int)
def set_to_value(self, value):
self.setValue(value)
QtGui.qApp.processEvents()
return True
def closeEvent(self, event):
self._active=False
class Dialog(QtGui.QDialog):
def __init__(self):
QtGui.QDialog.__init__(self, parent=None)
layout = QtGui.QVBoxLayout()
self.setLayout(layout)
self.label = QtGui.QLabel('idle...')
layout.addWidget(self.label)
progressbar = ProgressBar(self)
# QtCore.QObject.connect(self, QtCore.SIGNAL("customSignal(int)"), progressbar.set_to_value )
QtCore.QObject.connect(self, QtCore.SIGNAL("customSignal(int)"), progressbar, QtCore.SLOT("set_to_value(int)"))
layout.addWidget(progressbar)
button = QtGui.QPushButton('Process')
button.clicked.connect(self.clicked)
layout.addWidget(button)
def clicked(self):
for value in range(101):
message = '...processing %s of 100'%value
self.label.setText(message)
self.emit(QtCore.SIGNAL("customSignal(int)"), value)
QtGui.qApp.processEvents()
time.sleep(1)
if __name__ == '__main__':
dialog = Dialog()
dialog.resize(300, 100)
dialog.show()

Categories