I'm trying to build a QtGui application that uses the QtWaitingSpinner found here: https://github.com/z3ntu/QtWaitingSpinner. I have it in a QGridLayout. However, this means that the button next to it changes size when the spinner is unhidden and started. How do I reserve the correct amount of space for the spinner in the grid so that the button next to it stays a constant size regardless of whether the spinner is shown?
Based on this stackoverflow post How to use spacers in Qt, I suspect that the answer involves QSpacerItem. However, I can't figure out how to size the QSpacerItem based on the size the spinner will need.
Here's a minimal example of the code to show my problem:
import PyQt5.QtWidgets as QWidgets
import PyQt5.QtCore as QtCore
import PyQt5.QtGui as QtGui
from waitingspinnerwidget import QtWaitingSpinner
import sys
class Example_Window(QWidgets.QWidget):
def __init__(self):
super(QWidgets.QWidget,self).__init__()
self.initUI()
def initUI(self):
self.button=QWidgets.QPushButton("Start/Stop Spinner")
self.button.clicked.connect(self.toggle_spinner)
self.spinner = QtWaitingSpinner(self,centerOnParent=False)
self.grid = QWidgets.QGridLayout()
self.grid.addWidget(self.button,0,0)
self.grid.addWidget(self.spinner,0,1)
self.setLayout(self.grid)
self.show()
def toggle_spinner(self):
if self.spinner.isSpinning():
self.spinner.stop()
else:
self.spinner.start()
if __name__ == '__main__':
app = QWidgets.QApplication([])
main = Example_Window()
sys.exit(app.exec())
Try it:
import PyQt5.QtWidgets as QWidgets
import PyQt5.QtCore as QtCore
import PyQt5.QtGui as QtGui
from waitingspinnerwidget import QtWaitingSpinner
import sys
class Example_Window(QWidgets.QWidget):
def __init__(self):
super(QWidgets.QWidget,self).__init__()
self.initUI()
def initUI(self):
self.button = QWidgets.QPushButton("Start Spinner") # +
self.button.clicked.connect(self.toggle_spinner)
self.spinner = QtWaitingSpinner(self, centerOnParent=False)
self.grid = QWidgets.QGridLayout()
self.grid.addWidget(self.button, 0, 0)
# self.grid.addWidget(self.spinner,0,1) # ---
self.grid.addWidget(self.spinner, 0, 1, 1, 2) # +++ <---
self.setLayout(self.grid)
self.show()
def toggle_spinner(self):
if self.spinner.isSpinning():
self.spinner.stop()
self.button.setText("Start Spinner") # +
else:
self.spinner.start()
self.button.setText("Stop Spinner") # +
if __name__ == '__main__':
app = QWidgets.QApplication([])
main = Example_Window()
main.resize(170, 70) # +++
sys.exit(app.exec())
Related
I've tried several methods to stop a Qthread, but none seem to work. When I look at the Call Stack, the thread has not ended, and while click again, another thread starts, thus getting infinite active threads and consuming more unnecessary memory. I tried to put terminate() and quit() and nothing stops the thread. So I also tried all of them together and no result
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtCore import QThread
from PyQt5 import QtWidgets
from PyQt5 import QtCore
import time
import sys
class MyThread(QThread):
def run(self):
x=0
print("Starting...")
while x<5:
time.sleep(1.5)
print("Running...")
x=x+1
def close(self):
self.isRunning=False
self.terminate()
self.wait()
self.quit()
self.wait()
print("Done")
class Ui_MainWindow(QMainWindow):
def __init__(self):
super(Ui_MainWindow, self).__init__()
self.btn = QtWidgets.QPushButton(self)
self.btn.setGeometry(QtCore.QRect(77, 30, 50, 30))
self.btn.setText("CLICK")
self.btn.clicked.connect(self.doSomething)
def doSomething(self,event):
self.worker=MyThread()
self.worker.setTerminationEnabled(True)
self.worker.finished.connect(self.worker.close)
self.worker.start()
if __name__ == "__main__":
app = QApplication([])
ui = Ui_MainWindow()
ui.show()
sys.exit(app.exec_())
I managed to solve my problem using threading native library and with pyqtSignal. When clicking on the button, it loses its connection and starts the thread. After the end of the loop in the thread, a signal is issued and thus returning the connection to the button. This way, it prevents the user from clicking the button several times.
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtCore import pyqtSignal
from PyQt5 import QtWidgets
from PyQt5 import QtCore
import time
import sys
import threading
class Ui_MainWindow(QMainWindow):
signal=pyqtSignal(str)
def __init__(self):
super(Ui_MainWindow, self).__init__()
self.btn = QtWidgets.QPushButton(self)
self.btn.setGeometry(QtCore.QRect(77, 30, 50, 30))
self.btn.setText("CLICK")
self.signal.connect(self.signalResponse)
self.btn.clicked.connect(self.doSomething)
def doSomething(self,event):
print("Starting...")
self.btn.disconnect()
worker=threading.Thread(target=self.worker)
worker.start()
def signalResponse(self,response):
if response=="Finished":
self.btn.clicked.connect(self.doSomething)
print(response)
def worker(self):
x=0
while x<5:
time.sleep(0.5)
print("Running...")
x=x+1
self.signal.emit("Finished")
if __name__ == "__main__":
app = QApplication([])
ui = Ui_MainWindow()
ui.show()
sys.exit(app.exec_())
I am trying to edit the text in 'QTextEdit' but I got the error which is in the title above. I know that I shouldn't edit the widgets in the main app from another thread and I searched for some answers, But I didn't get the full answer that I want
This is a sample that produce the same error that I have
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5 import QtCore
import sys
import time
import threading
class Main(QMainWindow):
def __init__(self):
super().__init__()
self.move(50, 220)
self.resize(500, 500)
self.text_edit = QTextEdit(self)
self.text_edit.resize(200, 200)
self.btn = QPushButton('start', self)
self.btn.move(10, 250)
self.btn.clicked.connect(lambda x : self.thread_creator(self.test))
self.btn2 = QPushButton('print', self)
self.btn2.move(10, 290)
self.btn2.clicked.connect(lambda x : print('dad'))
def test(self):
for i in range(99):
self.text_edit.setText(f'dad {i}')
time.sleep(0.1)
def thread_creator(self, function):
self.thread = threading.Thread(target=function)
self.thread.start()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Main()
ex.show()
sys.exit(app.exec_())
so I'm currently working on a little project but I have an issue and all what I've tried did not work. I have 2 files :
Page1test :
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QPushButton, QWidget, QLabel
import sys
from page2test import Page2
class Page1(QWidget):
def __init__(self):
super(Page1, self).__init__()
self.setWindowTitle("Page 1")
label1 = QLabel(self)
label1.setText("\n PAGE 1")
self.btn_inMyApp = QPushButton ('Next page', self)
self.btn_inMyApp.setGeometry(1500,800,275,125)
self.btn_inMyApp.clicked.connect(self.closePage1_OpenPage2)
self.show()
def closePage1_OpenPage2(self):
self.Open = Page2()
self.Open.showMaximized()
self.close()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = Page1()
window.showMaximized()
sys.exit(app.exec_())
And page2test :
from PyQt5.QtWidgets import QPushButton, QWidget, QLabel
from Page1test import Page1
class Page2(QWidget):
def __init__(self):
super(Page2, self).__init__()
self.setWindowTitle("Test window principale")
label1 = QLabel(self)
label1.setText("\n Page2")
self.btn_inMyApp = QPushButton ('previous Page', self)
self.btn_inMyApp.setGeometry(1500,800,275,125)
self.btn_inMyApp.clicked.connect(self.closePage2_OpenPage1)
self.show()
def closePage2_OpenPage1(self):
self.Open = Page1()
self.Open.showMaximized()
self.close()
I run the code of Page1test : empty window with just a Qpushbutton "Next Page", goal : we click on it and it open Page 2 (and close Page 1). And, when we are in Page 2, we have A Qpushbutton with "Previous Page" and when we click on it, it opens page 1, and close Page 2. Like a loop.
But, when I run the code, it returns an error :
cannot import name 'Page2' from partially initialized module 'page2test' (most likely due to a circular import)
and I have no idea how to fix it...
If someone had an idea, it would be really helpful.
So, I've finally found the solution, that was not so difficult in the end. Here it is (if it can help someone) :
Instead of making 2 files, I've done just 1 file. Here is the code :
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QPushButton, QWidget, QLabel, QMainWindow
import sys
class MyApp(QWidget):
def __init__(self):
super(MyApp, self).__init__()
self.setWindowTitle("Page 1")
label1 = QLabel(self)
label1.setText("\n PAGE 1")
self.btn_inMyApp = QPushButton ('Page suivante', self)
self.btn_inMyApp.setGeometry(1500,800,275,125)
self.btn_inMyApp.clicked.connect(self.closePage1_OpenPage2)
self.show()
def btn1_onClicked(self):
pass
def closePage1_OpenPage2(self):
self.Open = NewApp()
self.Open.showMaximized()
self.close()
class NewApp(QMainWindow):
def __init__(self):
super(NewApp, self).__init__()
self.setWindowTitle("Test window principale")
label1 = QLabel(self)
label1.setText("\n Page2")
self.btn_inMyApp = QPushButton ('previous Page', self)
self.btn_inMyApp.setGeometry(1500,800,275,125)
self.btn_inMyApp.clicked.connect(self.closePage2_OpenPage1)
self.show()
def closePage2_OpenPage1(self):
self.Open = MyApp()
self.Open.showMaximized()
self.close()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = MyApp()
window.showMaximized()
sys.exit(app.exec_())
```
I want to display the logged in user os.getlogin() displayed in a label widget.
So far I need to set a custom signal and connect it to the label
here is my (not functioning) code, hope you can guide my how to write it properly
import sys
import os
from PyQt5 import QtWidgets as qtw
from PyQt5 import QtCore as qtc
from PyQt5 import QtGui as qtg
class UserTest(qtw.QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# your code will go here
user_in_label = qtw.QLabel()
layout = qtw.QVBoxLayout()
layout.addWidget(user_in_label)
# funktionen
self.trigger = qtc.pyqtSignal(str)
# fire signal to slot
self.set_signal.connect(user_in_label.setText)
# your code ends here
self.show()
def set_signal(self):
active_user = os.getlogin()
self.trigger.emit(active_user)
if __name__ == '__main__':
app = qtw.QApplication(sys.argv)
w = UserTest()
sys.exit(app.exec_())
self.set_signal.connect(user_in_label.setText)
this line is missing an object, do I need to set a new class for this take, seems like nonsense
Try it:
import sys
import os
from PyQt5 import QtWidgets as qtw
from PyQt5 import QtCore as qtc
from PyQt5 import QtGui as qtg
class UserTest(qtw.QWidget):
trigger = qtc.pyqtSignal(str) # +++
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
user_in_label = qtw.QLabel()
layout = qtw.QVBoxLayout(self) # + self
layout.addWidget(user_in_label)
layout.addWidget(qtw.QPushButton('Click me', clicked=self.set_signal)) # +++
self.trigger.connect(user_in_label.setText) # +++
def set_signal(self):
active_user = os.getlogin()
self.trigger.emit(active_user)
if __name__ == '__main__':
app = qtw.QApplication(sys.argv)
w = UserTest()
w.show()
sys.exit(app.exec_())
Update
can you explain why you relocate trigger = qtc.pyqtSignal(str) above init ? and any suggestions how to set active user automatically to the label without the push button?
trigger must be a class attribute
import sys
import os
from PyQt5 import QtWidgets as qtw
from PyQt5 import QtCore as qtc
from PyQt5 import QtGui as qtg
class UserTest(qtw.QWidget):
trigger = qtc.pyqtSignal(str)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
user_in_label = qtw.QLabel()
layout = qtw.QVBoxLayout(self)
layout.addWidget(user_in_label)
# layout.addWidget(qtw.QPushButton('Click me', clicked=self.set_signal))
self.trigger.connect(user_in_label.setText)
self.set_signal() # +++
def set_signal(self):
active_user = os.getlogin()
self.trigger.emit(active_user)
if __name__ == '__main__':
app = qtw.QApplication(sys.argv)
w = UserTest()
w.show()
sys.exit(app.exec_())
I want to use startup function which should have while loop.
but I run the code my gui doesn't appear until while loop ends.
I tried with self.show() it can make show gui but it doesn't allow to use sys.exit()
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5 import uic
import time
form_class,QMainWindow=uic.loadUiType('youhua.ui')
class MyWindow(QMainWindow,form_class):
def __init__(self):
super().__init__()
self.setupUi(self)
#self.show()
self.myfunc()
def myfunc(self):
k=1
stat=True
while stat:
k=k+1
time.sleep(1)
self.statusMessage.append(str(k))
QApplication.processEvents()
if k>10:
stat=False
#sys.exit()
if __name__=='__main__':
app=QApplication(sys.argv)
myWindow=MyWindow()
myWindow.show()
app.exec_()
If you need to perform an action again, you have several options.
For example, if each iteration takes very little time, without the possibility of blocking the main loop, you can replace the cycle with a timer (QTimer) and call the method each time, which is responsible for obtaining new data and updating the necessary interface elements in accordance with them:
import sys
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5 import uic
from PyQt5.QtCore import QThread, QTimer
import time
#form_class, QMainWindow = uic.loadUiType('youhua.ui')
class MyWindow(QMainWindow): #, form_class):
def __init__(self):
super().__init__()
self.k = 0
centralWidget = QtWidgets.QWidget(self)
self.setCentralWidget(centralWidget)
self.button = QtWidgets.QPushButton('Start', self)
self.button.clicked.connect(self.read_data)
self.label_data = QtWidgets.QLabel(self, alignment=QtCore.Qt.AlignCenter)
self.label_data.setText('Pending')
layout = QtWidgets.QGridLayout(centralWidget)
layout.addWidget(self.label_data)
layout.addWidget(self.button)
self.timer = QtCore.QTimer(self)
self.timer.setInterval(1000)
self.timer.timeout.connect(self.read_data_from_sensor)
#QtCore.pyqtSlot()
def read_data(self):
''' Start / Stop reading at the touch of a button '''
if not self.timer.isActive():
self.timer.start()
self.button.setText("Stop")
else:
self.timer.stop()
self.button.setText("Start")
self.label_data.setText("Pending")
#QtCore.pyqtSlot()
def read_data_from_sensor(self):
dt = time.strftime("%Y-%m-%d %H:%M:%S")
self.label_data.setText(dt)
self.label_data.adjustSize()
self.k += 1
self.statusBar().showMessage('{} item(s)'.format(self.k))
if self.k > 10:
self.timer.stop()
self.button.setText("Start")
self.label_data.setText("Pending")
self.k = 0
if __name__=='__main__':
app = QApplication(sys.argv)
myWindow = MyWindow()
myWindow.show()
app.exec_()
What you wrote may also work, but this is not very good. You can compare.
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5 import uic
from PyQt5.QtCore import QThread
#import time
#form_class, QMainWindow = uic.loadUiType('youhua.ui')
class MyWindow(QMainWindow): #, form_class):
def __init__(self):
super().__init__()
# self.setupUi(self)
self.show()
self.myfunc()
def myfunc(self):
k = 0
stat = True
while stat:
k += 1
# time.sleep(1)
# self.statusMessage.append(str(k))
self.statusBar().showMessage('{} item(s)'.format(k))
QThread.msleep(1000)
QApplication.processEvents()
if k>10:
stat=False
#sys.exit()
if __name__=='__main__':
app = QApplication(sys.argv)
myWindow = MyWindow()
# myWindow.show()
app.exec_()
In your loop you are sleeping for 10 second, since you are creating a while loop on the main thread, the GUI wont show until the loop is done because it would be blocking the main thread. You can test this by removing time.sleep(1).
Without changing your code much, try this:
import sys,threading, time
from PyQt5.QtWidgets import QApplication
from PyQt5 import uic
form_class,QMainWindow=uic.loadUiType('youhua.ui')
class MyWindow(QMainWindow,form_class):
def __init__(self):
super().__init__()
self.setupUi(self)
#self.show()
t = threading.Thread(target=self.myfunc)
t.start()
def myfunc(self):
k=1
stat=True
while stat:
k=k+1
time.sleep(1)
self.statusMessage.append(str(k))
QApplication.processEvents()
if k>10:
stat=False
#sys.exit() - if you are trying to close the window here use self.close()
if __name__=='__main__':
app=QApplication(sys.argv)
myWindow=MyWindow()
myWindow.show()
sys.exit(app.exec_())