How to disable widgets during handling of a signal in PySide6 [duplicate] - python

This question already has answers here:
PyQt run time issue
(3 answers)
Disabling QPushButton before a task
(1 answer)
PyQT: PushButton receives commands while disabled
(1 answer)
Closed 21 days ago.
During handling of a clicked signal I would like to show a wait cursor until this is done.
All mouse clicks on other widgets should be ignored during that time.
The cursor part is working fine using setCursor().
But all mouse clicks on other buttons (or the original one) are still detected and cached, so they are handled after the first button
operation is finished.
How can I make sure that no user interface events are generated until my operation is finished?
This code shows the things I have already tried:
pushButton_B.setEnabled()
pushButton_B.clicked.disconnect()
pushButton_B.blockSignals()
None of those have led to the desired behaviour.
I am hoping for some way to disable user interface events for the whole window, so I don't need to do this for every
widget I introduce, but I couldn't find the right methods for this.
I'd appreciate any hints on how to do this properly.
import sys
import time
from PySide6.QtCore import Qt, QRect, QCoreApplication, QMetaObject
from PySide6.QtWidgets import QApplication, QWidget, QMainWindow, QPushButton
class Ui_MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.resize(200, 200)
self.centralwidget = QWidget(self)
self.pushButton_A = QPushButton(self.centralwidget)
self.pushButton_A.setText('A')
self.pushButton_A.setGeometry(QRect(20, 20, 100, 24))
self.pushButton_B = QPushButton(self.centralwidget)
self.pushButton_B.setText('B')
self.pushButton_B.setGeometry(QRect(20, 60, 100, 24))
self.setCentralWidget(self.centralwidget)
self.pushButton_A.clicked.connect(self.pushButton_A_clicked)
self.pushButton_B.clicked.connect(self.pushButton_B_clicked)
def pushButton_A_clicked(self):
print("pushButton_A clicked...")
self.setCursor(Qt.CursorShape.WaitCursor)
self.pushButton_B.setEnabled(False)
self.pushButton_B.clicked.disconnect(self.pushButton_B_clicked)
self.pushButton_B.blockSignals(True)
self.repaint()
time.sleep(3) # Some lengthy operation
self.setCursor(Qt.CursorShape.ArrowCursor)
self.pushButton_B.setEnabled(True)
self.pushButton_B.clicked.connect(self.pushButton_B_clicked)
self.pushButton_B.blockSignals(False)
print("pushButton_A done.")
def pushButton_B_clicked(self):
print("pushButton_B clicked.")
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Ui_MainWindow()
w.show()
app.exec()

Related

How can I fix PyQt5 widget crash problem? [duplicate]

This question already has answers here:
Variable scopes in Python classes
(4 answers)
Closed 1 year ago.
I was making a program with Python 3.7 & PyQt5 5.15.4, and I have a sudden crash issue.
This is a simplified version which shows this phenomena.
import sys
from PyQt5.QtWidgets import QWidget, QPushButton, QDialog, QVBoxLayout, QApplication
class WindowA(QWidget):
def __init__(self):
super().__init__()
self.make_layout()
def make_layout(self):
vbox = QVBoxLayout()
self.setLayout(vbox)
class LoginDialog(QDialog):
def __init__(self):
super().__init__()
self.btn_login = QPushButton('Login')
self.btn_login.clicked.connect(self.btn_login_clicked)
vbox = QVBoxLayout()
vbox.addWidget(self.btn_login)
self.setLayout(vbox)
def btn_login_clicked(self):
self.accept()
main_window = WindowA()
main_window.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
login_dialog = LoginDialog()
login_dialog.show()
sys.exit(app.exec_())
This is what I intended:
The program starts with LoginDialog, which contains one login button.
If I push the button, WindowA appears.
However, if I run the code, when I push the login button in LoginDialog, WindowA flashes and the program terminates with no error message.
There is no crash, both windows close so the program is finished running.
LoginDialog closes because QDialog.accept() causes the dialog to close.
WindowA gets garbage collected as soon as the function returns. Use an instance variable so it stays in scope.
def btn_login_clicked(self):
self.main_window = WindowA()
self.main_window.show()

How to capture PyQt5 QMainWindow losing focus

What I want to achieve: if a user clicks outside of the QMainWindow the window should hide.
How I tried to to tackle this problem: find a way to determine if the QMainWindow lost focus, and if so, hide the window using a followup function.
Unfortunately I can not totally grasp how to achieve this.
It can be done using the flag Qt::Popup but than I am not able to give any keyboard input to the widget my QMainWindow contains.
void QApplication::focusChanged(QWidget *old, QWidget *now)
This signal is emitted when the widget that has keyboard focus changed from old to now, i.e., because the user pressed the tab-key, clicked into a widget or changed the active window. Both old and now can be the null-pointer.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class MyWin(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setFocus()
QtWidgets.qApp.focusChanged.connect(self.on_focusChanged)
#QtCore.pyqtSlot("QWidget*", "QWidget*")
def on_focusChanged(self, old, now):
if now == None:
print(f"\nwindow is the active window: {self.isActiveWindow()}")
# window lost focus
# do what you want
self.setWindowState(QtCore.Qt.WindowMinimized)
else: print(f"window is the active window: {self.isActiveWindow()}")
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = MyWin()
MainWindow.show()
sys.exit(app.exec_())

How to use Threading in Python pyqt4 GUI design [duplicate]

This question already has answers here:
Simplest way for PyQT Threading
(2 answers)
Closed 4 years ago.
I have written a simple code of python GUI for sleeping my code for 10 seconds. At the same time, the GUI should run without freezing. Is not possible by defining primari thready and secondary thread? While I click on "Run" button pythonw.exe has stopped working. Does anyone have any thoughts on how I may be able to troubleshoot this? Thank you.
import sys, threading
from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import pyqtSlot
from PyQt4.QtGui import *
class Window(QtGui.QMainWindow):
def __init__(self):
super(Window,self).__init__()
self.setGeometry(400,150, 550, 500)
self.setWindowTitle("my first program")
self.home()
def home(self):
self.button_1 = QtGui.QPushButton('Run', self)
self.button_1.resize(60,25)
self.button_1.move(230,230)
self.button_1.clicked.connect(self.Thread_test)
self.textbox_1 = QTextEdit(self)
self.textbox_1.move(15,290)
self.textbox_1.resize(510,170)
self.show()
def Thread_test(self):
t = threading.Thread(target=self.calculation)
t.start()
def calculation(self):
msg="program is working"
self.textbox_1.setText(msg)
time.sleep(5)
msg="program was slept for few sec."
self.textbox_1.setText(msg)
def run():
app=QtGui.QApplication(sys.argv)
GUI=Window()
sys.exit(app.exec_())
run()
Qt Widgets are not thread safe. You should access a widget only from the thread that created it. See http://doc.qt.io/qt-5/thread-basics.html:
All widgets and several related classes [...] don't work in secondary threads.
You will have to notify your main (GUI) thread to update the UI. You can do so with signals & slots.

Problems with connect in pyqt5

I have a problem. I am writing a simple app in Pyqt5. I am trying to do this block of code in PyQt:
QNetworkAccessManager manager;
QNetworkReply *response = manager.get(QNetworkRequest(QUrl(url)));
QEventLoop event;
connect(response,SIGNAL(finished()),&event,SLOT(quit()));
event.exec();
QString html = response->readAll();
But when I am trying to use "connect" IDE tells me that "MainWindow" don't have method. How can I do it ?? Please help
This is my code:
class MainWindow(QtWidgets.QWidget):
def __init__(self, parent = None):
super(MainWindow, self).__init__()
# window settings
self.setWindowTitle("Hello world app")
# main layout
self.lay = QtWidgets.QVBoxLayout()
# main widgets
self.label = QtWidgets.QLabel("Enter URL:")
self.line = QtWidgets.QLineEdit()
self.label_conn = QtWidgets.QLabel("")
self.btn = QtWidgets.QPushButton("Connect")
self.btn.clicked.connect(self.btn_click)
# adding widgets to layout
self.lay.addWidget(self.label, alignment=QtCore.Qt.AlignBottom)
self.lay.addWidget(self.line)
self.lay.addWidget(self.btn)
self.lay.addWidget(self.label_conn, alignment=QtCore.Qt.AlignTop | QtCore.Qt.AlignCenter)
self.setLayout(self.lay)
self.connect()
The connect method belongs to the signal that you wish to connect to a specific slot, not to the MainWindow widget itself. (BTW, you should consider inheriting from QMainWindow instead.)
In your code, the MainWindow widget is not a signal, so does not have a connect method. Also, even if it did, you need to specify the slot to which you're trying to connect the signal, which is also missing.
In other words, you must declare a pyqtSignal, if you're not using a pre-existing one, and then connect it to the pyqtSlot of your choice. Whether this slot is pre-defined or a custom one is up to you.
Consider the following code snippet, which I tested in Python3:
#!/usr/bin/python3 -B
import sys
from PyQt5.QtWidgets import QApplication, QDialog, QPushButton
if __name__ == '__main__':
app = QApplication(sys.argv)
diag = QDialog()
diag.setWindowTitle('Signal Demo')
diag.resize(200,50)
btn = QPushButton(diag)
btn.setText('Close Dialog')
# connect button's clicked signal to dialog's close slot
btn.clicked.connect(diag.close)
diag.show()
diag.exec_()
Notice that the button's clicked signal, not the button, is what gets connected to the dialog's close slot, not the dialog itself.
EDIT 1:
Just noticed that the very code you've posted has an example of how to properly perform a connection.
If your code has not simply been copy-pasted from some other place, you should've noticed that you seem to know how to properly connect signals and slots already. This line plainly gives it away:
self.btn.clicked.connect(self.btn_click)
If your MainWindow does have a btn_click method, then it should get invoked after the QPushButton named btn gets clicked.
EDIT 2:
Based on your recent comment, you seem to simply be trying to translate a snippet for a larger application, so consider the following code:
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
from PyQt5.QtCore import QEventLoop, QUrl
app = QApplication(sys.argv)
url = 'https://stackoverflow.com'
manager = QNetworkAccessManager()
response = manager.get(QNetworkRequest(QUrl(url)))
event = QEventLoop()
response.finished.connect(event.quit)
event.exec()
html = str(response.readAll()) # in Python3 all strings are unicode, so QString is not defined
print(html)
The code above was tested to work as expected.
PS: I did notice that some seemingly valid URLs were returning an empty response (e.g. http://sourceforge.net/), but others, such as the one above, worked fine. It seems to be unrelated to the code snippet itself.

Query regarding Pyside

In the below mentioned example when I click on 'Help' submenu under 'View' menu multiple times its creating multiple windows. Can anyone tell me how to resolve this issue?
import sys
from PySide import Qt Gui
from PySide.QtCore import Qt
class Window(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.menu_bar()
def menu_bar(self):
helpAction = QtGui.QAction('&Help', self)
helpAction.setShortcut('Ctrl+H')
helpAction.triggered.connect(self.add_helpWindow)
menu = self.menuBar().addMenu('View')
menu.addAction(helpAction)
def add_helpWindow(self):
window = QtGui.QMainWindow(self)
window.setWindowTitle('New Window')
window.show()
if __name__ == '__main__':
import sys
app=QtGui.QApplication.instance()
if not app:
app = QtGui.QApplication(sys.argv)
window = Window()
window.resize(300, 300)
window.show()
sys.exit(app.exec_())
You help window is just a QMainWindow, which is not modal and there are no restrictions on the number that can exist. Hence why if you select the help option multiple times, you get multiple windows.
You likely want to use a QMessageBox which has its modal property set. While there is nothing forcing only one dialog to exist at a time, being modal means that the use can only interact with that window so long as it is open. Example:
from Pyside.QtGui import QMessageBox
def add_helpWindow(self):
help_dialog = QMessageBox.information(self, 'Help', 'Some Help Text Here')
help_dialog.setModal(True)
return help_dialog.exec_()
You can also get a more generic dialog box using QDialog, which is the parent class of QMessageBox.
If that's not the behavior you want, you'll need to manually track whether the user has opened that window before, and then connect a signal that is emitted when the user closes the help window to a slot that reset the existence tracker. Here is an example using a non-modal QDialog:
from Pyside.QtGui import QDialog
class Window(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.menu_bar()
self.help_open = False # Tracks if the help dialog is already open
def help_closed(self):
self.help_open = False
...
def add_helpWindow(self):
if not self.help_open:
self.help_open = True
help_dialog = QDialog(self)
# Any other setup code here
help_dialog.setModal(False)
help_dialog.accepted.connect(self.help_closed)
help_dialog.rejected.connect(self.help_closed)
help_dialog.show()

Categories

Resources