Python error with QFileDialog using pyqt5 library - python

I am trying to create a path browser in pyqt5 using python3 but I have some doubts. My code is this:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
self.horizontalLayoutWidget.setGeometry(QtCore.QRect(300, 150, 160, 80))
self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
self.horizontalLayout.setObjectName("horizontalLayout")
self.label = QtWidgets.QLabel(self.horizontalLayoutWidget)
self.label.setObjectName("label")
self.horizontalLayout.addWidget(self.label)
self.lineEdit = QtWidgets.QLineEdit(self.horizontalLayoutWidget)
self.lineEdit.setObjectName("lineEdit")
self.horizontalLayout.addWidget(self.lineEdit)
self.toolButton = QtWidgets.QToolButton(self.horizontalLayoutWidget)
self.toolButton.setObjectName("toolButton")
self.horizontalLayout.addWidget(self.toolButton)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
# Button action
self.toolButton.pressed.connect(self.selectDirectory(MainWindow, self.lineEdit))
def selectDirectory(self, MainWindow, editText):
editText.setText(str(QtWidgets.QFileDialog.getExistingDirectory(MainWindow, "Select Directory", str(editText.text()))))
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.label.setText(_translate("MainWindow", "Path"))
self.toolButton.setText(_translate("MainWindow", "..."))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
The idea was using a edittext to write the path to the folder but as helper I want to introduce a button to open a file browser in order to select the directory. To achive this I am trying to use QFileDialog without success. My problem at this moment is when I run my application the QFileDialog is displayed and when I choose a folder the application crash. The first thing I do not want to display the QFileDialog without press the button and the second thing I do not understand why the application is crashing with this error:
Traceback (most recent call last):
File "test.py", line 48, in <module>
ui.setupUi(MainWindow)
File "test.py", line 31, in setupUi
self.toolButton.pressed.connect(self.selectDirectory(MainWindow, self.lineEdit))
TypeError: argument 1 has unexpected type 'NoneType'
Thanks for your help

The signal pressed hopes to connect to a slot that does not expect arguments, so you can not do the direct connection with the selectDirectory() function, you must make the connection using a lambda function.
You must change
self.toolButton.pressed.connect(self.selectDirectory(MainWindow, self.lineEdit))
to
self.toolButton.pressed.connect(lambda m=MainWindow, l=self.lineEdit: self.selectDirectory(m, l))

Related

How to properly connect QPushButton clicked signal to pyqtSlot

I'm writing a tool with GUI, where I inevitably need to use pyqtSlot. I had errors in this tool, related to its usage and decided to try a minimal example. However, I still fail to figure out the problem.
I've read instructions here. My findings for creation of a custom slot for a pushbutton were the following:
In a class, defining my GUI, I need to create a method, decorated as #pyqtSlot();
I need to write something like self.mybtn.connect(self.<method name>) after button creation.
So, I created a UI in QtDesigner, compiled it and came up with the following code:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
self.verticalLayout.setObjectName("verticalLayout")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setObjectName("pushButton")
self.pushButton.clicked.connect(self.react_to_signal)
self.verticalLayout.addWidget(self.pushButton)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "PushButton"))
#QtCore.pyqtSlot()
def react_to_signal(self):
print("Button press signal emitted")
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
However, when this code is run, it fails for me with the following error:
QObject::connect: Cannot connect QPushButton::clicked(bool) to (nullptr)::react_to_signal()
Traceback (most recent call last):
File "<mypath>/ui_test_slots.py", line 36, in <module>
ui.setupUi(MainWindow)
File "<mypath>/ui_test_slots.py", line 15, in setupUi
self.pushButton.clicked.connect(self.react_to_signal)
TypeError: connect() failed between clicked(bool) and react_to_signal()
Other questions I saw on SO for this problem were caused by usage of the decorated method as a class method, no an instance method. But this doesn't seem to be my case.
What am I missing? What are the real differences between the example I referred to and my code?

Can a QMenu be used like a Button

I searched some time for this topic but i didnt find anything
I want to use a QMenu like a Button but it has no clicked event and the triggered Event doesent seem to work either.
I tried it like that:
self.ui.menuInfo.triggered.connect(self.MenuInfoClicked)
but nothing happens when i click the Menu.
I have a MainWindow with a QMenuBar and multiple QMenus in it.
But I dont want to open a submenu with QActions i just want to use it as a Button to open a info popup
As for my code, i compiled the ui with pyuic5 and then i imported the UI like that
from PyQt5 import QtWidgets
import sys
import MainWindow
class GUI(QtWidgets.QMainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.ui = MainWindow.Ui_MainWindow()
self.ui.setupUi(self)
self.ui.menuInfo.triggered.connect(self.MenuInfoClicked)
def MenuInfoClicked(self):
print("test!")
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
mainWindow = GUI()
mainWindow.show()
sys.exit(app.exec_())
The UI:
Compiled UI Code (MainWindow.py):
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(634, 415)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 634, 21))
self.menubar.setObjectName("menubar")
self.menuInfo = QtWidgets.QMenu(self.menubar)
self.menuInfo.setObjectName("menuInfo")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.menubar.addAction(self.menuInfo.menuAction())
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.menuInfo.setTitle(_translate("MainWindow", "Info"))
The triggered signal is triggered when a QAction is pressed but in this case there are none. Instead you should use the aboutToShow signal:
self.ui.menuInfo.aboutToShow.connect(self.MenuInfoClicked)

Pyinstaller and PyQt5 macOS Mojave compatibility issues

My application, created with Pyinstaller, worked fine until I upgraded from High Sierra to Mojave. In order to demonstrate the issue I create the simple application.
Main window has only one button. When you press the button its text should be changed to "Please wait" for 10 seconds.
When I run this program as .py script, everything works fine, but after creating .app file with Pyinstaller it behaves differently. The text is not updated until you click anywhere outside of the window.
I tried to reinstall Pyinstaller, but the problem still exists.
from PyQt5 import QtCore, QtGui, QtWidgets
import time
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(303, 304)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setGeometry(QtCore.QRect(50, 80, 300, 43))
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(80, 170, 113, 32))
self.pushButton.setObjectName("pushButton")
self.pushButton.setDefault(True)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
self.pushButton.clicked.connect(self.click)
self.thread = Thread()
self.thread.finished.connect(lambda: self.pushButton.setEnabled(True))
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "Click me"))
def click(self):
if not self.thread.isRunning():
self.pushButton.setEnabled(False)
self.pushButton.setText("Please wait")
self.label.setText("The button below should display \n 'Please wait' for 10 seconds")
self.thread.start()
class Thread(QtCore.QThread):
def run(self):
time.sleep(10)
ui.pushButton.setEnabled(False)
ui.pushButton.setText("Click me")
ui.label.setText("")
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
I found an answer to my question. In order to solve this rendering issue you need to add the following line for a ui element, which needs to be updated. In my case it is required only if I need to run this application on macOS Mojave.
<element>.repaint()
For example:
def click(self):
self.pushButton.setEnabled(False)
self.pushButton.setText("Button is clicked...")
self.pushButton.repaint()

update a python ui without rewrite all the button event

I'm looking for a better way to handle python UI "update" from QtDesigner without overwrite button event. The workflow I got now is:
Design UI layout in QtDesigner
convert .ui to .py by pyuic5
adding button event in .py file
excute .py to see window and button action
So if my UI keep changing the design, how do I keep all the button event I add into .py without being overwrite after convert? Thank you.
Answer my own question, what I found is have three python files. Main.py, CallUI.py and MainWindow.py. (Named as you want.)
So you can keep regenerate UI and override MainWindow.py without clear button event you wrote.
1.Main.py is the one to launch everything, name == "main". Call CAllUI.py's setup function.
#Main.py
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
import CallUI
def setUp():
CallUI.setUpWindow()
raw_input()
if __name__ == "__main__":
setUp()
2.CallUI.py is the one to use "QtWidgets.QApplication(sys.argv)" to show UI and add button click functions.
#CallUI.py
import sys
from MainWindow import Ui_Dialog
from PyQt5 import QtCore, QtGui, QtWidgets
import os
class CallUI(QtWidgets.QWidget):
def __init__(self):
QtWidgets.QWidget.__init__(self)
self.ui = Ui_Dialog()
self.ui.setupUi(self)
self.setUpBtnconnect()
def setUpBtnconnect(self):
self.ui.pushButton.clicked.connect(self.myFunction)
def myFunction(self):
os.system("ipconfig")
raw_input()
def setUpWindow():
app = QtWidgets.QApplication(sys.argv)
nowWindow = CallUI()
nowWindow.show()
sys.exit(app.exec_())
3.MainWindow.py is the one you converted from pyuic5, it's describe all the UI layout.
#MainWindow.py
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("MainWindow")
Dialog.resize(466, 417)
self.centralwidget = QtWidgets.QWidget(Dialog)
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(160, 260, 75, 23))
self.pushButton.setObjectName("pushButton")
self.menubar = QtWidgets.QMenuBar(Dialog)
self.menubar.setGeometry(QtCore.QRect(0, 0, 466, 21))
self.menubar.setObjectName("menubar")
self.statusbar = QtWidgets.QStatusBar(Dialog)
self.statusbar.setObjectName("statusbar")
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
self.pushButton.setText(_translate("MainWindow", "PushButton"))

How do I access a dynamic property in PyQt?

I've created a dynamic property in the Designer interface. How do I access this property in my code?
I don't see any properties listed with the name I've provided. I've found a dynamicPropertyNames property that contains a QByteArray object and the name I provided, but I cannot figure out how to access the data I stored (nor do I know if this is even the correct place to be querying).
Thanks!
Just because I had a similar problem and the reason wasn't a wrong object: The property's content can be accessed with toString().
this answer is just for myself, tried to reproduce the question and answer it.
Here the QtDesigner generated code for a Mainwindow with a DynamicPropierty,
main_win.py :
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'main_win.ui'
#
# Created by: PyQt5 UI code generator 5.12.3
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(270, 20, 261, 131))
self.pushButton.setObjectName("pushButton")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 29))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
MainWindow.setProperty("Prova-proprieta", _translate("MainWindow", "valore_pp")) ### here the dynamic property created in the Designer interface
self.pushButton.setText(_translate("MainWindow", "PushButton"))
here my code printing the value of the dynamic property,
main.py :
import sys
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtWidgets import QPushButton
from main_win import Ui_MainWindow
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.setupUi(self)
print('from inside init : ', self.property('Prova-proprieta'))
self.pushButton.clicked.connect(self.pushButton_1_Pressed)
def pushButton_1_Pressed(self):
self.setProperty('Prova-proprieta', 'changed')
print('from PushButton : ', self.property('Prova-proprieta'))
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
print('from loop : ',window.property('Prova-proprieta'))
window.show()
sys.exit(app.exec_())
Let me know if it right for You

Categories

Resources