I want to get the submenu That is QuitProgram <- This is name of submenu but it is in UI file.
How can I get it in a variable set its action to quit program?
File menu object name is 'actionQuit_FromProgram' <- This is a submenu.
Python File:
from PyQt5 import QtWidgets, uic
from PyQt5.QtWidgets import QFileDialog, QMenuBar
from PyQt5.QtGui import *
import sys
import os
import qdarkgraystyle
path = os.path.abspath(os.getcwd())
import qrcode
class Ui(QtWidgets.QMainWindow):
def __init__(self):
super(Ui, self).__init__()
uic.loadUi('main.ui', self)
#self.show()
self.button = self.findChild(QtWidgets.QPushButton, 'qrgenerator')
self.button.clicked.connect(self.QrCodeGenerator) # Remember to pass the definition/method, not the return value!
self.input = self.findChild(QtWidgets.QLineEdit, 'qredit')
self.button1 = self.findChild(QtWidgets.QPushButton, 'qropen')
self.button1.clicked.connect(self.OpenQRCode)
self.menu1 = self.findChild(QMenuBar,'actionQuit_FromProgram')
print(self.menu1)
#self.menu1.triggered.connect(qApp.quit)
self.show()
def QrCodeGenerator(self):
# Generate QR code
self.url = qrcode.make(self.qredit.text())
if self.qredit.text() == '':
QMessageBox.warning(self, "Error", "Please Type In Something To Generate Qr Code")
else:
self.url.save("filename.png","PNG")
def OpenQRCode(self):
fname = QFileDialog.getOpenFileName(self, 'Open file',
path,"Image files (*.jpg *.gif *.png *.svg)")[0]
self.label_2.setPixmap(QPixmap(fname))
app = QtWidgets.QApplication(sys.argv)
app.setStyleSheet(qdarkgraystyle.load_stylesheet())
window = Ui()
app.exec_()
If you're using loadUi (or setupUi if you're using files generated by pyuic), all elements in the object inspector (the tree view that lists all widgets on your UI) become available as instance attributes, according to their object name.
So, if your action is called actionQuit_FromProgram in the inspector, you can directly access it using self.actionQuit_FromProgram.
This also means that:
all those findChild are absolutely useless: you already have access to self.qrgenerator, self.qredit, etc;
in any case, findChild should be used with the correct class of the object you're looking for: I sincerely doubt that actionQuit_FromProgram is a QMenuBar (so, using findChild(QMenuBar, ...) won't work at all; if it is an action, use findChild(QAction, ...); if it's a submenu, use findChild(Qmenu, ...);
there's usually just one menu bar for each QMainWindow, and it is easily accessible using self.menuBar();
Related
I am trying to build a simple GUI using PyQT5, with 3 buttons to open file browsers and one more to run processing with the selected files, but I can't get my buttons to connect to the functions needed to carry this out.
In the Ctrl class, the _connect_signals function doesn't seem to be calling _input_select. Can anyone help me figure out why?
import sys
# Import QApplication and the required widgets from PyQt5.QtWidgets
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtWidgets import QVBoxLayout
from PyQt5.QtWidgets import QWidget
from PyQt5.QtWidgets import QFileDialog
# Create a subclass of QMainWindow to setup the calculator's GUI
class UI(QMainWindow):
"""App's View (GUI)."""
def __init__(self):
"""View initializer."""
super().__init__()
# Set some main window's properties
self.setFixedSize(300, 150)
# Set the central widget and the general layout
self.generalLayout = QVBoxLayout()
self._centralWidget = QWidget(self)
self.setCentralWidget(self._centralWidget)
self._centralWidget.setLayout(self.generalLayout)
# Create the buttons
self._create_buttons()
def _create_buttons(self):
"""Create the buttons."""
self.buttons = {}
buttons_layout = QVBoxLayout()
# Button text | position on the QVBoxLayout
buttons = {
"Select input file...": 0,
"Select config file...": 1,
"Select output file...": 2,
"Run": 3,
}
# Create the buttons and add them to the grid layout
for btnText, pos in buttons.items():
self.buttons[btnText] = QPushButton(btnText)
buttons_layout.addWidget(self.buttons[btnText], pos)
# Add buttons_layout to the general layout
self.generalLayout.addLayout(buttons_layout)
# Create a Controller class to connect the GUI and the model
class Ctrl:
"""App's Controller."""
def __init__(self, setup, view):
"""Controller initializer."""
self._view = view
self._setup = setup
# Connect signals and slots
self._connect_signals()
def _input_select(self): # Not being called
print("input selection")
options = QFileDialog.Options()
file_select, _ = QFileDialog.getOpenFileNames(
self,
'Select Input File...',
'',
'CSV Files (*.csv);;All Files (*)',
options=options
)
if file_select:
self._setup["input"] = file_select
def _connect_signals(self):
"""Connect signals and slots."""
self._view.buttons["Select input file..."].clicked.connect(self._input_select) # Not working!
# Client code
def main():
"""Main function."""
# Create an instance of `QApplication`
app = QApplication(sys.argv)
# Show the app's GUI
view = UI()
view.show()
setup = {}
# Create instance of the controller
Ctrl(setup=setup, view=view)
# Execute app's main loop
sys.exit(app.exec_())
if __name__ == "__main__":
main()
In case it helps, I started out by butchering this example code from a Real Python tutorial, but must have broken it along the way.
The problem is that you are not keeping any persistent reference to the Ctrl() instance you are creating. This results in python garbage collecting it as soon as the instance is created.
To solve the issue, just assign it to a variable:
def main():
"""Main function."""
# Create an instance of `QApplication`
app = QApplication(sys.argv)
# Show the app's GUI
view = UI()
view.show()
setup = {}
# Create instance of the controller
ctrl = Ctrl(setup=setup, view=view)
# Execute app's main loop
sys.exit(app.exec_())
Some considerations:
while separating logic from interface is usually good practice, it's a concept that needs to be used with care, as sometimes it only makes things more complex than they should be. Most of the times (especially with simple programs), it only makes a bigger codebase without giving any actual benefit: it's harder to read and to debug, and you'll probably end up continuously switching from the logic parts and the ui parts of your code;
your code shows one of the drawback of that concept: when you create the file dialog, you're using self, but in that case it refers to the Ctrl instance, while the argument should be the UI instance instead (which will result in a crash, as Qt will get an unexpected argument type); you can use self._view instead, but, as said, the whole separation in this case just makes things unnecessarily complex;
using strings for dictionary keys that refer to internal objects is rarely a good idea (especially when using long descriptive strings like you did);
when importing more than one element from a module, it's usually better to group them instead of using single line imports: it makes the code tidier and easier to read and inspect: from PyQt5.QtWidgets import (QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget, QFileDialog)
I'm trying to make a GUI for a small program I wrote with the help of some people from here, anyway, I made the GUI in PyQt and it looks fine. I added a button called dirButton that says "Choose Directory"
self.dirButton = QtGui.QPushButton(self.buttonWidget)
self.dirButton.setGeometry(QtCore.QRect(0, 0, 91, 61))
self.dirButton.setObjectName(_fromUtf8("dirButton"))
self.dirButton.clicked.connect(self.browse)
and in the bottom line there I've made it call self.browse when I click it, which is:
def browse(self):
filename = QtGui.QFileDialog.getOpenFileName(self, 'Open File', '.')
fname = open(filename)
data = fname.read()
self.textEdit.setText(data)
fname.close()
However, this is the error I get:
Traceback (most recent call last):
File "C:\Users\Kevin\Desktop\python-tumblr-0.1\antearaGUI.py", line 88, in browse
filename = QtGui.QFileDialog.getOpenFileName(self, 'Open File', '.')
TypeError: QFileDialog.getOpenFileName(QWidget parent=None, QString caption=QString(), QString directory=QString(), QString filter=QString(), QString selectedFilter=None, QFileDialog.Options options=0): argument 1 has unexpected type 'Ui_mainWindow'
So, ui_mainWindow is the class that all of my GUI buttons and the GUI itself is stored in.
class Ui_mainWindow(object):
I don't understand why I'm getting an error, does anyone have any ideas?
Here is a pastebin link to the entire GUI: http://pastebin.com/BWCcXxUW
As I understand, you are using Ui_mainWindow generated from .ui file. As you can see Ui_mainWindow is just python class which contains widgets. getOpenFileName recieves QWidget instance as first parameter. So you need to subclass QWidget or QMainWindow and define methods in that class.
Code will look like this:
import sys
from PyQt4 import QtCore, QtGui
from file_with_ui import Ui_MainWindow
class Main(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.setupUi(self)
def browse(self):
filename = QtGui.QFileDialog.getOpenFileName(self, 'Open File', '.')
fname = open(filename)
data = fname.read()
self.textEdit.setText(data)
fname.close()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = Main()
window.show()
sys.exit(app.exec_())
Alternatively you can store ui as instance attribute:
class Main(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.ui=Ui_MainWindow()
self.ui.setupUi(self)
And acces your controls through self.ui, e.g.: self.ui.textEdit.setText(data)
Consider reading tutorial about pyuic usage PyQt by Example (Session 1)
import the following:
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4 import QtGui, QtCore
In class Ui_MainWindow(object), replace object with QWidget:
Ui_MainWindow(QWidget)
def browseFiles(self):
text, placeholder = QFileDialog.getOpenFileName(None, 'Select file')
The way it worked out for me is to use None, rather than self, as self inherits the type of the window above it, which is expected to be None, but is given QMainWindow as a type.
I also unpacked the tuple it returns, since this function returns two objects to work with.
I hope this helps, perhaps someone else could explain this better, as to why it works.
TLDR: The function expects None as the first parameter, but inherits a QmainWindow as type, due to self.
file = str(QFileDialog.getExistingDirectory(None, "Select Directory"))
I am trying to return a path from a browse folder dialog box.
I have tried passing the instance object or even attribute to the call and setting it there:
self.browseBtn.clicked.connect(myExporter.browseFolder(self))
or
self.browseBtn.clicked.connect(myExporter.browseFolder(self.path))
But this doesn't work. It causes the browser dialog to pop open immediately upon load and then once you choose a folder it errors out with : Failed to connect signal clicked().
I have tried to set the clicked call to a return, with no luck:
result = self.browseBtn.clicked.connect(myExporter.browseFolder)
Can someone lead me in the right direction as far as how to return a value, when you are dealing with separate classes handling the UI and logic? Also... is it bad practice to be separating them like this? I know I could probably easily solve this if I threw everything into just one python file, but I figured that is not proper.
Here is my ui file (ui.py):
from PySide import QtCore, QtGui
class Ui_Dialog(object):
def __init__(self):
self.path =""
def setupUi(self, Dialog, myExporter):
Dialog.setObjectName("Dialog")
Dialog.resize(382, 589)
...
.......
.............
.................
self.retranslateUi(Dialog)
self.tabWidget.setCurrentIndex(1)
QtCore.QMetaObject.connectSlotsByName(Dialog)
self.browseBtn.clicked.connect(myExporter.browseFolder)
Here is my exporter file (exporter.py):
class Exporter(object):
def __init__(self):
...
......
def browseFolder(self):
...
.......
do something
...........
return path
Here is my load/test file (loadExporter.py):
import ui as interface
import exporter as exporter
from PySide import QtCore, QtGui
app = QtGui.QApplication.instance()
if app is None:
app = QtGui.QApplication(sys.argv)
Dialog = QtGui.QDialog()
myExporter = exporter.Exporter()
myUI = interface.Ui_Dialog()
myUI.setupUi(Dialog, myExporter)
Dialog.show()
app.exec_()
It's not necessarily bad to have them in separate files. Having a separate file for certain widgets is a good thing especially if those widgets can be reused.
I would have my main file have a QMainWindow class.
class MyWindow(QtGui.QMainWindow):
pass
if __name__ == "__main__":
QtGui.QApplication([])
mywindow = MyWindow()
mywindow.show()
sys.exit(QtGui.qApp.exec_())
Wrapping the app functionality in if __name__ == "__main__" prevents this code from being run when another file tries to import this file.
A signal (self.browserBtn.clicked) calls a callback method. Everything is an object in python.
def my_func():
pass
new_func_name = my_func # my_func can be reassigned like any variable
my_func is an object. self.browseBtn.clicked.connect(my_func) passes my_func as a variable to be called later.
When self.browserBtn.clicked.emit() happens (on user click) it is the same as calling the connected functions my_func(). Other signals may pass values to callback functions self.lineEdit.textChanged.connect(my_func) calls 'my_func(new_text)'
You want your function to call everything for you.
def open_file(filename=None):
"""Open a file."""
# If a filename isn't given ask the user for a file
if filename is None:
filename, ext = QtGUi.QFileDialog.getOpenFileName(None, "Open File", ".../My Documents/")
# You may have to use the ext to make a proper filename
# Open the file
with open(filename, "r") as file:
file.read()
self.browserBtn.clicked.connect(open_file)
Structure
...
import mywidget
class MyWindow(QtGui.QMainWindow):
def __init__(self):
super().__init__()
...
self.show_file = QtGui.QLineEdit()
self.setCentralWidget(self.show_file)
self.exporter = mywidget.Exporter()
self.browserBtn.clicked.connect(self.open_file)
def open_file(self, filename=None):
"""Open a file."""
path = self.exporter.browseFolder()
# Do something with the path
self.show_file.setText(path)
if __name__ == "__main__":
QtGui.QApplication([])
mywindow = MyWindow()
mywindow.show()
sys.exit(QtGui.qApp.exec_())
I don't think I fully understand what you're trying to achieve, but may I suggest the following solution.
exporter.py
# implementation
dialog.py (Main UI)
import PyQt4.QtGui as qg
from exporter import Exporter
class Dialog(qg.QDialog):
def __init__(self):
super().__init__()
self.path = None
self.setup_widgets()
def setup_widgets(self):
self.browse.clicked.connect(self.on_browse_clicked)
def on_browse_clicked(self):
self.path = Exporter().browse_folder()
main.py
import sys
import PyQt4.QtGui as qg
from dialog import Dialog
if __name__ == '__main__':
app = qg.QApplication(sys.argv)
dialog = Dialog()
dialog.show()
sys.exit(app.exec_())
This way you still have three files, but dialog.py imports exporter.py instead of main.py importing both of them.
As for returning values from signals, I don't know; I think signals are voids (do not return values).
Signals pass arguments to slots via their formal parameters. Usually slots have the same or fewer parameters than their signal counterparts'. It's slots that return values.
This question already has an answer here:
How do I load children from .ui file in PySide?
(1 answer)
Closed 12 months ago.
So I have an application where I'm considering moving from PyQt4 to PySide. In this application, I use .ui files pretty frequently, with the following usage pattern:
class BaseGUIWidget(QWidget):
def __init__(self, parent = None, ui_file = None):
'''
:param parent: parent widget of this widget
:param ui_file: path to UI file to load (optional)
'''
super(BaseGUIWidget, self).__init__(parent)
if ui_file is not None:
uic.loadUi(ui_file, self)
Let's assume I have similar classes for QFrame, QMainWindow, QGroupBox, etc.
This allows me to create python classes that use the data from the UI file, as well as any additional functionality I add manually in the code. Essentially, my BaseGUIWidget class acts as if it was extending the class generated by the UI file. A lot of the functionality in the application is pretty reliant on this behavior.
However, from what I can tell, PySide's QUIloader doesn't have a similar functionality. Instead of 'shimming' the UI file's contents into your class, it simply builds a widget from the UI file internally, then returns it, and you then embed it into your widget in a layout like you would any other widget., I.E:
class BaseGUIWidget(QWidget):
def __init__(self, parent = None, ui_file = None):
'''
:param parent: parent widget of this widget
:param ui_file: path to UI file to load (optional)
'''
super(BaseGUIWidget, self).__init__(parent)
self.setLayout(QVBoxLayout())
if ui_file is not None:
loader = QUILoader()
uifile = QFile(ui_file)
uifile.open(QFile.ReadOnly)
self.ui_widget = loader.load(ui_file, self)
self.layout().addWidget(self.ui_widget)
uifile.close()
This is a pretty massive difference. If, for example, you wanted your UI file to contain a QMainWindow and your python class to still be an extension of QMainWindow so it acts like one to other classes, you'd wind up with a QMainWindow inside a QMainWindow, which isn't what you want. It also means you have to do widget.ui_widget.XXX to access the widgets produced by the UI file.
Is there any way to make PySide's uic implementation act like PyQt's?
use QtPy package
pip3 install QtPy
it autodetect used binding(pyqt5 or pyside2)
from QtPy import uic # and all other QT modules
w = uic.loadUi("mywidget.ui")
w.show()
Yes it is possible.
I know it is old question, but suddenly someone will come in handy.
I found this solution on:
https://robonobodojo.wordpress.com/2017/10/03/loading-a-pyside-ui-via-a-class/
ui_loader.py:
#!/usr/bin/env python
from PySide.QtUiTools import QUiLoader
from PySide.QtCore import QMetaObject
class UiLoader(QUiLoader):
def __init__(self, base_instance):
QUiLoader.__init__(self, base_instance)
self.base_instance = base_instance
def createWidget(self, class_name, parent=None, name=''):
if parent is None and self.base_instance:
return self.base_instance
else:
# create a new widget for child widgets
widget = QUiLoader.createWidget(self, class_name, parent, name)
if self.base_instance:
setattr(self.base_instance, name, widget)
return widget
def load_ui(ui_file, base_instance=None):
loader = UiLoader(base_instance)
widget = loader.load(ui_file)
QMetaObject.connectSlotsByName(widget)
return widget
example:
#!/usr/bin/env python
from PySide import QtGui
from ui_loader import load_ui
import sys
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
load_ui('my_interface.ui', self)
def main():
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
if __name__ == '__main__':
main()
Actually I was wondering about the same thing and just tried this:
Copy the uic package from a PyQt4 installation to your PySide package(its all python modules, no compiled libraries, so there should not be any incompatibilities)
Do a search & replace on the uic package, replacing "PyQt4" with "PySide"
Use it:
from PySide import uic
w = uic.loadUi("mywidget.ui")
w.show()
It also worked for me with the use case you describe (uic.loadUi(ui_file, self)) :)
Of course there is no guarantee that everything will work, but I was surprised that this hack made it work.
I am working on pyqt4 and python26 application.I created forms using qt designer (.ui files).
I converted them to .py and .pyc files.But when i try to run .py file ,python command line comes and goes within a second,the form (corresponding .ui file) cannot be seen...what can be the problem??
this is my code:(.py file)
from DlgAbout_ui import Ui_DlgAbout
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import resources
class DlgAbout(QDialog, Ui_DlgAbout):
def __init__(self, parent=None):
QDialog.__init__(self, parent)
self.setupUi(self)
self.logo.setPixmap( QPixmap( ":/icons/faunalia_logo.png" ) )
text = self.txt.toHtml()
text = text.replace( "$PLUGIN_NAME$", "RT Sql Layer" )
self.txt.setHtml(text)
First, don't use:
from PyQt4.QtCore import *
from PyQt4.QtGui import *
Instead:
from PyQt4 import QtCore, QtGui
And reference the modules explicitly.
class DlgAbout(QtGui.QDialog, Ui_DlgAbout):
etc.
In your code, all you've done is defined a dialog box. You haven't defined any main application to run, or any way to show the dialog box.
For an example, here's a basic main application to run:
from PyQt4 import QtGui
import sys
class MyMainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MyMainWindow, self).__init__(parent)
self.form_widget = FormWidget(self)
self.setCentralWidget(self.form_widget)
class FormWidget(QtGui.QWidget):
def __init__(self, parent):
super(FormWidget, self).__init__(parent)
self.layout = QtGui.QVBoxLayout(self)
self.button = QtGui.QPushButton("Button!")
self.layout.addWidget(self.button)
if __name__ == "__main__":
app = QtGui.QApplication([])
foo = MyMainWindow()
foo.show()
sys.exit(app.exec_())
This defines a main window, and a form (Which MyMainWindow sets up, as you can see).
I then check if this is the main file being run (if __name__ == "__main__":), and I start the application (The app = QtGui.QApplication([]), create the main window, and show the main window.
In your case, you could define a main application like I did, and make it alert your QDialog.
Your python code just imports some modules and then defines a new class. It doesn't do anything with the class it has defined, though. In other words, once Python is done creating the new class, it is finished, and it exits.
I don't know PyQT at all, but most likely you need to start the GUI's main loop, and also instantiate an instance of your new class and pass it to PyQT.