Problems with connect in pyqt5 - python

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.

Related

How do I get the text of an QLineEdit into a method?

I'm still not able to understand how to properly connect Qt_pushButton or Qt_LineEdit to methods. I would be so glad to get a explanation which even I do truly understand...
I've put together a pretty basic UI with Qt Designer. It contains a lineEdit called "lineEditTest" which I can indeed change by typing
self.lineEditTest.setText("changed Text")
However, I'm totally stuck with getting the text which the user entered back into my program. I would like to automatically submit the entered text into my function which sets a var to this value and returns it into my UI class. QLineEdit's signal editingFinished sounds perfect for that I guess? But it won't pass the text which the user entered into my function.
QLineEdit does have a property called "text" right? So it seems logical to me that I just have to pass another arg - apart from self - to my function called text.
My code does look like this but it obviously won't work at all:
from PyQt5 import QtWidgets, uic
import sys
class Ui(QtWidgets.QMainWindow):
def __init__(self):
super(Ui, self).__init__()
uic.loadUi('test.ui', self)
self.lineEditTest.setText("test")
self.lineEditTest.editingFinished.connect(self.changeText(text))
self.show()
def changeText(self):
currentText = text
print (currentText)
return currentText
app = QtWidgets.QApplication(sys.argv)
window = Ui()
app.exec_()
This will just crash with:
NameError: name 'text' is not defined`
The problem seems to be that the OP doesn't understand the logic of the signals and slots (I recommend you check here). The signals are objects that emit information, and the slots are functions (generally callable) that are connected to the signals to receive that information. And the information transmitted by the signal depends on each case, for example if you check the docs of editingFinished signal:
void QLineEdit::editingFinished() This signal is emitted when the
Return or Enter key is pressed or the line edit loses focus. Note that
if there is a validator() or inputMask() set on the line edit and
enter/return is pressed, the editingFinished() signal will only be
emitted if the input follows the inputMask() and the validator()
returns QValidator::Acceptable.
That signal does not send any information so do not expect to receive any information except knowing that the edition has ended by the user. So how can I get the text? Well, through the text() method of QLineEdit:
class Ui(QtWidgets.QMainWindow):
def __init__(self):
super(Ui, self).__init__()
uic.loadUi("test.ui", self)
self.lineEditTest.setText("test")
self.lineEditTest.editingFinished.connect(self.changeText)
self.show()
def changeText(self):
text = self.lineEditTest.text()
print(text)
And how to do if the signal sends information? Then the slot (the function) that this connected will have as arguments to that information, for example if you use the textChanged signal that is emitted every time the text of the QLineEdit is changed, it should be as follows:
class Ui(QtWidgets.QMainWindow):
def __init__(self):
super(Ui, self).__init__()
uic.loadUi("test.ui", self)
self.lineEditTest.setText("test")
self.lineEditTest.textChanged.connect(self.changeText)
self.show()
def changeText(self, text):
print(text)
# or
# text = self.lineEditTest.text()
# print(text)
The way you're binding your callback isn't correct. When you bind a callback (and this is true for other frameworks, not just for binding callbacks in PyQt), you want to bind the function which should be triggered when a certain event occurs. That's not what you're doing - you're explicitly invoking the callback self.changeText(text) (note: the parentheses invoke the function). So, you're invoking (calling) the function, evaluating it and placing the return value in ...editingFinished.connect().
Don't explicitly invoke the function. Just pass the function's name. This makes sense if you think about it: we're passing a callable object to connect - this is the function which should be called at a later point in time, when the editingFinished event occurs.
You also don't need to pass lineEditTest's text into the callback, since you can just get whatever text is in the widget via self.lineEditTest.text() from inside the callback.
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets import QMainWindow
class MainWindow(QMainWindow):
def __init__(self):
from PyQt5 import uic
super(MainWindow, self).__init__()
uic.loadUi("mainwindow.ui", self)
self.lineEditTest.setPlaceholderText("Type something...")
self.lineEditTest.editingFinished.connect(self.on_line_edit_finished_editing)
#pyqtSlot()
def on_line_edit_finished_editing(self):
text = self.lineEditTest.text()
print(f"You finished editing, and you entered {text}")
def main():
from PyQt5.QtWidgets import QApplication
application = QApplication([])
window = MainWindow()
window.show()
return application.exec()
if __name__ == "__main__":
import sys
sys.exit(main())

Python3 PyQt5 setEnabled of a QAction causing crashes

For a project I'm creating a GUI using Python 3 and PyQt5. Because it has to be usable by people outside of my immediate team, I want to disable actions on the menu until they've already filled out some forms in other parts of the program (e.g. disabling the final solution view when they haven't set up the initial data connection). The issue is that when I try to call the QAction's setEnabled function outside of the function that created it (but still inside the overall class), it's causing my script to crash with no error code, so I'm having trouble understanding the issue. In the snipit below, I'm trying to set the "View Solution" menu option as true. There are some more options in that menu, but I deleted them here to make it more easy to read.
The code is structured something like this:
import sys
from PyQt5.QtWidgets import QMainWindow, QAction, qApp, QApplication, QMessageBox, QStackedLayout
class MediaPlanner(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
# Menu bar example from: zetcode.com/gui/pyqt5/
exitAction = QAction('&Exit', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.setStatusTip('Exit application')
exitAction.triggered.connect(self.close)
newProject = QAction('&New Project', self)
newProject.setShortcut('Ctrl+N')
newProject.setStatusTip('Start A New Project')
newProject.triggered.connect(self.createNewProject)
openProject = QAction('&Open Project',self)
openProject.setShortcut('Ctrl+O')
openProject.setStatusTip('Open A Saved Project')
openProject.setEnabled(False)
viewSolution = QAction('&View Solution',self)
viewSolution.setStatusTip('View the Current Solution (If Built)')
viewSolution.setEnabled(False)
self.statusBar()
menubar = self.menuBar()
filemenu = menubar.addMenu('&File')
filemenu.addAction(newProject)
filemenu.addAction(openProject)
filemenu.addAction(exitAction)
viewmenu = menubar.addMenu('&View')
viewmenu.addAction(viewSolution)
self.setGeometry(300,300,700,300)
self.setWindowTitle('Menubar')
self.show()
def createNewProject(self):
print('Project Created')
self.viewSolution.setEnabled(True)
if __name__ == '__main__':
app = QApplication(sys.argv)
gui = MediaPlanner()
sys.exit(app.exec_())
The problem is that viewSolution is a variable, but it is not a member of the class so you will not be able to access it through the self instance. One possible solution is to make viewSolution member of the class as shown below:
self.viewSolution = QAction('&View Solution',self)
self.viewSolution.setStatusTip('View the Current Solution (If Built)')
self.viewSolution.setEnabled(False)
...
viewmenu.addAction(self.viewSolution)
Another possible solution is to use the sender() function, this function returns the object that emits the signal, using the following:
def createNewProject(self):
print('Project Created')
self.sender().setEnabled(True)

Python PySide Event Handling

I don't quite understand event handling in PySide.
When I try something like:
exitAction.triggered.connect(self.close)
where exitAction is previously made QAction that will be used to exit program it works just fine. But how come when I make something like
newAction.triggered.connect(QMessageBox.information(self, 'New document', "New document is being created...", QMessageBox.Ok))
it doesn't work? It shows QMessageBox whenever I start program, not when I click that QAction on toolbar/menu bar. How would I go on to implement QMessageBox as a result of clicking on an QAction? Any help is greatly appreciated!
When you connect a signal you must specify the slot to which it is connected without actually calling the slot. Note that in exitAction.triggered.connect(self.close) there are no parentheses after close. If you would write exitAction.triggered.connect(self.close()), the close method would be executed when you connect the signal (at the program start-up). This what happens in your second example.
If there doesn't exist a slot yet that does exactly what you want, you will have to create it yourself (with the parameters that the signal expects). For example:
import sys
from PyQt5.QtWidgets import QMainWindow, QPushButton, QApplication, QMessageBox
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.button = QPushButton('Click me!')
self.setCentralWidget(self.button)
self.button.clicked.connect(self.btnClicked)
def btnClicked(self):
QMessageBox.information(self, 'New document',
"New document is being created...", QMessageBox.Ok)
if __name__ == '__main__':
app = QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
app.exec_()
Note that Qt Signals and Qt Events are not the same! (your title says events but your post is about signals). In general I would use signals and only use events if there is no signal for what you want to do.

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()

How to create a new window button PySide/PyQt?

I'm having problems with a "New Window" function in PyQt4/PySide with Python 2.7. I connected a initNewWindow() function, to create a new window, to an action and put it in a menu bar. Once a common function in desktop software. Instead of giving me a new persistent window alongside the other one the new window pops up and closes. The code I'm working on is proprietary so I created an example that does the same thing with the same error below. Is there any way to get this to work? Runs in PySide with Python 2.7. It was written in and tested in Windows.
from PySide.QtCore import QSize
from PySide.QtGui import QAction
from PySide.QtGui import QApplication
from PySide.QtGui import QLabel
from PySide.QtGui import QMainWindow
from PySide.QtGui import QMenuBar
from PySide.QtGui import QMenu
from sys import argv
def main():
application = QApplication(argv)
window = QMainWindow()
window.setWindowTitle('New Window Test')
menu = QMenuBar(window)
view = QMenu('View')
new_window = QAction('New Window', view)
new_window.triggered.connect(initNewWindow)
view.addAction(new_window)
menu.addMenu(view)
label = QLabel()
label.setMinimumSize(QSize(300,300))
window.setMenuBar(menu)
window.setCentralWidget(label)
window.show()
application.exec_()
def initNewWindow():
window = QMainWindow()
window.setWindowTitle('New Window')
window.show()
if __name__ == '__main__':
main()
If a function creates a PyQt object that the application needs to continue using, you will have to ensure that a reference to it is kept somehow. Otherwise, it could be deleted by the Python garbage collector immediately after the function returns.
So either give the object a parent, or keep it as an attribute of some other object. (In principle, the object could also be made a global variable, but that is usually considered bad practice).
Here's a revised version of your example script that demonstrates how to fix your problem:
from PySide import QtGui, QtCore
class Window(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
menu = self.menuBar().addMenu(self.tr('View'))
action = menu.addAction(self.tr('New Window'))
action.triggered.connect(self.handleNewWindow)
def handleNewWindow(self):
window = QtGui.QMainWindow(self)
window.setAttribute(QtCore.Qt.WA_DeleteOnClose)
window.setWindowTitle(self.tr('New Window'))
window.show()
# or, alternatively
# self.window = QtGui.QMainWindow()
# self.window.setWindowTitle(self.tr('New Window'))
# self.window.show()
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.resize(300, 300)
window.show()
sys.exit(app.exec_())
When initNewWindow() returns, the window variable is deleted and the window's reference count drops to zero, causing the newly created C++ object to be deleted. This is why your window closes immediately.
If you want to keep it open, make sure to keep a reference around. The easiest way to do this is to make your new window a child of the calling window, and set its WA_DeleteOnClose widget attribute (see Qt::WidgetAttribute).

Categories

Resources