I like the idea of using the loadUi() method in PyQt to load in a QtDesigner interface file as I'm often changing the ui file and don't want to be constantly converting it to a py file.
However, I've been having difficulty understanding how to access the different widgets and views in my ui file. The below shows how I'm loading in the ui:
class MyClass(QtGui.QMainWindow):
def __init__(self, parent = None):
super().__init__(parent)
ui = uic.loadUi('MyUserInterface.ui',self)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
mainapplication = MyClass()
mainapplication.show()
app.exec_()
It displays fine, but I was expecting that I would be able to access the elements of the GUI by something along the lines of...
ui.sampleButton.makeDraggable()
My question is, how can I access the various elements within my GUI?
Edit 1: I Also have tried to without assigning the uic.load call to ui. When accessing as shown below, I can access some of the properties through using Ui_MainWindow. However, it throws an error
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
mainapplication = MyClass()
mainapplication.Ui_MainWindow.graphicsView.acceptDrops(True)
mainapplication.show()
app.exec_()
The error i get is...
Traceback (most recent call last): File "C:\Users\Me\workspaces\com.samplesoftware.software\src\SoftwareView\MyClass.py", line 17, in <module>
mainapplication.Ui_MainWindow.graphicsView.acceptDrops(True)
AttributeError: 'MyClass' object has no attribute 'Ui_MainWindow'
Any ideas?
You don't need to convert it to a .py file but you definitely have to name them to access them, otherwise you need to know what the sampleButton is called in the ui file.
you can simply rename the sampleButton to my_btn and access it..
The loadUi function takes a ui file path as the first argument, and a base-instance as the second argument.
The base-instance should be the same Qt class as the top-level widget in the UI. This is because loadUi will insert all the UI into the base-instance, and may need to call some of its methods in doing so. All the child widgets that are defined in the UI will end up as attributes of the base-instance.
So you should call loadUi like this in your __init__:
class MyClass(QtGui.QMainWindow):
def __init__(self, parent = None):
super(MyClass, self).__init__(parent)
uic.loadUi('MyUserInterface.ui', self)
And you should then be able to access graphicsView like this:
self.graphicsView.setAcceptDrops(True)
I found out the reason why was because I was trying to call the Ui_MainWindow prior to running app.exec_(). After moving the Ui_MainWindow call to the end it does work. I'm guessing, and it does make sense in hindsight, the MainWindow needs to exist before its properties can be altered.
Related
Is it possible to add another .py file (via import) and use it as an initialisation and event handler for a PyQt5 Multiwindow project other window & main .py files?
I have a project that consists of the following files:
main.py - the main app which imports the Ui...py files below & the ui_init_events.py file also
main.py - the main program
Ui_main_window_ui.py - a complied Qt UI to display the main window
Ui_frmConsoleLog_ui.py - a complied Qt UI to display a window with a textEdit & comboBox objects
ui_init_events.py - I want this file to have functions for setting each windows Ui object fields as well as contain the Ui windows object events such as btn.clicked.connect(self.SomeotherFunc)
I have found both these posts helpful but am stuck as I do not know how to reference objects other than the self object and can't find where this is explained. See: PyQt: Modifying Widget Object from another Function
Multiple Windows in PyQt4
In the 2nd post they are using multi-windows (via the QDialog) object also, however they are only using a single .py file. I am using Visual Studio Code & have built the Ui files then complied them. They are works in progress so I expect to make more changes meaning they wil be overwritten so I do not want to edit these files.
I cannot work out how to reference and change the properties in another window for initialisation purposes. The thread is:
Here is the main bits. Currently I call a function to return some data using self.combobox.additems (see #1) but I think calling this over and over again from main.py somewhat decreases code readability.
(#2) Therefore I would like advice how to move all the initialisation parts of an existing PyQt window (being an imported .py file used to generate the window + controls) into a separate .py file (eg named ui_init_events.py).
However in trying & researching this I do not know nor can find an example how to reference the objects using their full hierarchical naming convention. I have tried Application. QWindow., etc. I only know how to use self.... and it isn't working (of course as that would be the referring to the function itself I understand that its being called in, not the PyQt window I'm wanting to reference). See (#3) for what is not working.
Any ideas please on resources available to assist with understanding how PyQt labels its objects from the root of the object tree is appreciated as well as an example of how to set the comboBox_Selector in the Ui_frmConsoleLog window from app.py or another .py file other than from the Ui_frmConsoleLog_ui.py or within the class definition under --init--(self, parent=None): ?
The desired file structure is like this:
+-- main.py (or app.py)
|
+--- Ui_main_window_ui.py (has setupui & adds objects for main window)
|
+--- Ui_main_window_init.py (has init code for Ui_main_window_ui.py)
|
+--- Ui_main_window_events.py (has event code for Ui_main_window_ui.py)
|
+--- Ui_frmConsoleLog_ui.py (has setupui & adds objects for 2nd window)
|
+--- Ui_frmConsoleLog_init.py (has init code for Ui_frmConsoleLog_ui.py)
|
+--- Ui_frmConsoleLog_events.py (has event code for Ui_frmConsoleLog_ui.py)
main.py file contents
# Default imports
import sys, os, io, sched, time
import ui_resources_rc
from PyQt5.QtCore import QObject, QThread, pyqtSignal
from configparser import ConfigParser
...a portion of code here uses a ini config file for retrieving/saving some settings of the UI & I want to set the properties for each object accordingly...
from PyQt5.QtWidgets import (QApplication, QDialog, QMainWindow, QMessageBox, QWidget)
from PyQt5.uic import loadUi
from Ui_main_window_ui import Ui_MainWindow
from Ui_frmConsoleLog_ui import Ui_frmConsoleLog
from ui_init_events import (funcUpdateFormCombos)
...todo... open a network connection to the system (its an inverter)
# This window will be used for filtering network poll event types
class ConsoleLogWindow(QWidget, Ui_frmConsoleLog):
def __init__(self):
super().__init__()
self.setWindowTitle('Console Log')
class AppWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.win = None # No Console Log window visible yet.
self.setupUi(self)
#(1) Here is where I will have to call this a lot of times to
# populate the vaious window objects which I dont want to do
self.comboBox_Selector.addItems(funcUpdateFormCombos())
self.action_Console.triggered.connect(self.OpenConsoleLogWindow)
self.action_About.triggered.connect(self.about)
# Populate the controls as per the config file
#initialise() <-- (2) Here is where I want to call a
# function in ui_init_events.py to setup
# the form's objects as created in QtDesigner
# & exported as a .py file. I want to continue
# to edit each PyQt window later so I dont want
# to add to the Ui_... files themselves but in
# main.py call a function that has the code to
# set the object fields like checked, combobox
# list contents, etc.
def OpenConsoleLogWindow(self, checked):
if self.win is None:
self.win = ConsoleLogWindow()
self.win.show()
else:
self.win.close() # Close window.
self.win = None # Discard reference.
if __name__ == "__main__":
app = QApplication(sys.argv)
win = AppWindow()
win.show()
print('HERE')
sys.exit(app.exec())
ui_init_events.py file contents (this is the file I want to house all window object setup without editing the PyQt UI converted .ui to .py files such as Ui_main_window_u.py & Ui_frmConsoleLog_ui.py which I don't want to edit at all.
# Contains the initialisation of widgets fields
def funcUpdateFormCombos():
LIST = ['Amps', 'Volts', 'Watts']
LIST.sort()
return tuple(LIST)
# Would prefer to have a single file per Ui_...py file that houses
# all the events that get triggered for each Ui windows.
# How is this done? eg: <not working> (3)
def initialise()
frmConsoleLog.self.comboBox_Selector.addItems('Watts','Amps','Volts')
Contents of Ui_frmConsoleLog_ui.py (has the combobox I want to populate)
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_frmConsoleLog(object):
def setupUi(self, frmConsoleLog):
frmConsoleLog.setObjectName("frmConsoleLog")
frmConsoleLog.resize(640, 468)
frmConsoleLog.setToolTip("")
frmConsoleLog.setStatusTip("")
frmConsoleLog.setAccessibleName("")
self.horizontalLayout = QtWidgets.QHBoxLayout(frmConsoleLog)
self.horizontalLayout.setContentsMargins(0, 1, 0, 0)
self.horizontalLayout.setObjectName("horizontalLayout")
self.comboBox_Selector = QtWidgets.QComboBox(frmConsoleLog)
self.comboBox_Selector.setObjectName("comboBox_Selector")
self.horizontalLayout.addWidget(self.comboBox_Selector)
self.textEdit_ConsoleLog = QtWidgets.QTextEdit(frmConsoleLog)
self.textEdit_ConsoleLog.setEnabled(True)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.textEdit_ConsoleLog.sizePolicy().hasHeightForWidth())
self.textEdit_ConsoleLog.setSizePolicy(sizePolicy)
self.textEdit_ConsoleLog.setObjectName("textEdit_ConsoleLog")
self.horizontalLayout.addWidget(self.textEdit_ConsoleLog)
self.retranslateUi(frmConsoleLog)
QtCore.QMetaObject.connectSlotsByName(frmConsoleLog)
def retranslateUi(self, frmConsoleLog):
_translate = QtCore.QCoreApplication.translate
frmConsoleLog.setWindowTitle(_translate("frmConsoleLog", "Console Log"))
Worked it out, thanks to an answer in my previous post (that the poster removed?), here's the snippet that works and allows your functions in another .py file to set MainWindow pyQt object control properties in the parent main.py file for a much tidier project. I honestly don't care that professional devs think that is not good practice (if they do they are very closed minded).
In app.py (or main.py - I had two files and was playing with names)
class AppWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.win = None # No Console Log window visible yet.
self.setupUi(self)
from init_Ui_main_window import (initMainWindow)
initMainWindow(self)
...
In init_Ui_main_window.py:
def initMainWindow(window):
funcLoadConfig()
# Populate the controls in the main window
window.line_Settings_Simple_Default_Selection.setText(str(SETTINGS_SIMPLE_DEFAULT_SELECTOR))
Note: SETTINGS_SIMPLE_DEFAULT_SELECTOR is a globally defined static variable that wont ever be changed as its read from a config ini file. The user will be able to change the line value if they want to override the default, but it's nice to have a default value populated.
Was that easy as when initMainWindow(self) is called self from the previous object context is passed across to the function and set in function variable window, so to reference the main windows objects its just a case of referencing window.[object name as one would in main.py]
I'm new to python and PyQt and was developing my first app using it, and I've been stuck in a problem when trying to instantiate a class I made again. I've got the following error:
Traceback (most recent call last):
File "ConfiguradorAnx.py", line 16, in <lambda>
self.ProductInfo.clicked.connect(lambda: self.newWindow(InfoProduct))
TypeError: 'InfoProduct' object is not callable
Aborted
The code goes like this:
from PyQt5 import QtCore, QtGui, QtWidgets, uic
import sys
class StartWindow(QtWidgets.QMainWindow): #This function should inherit the class
#used to make the ui file
def __init__(self):
super(StartWindow,self).__init__() #Calling the QMainWindow constructor
uic.loadUi('Janela_inicial.ui',self)
#defining quit button from generated ui
self.QuitButton = self.findChild(QtWidgets.QPushButton, 'QuitButton')
self.QuitButton.clicked.connect(QtCore.QCoreApplication.instance().quit)
#defining product info button
self.ProductInfo = self.findChild(QtWidgets.QPushButton, 'ProductInformation')
self.ProductInfo.clicked.connect(lambda: self.newWindow(InfoProduct))
self.show() #Show the start window
def newWindow(self, _class):
self.newWindow = _class()
del self.newWindow
class InfoProduct(QtWidgets.QMainWindow):
def __init__(self):
super(InfoProduct,self).__init__()
uic.loadUi('informacao_prod.ui',self)
self.QuitButton = self.findChild(QtWidgets.QPushButton, 'pushButton')
self.QuitButton.clicked.connect(lambda: self.destroy())
self.show()
def main():
app = QtWidgets.QApplication(sys.argv) #Creates a instance of Qt application
InitialWindow = StartWindow()
app.exec_() #Start application
if __name__ == '__main__':
main()
The first time I click on self.ProductInfo button it works and the InfoProduct window opens, but when I close the window and click on the same button again, I've got the error. I can't figure out what is it that I'm missing, I hope you guys could help!
Cheers!
You're overwriting the newWindow function in its execution:
def newWindow(self, _class):
self.newWindow = _class()
By doing this, the result is that the next time you click the button, the lambda will try to call self.newWindow(InfoProduct), but at that point self.newWindow is an instance of InfoProduct, which obviously is not callable.
The solution is simple (and very important) use different names for the function and the variable that points to the instance:
self.ProductInfo.clicked.connect(lambda: self.createNewWindow(InfoProduct))
def createNewWindow(self, _class):
self.newWindow = _class()
Two small side notes:
There's no need to use findChild, as loadUi already creates python instance attributes for widgets: you already can access self.QuitButton, etc.
Avoid using capitalized names for variables and attributes. Read more about this and other code styling suggestions on the Style Guide for Python Code (aka, PEP-8).
I found this simple program in a Youtube tutorial which is used QtSide modules with python. Basically what it does is connect QLineEdit to a QTextBrowser. As you can see below, the entire program handled by single class. I have basic idea of super() function which is used in multiple inheritance. So here, I don't understand what super(Form, self).__init__(parent) statement does. I tried running the same program after commenting that statement which produced below error message.
Error:
Traceback (most recent call last):
File "/home/dazz/Projects/PycharmProjects/FirstTutorial/a2_gui.py", line 35, in <module>
form = Form()
File "/home/dazz/Projects/PycharmProjects/FirstTutorial/a2_gui.py", line 17, in __init__
self.setLayout(layout)
RuntimeError: '__init__' method of object's base class (Form) not called.
Program code:
import sys
from PySide.QtCore import *
from PySide.QtGui import *
class Form(QDialog):
def __init__(self, parent=None):
super(Form, self).__init__(parent)
self.browser = QTextBrowser()
self.lineEdit = QLineEdit()
layout = QVBoxLayout()
layout.addWidget(self.browser)
layout.addWidget(self.lineEdit)
self.setLayout(layout)
self.lineEdit.returnPressed.connect(self.update_ui)
self.setWindowTitle('Calculate')
def update_ui(self):
try:
text = self.lineEdit.text()
self.browser.append('%s = %s' % (text, eval(text)))
self.lineEdit.selectAll()
except:
self.browser.append('%s is invalid!' % text)
app = QApplication(sys.argv)
form = Form()
form.show()
app.exec_()
Here, what is the use of super()?
I found a question which may related to this. But it's not clear to me.
super() is used is inheritance, multiple or otherwise, to call methods that may have been overridden in the current class.
Here super() is used to call the original __init__ method defined for QDialog or a parent class of that. Not calling the original probably will have consequences as that original method does work you don't want to have to replicate in your own __init__ method.
super() makes multiple inheritance easier and more flexible to deal with, but it is not just for multiple inheritance.
Basically, super() is used for inheritance, as you already understand. As mentioned by Martjin, the 'consequence' that appeared in your case when you commented away is that there is initialisation required that was already implemented in `QDialog' class.
Hence, all that needs to be done in this child class is to call the parent's init.
There is already a good article in stackoverflow on super and init here.
super and init
I have the code of a cipher and designed a GUI for it in PyQt and am now attempting to integrate the two now but I have become stuck.
I want to be able to push the Generate Key button which will then run the generatekey function I have and display it in the text box to the left of the button. I tried with .setText() but could not get it to work. The object name for the text box is keytext. Not really sure what to do, when I push the button now it just crashes.
from PyQt5 import QtCore, QtGui, QtWidgets
import sys, random
import cipher ## Designed GUI
LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
class CipherDesign(QtWidgets.QMainWindow, cipher.Ui_MainWindow):
def __init__(self, parent=None):
super(CipherDesign, self).__init__(parent)
self.setupUi(self)
self.BGenerate.clicked.connect(self.generatekey) # Generate Button
def generatekey():
key = list(LETTERS)
random.shuffle(key)
return ''.join(key)
def main():
app = QtWidgets.QApplication(sys.argv)
form = CipherDesign()
form.show()
app.exec_()
if __name__ == "__main__":
main()
Your problem is that your generateKey method is missing the self argument and then inappropriately returning the key value rather than assigning it with setText.
In normal Python omitting the self parameter would just give a "number of arguments" error when you call self.generateKey(). But here it is being called by PyQT which is (somewhat unexpectedly) causing a crash.
In Python, a call to a method on an object (like self.generateKey()) is translated into a call like this generateKey(self). self here is just an arbitrary name (self is used by convention). This is the mechanism by which the method is associated to the object on which it is called. Then, in the body of the method, the name self is in scope and is bound to the object instance, as you need it to be to use it in Python.
So you should have something like:
def generatekey(self):
key = list(LETTERS)
random.shuffle(key)
self.TextBoxObject.setText(''.join(key))
I am trying to make a pretty big app with many different parts and windows. I decided that it would look a lot cleaner if I had some windows have their own file and then import them into the main file. I tried to do this but when I try to run the class, it gives the error of needing three arguments. I do not understand how I should go about doing this so any help will be greatly appreciated!
Main file:
import wx
import Login
Login.apples(self,parent,id)
class oranges(wx.Frame):
def __init__(self,parent, id):
wx.Frame.__init__(self,parent,id,"Mail",size=(700,700))
self.frame=wx.Panel(self)
if __name__=="__main__":
app=wx.App(False)
window=oranges(parent=None, id=-1)
window.Show()
app.MainLoop()
I get a NameError: name "self" is not defined.
import wx
class apples(wx.Frame):
def __init__(self,parent,id):
wx.Frame.__init__(self,parent,id,"Login to Mail",size=(400,400))
self.frame=wx.Frame(self)
if __name__=="__main__":
app=wx.App(False)
window=apples(parent=None, id=-1)
window.Show()
app.MainLoop()
import wx
import Login
#Login.apples(self,parent,id) #this line wont work ... there is no self here...
#see below in the if __name__ == "__main__" part
class oranges(wx.Frame):
def __init__(self,parent, id):
wx.Frame.__init__(self,parent,id,"Mail",size=(700,700))
self.frame=wx.Panel(self)
if __name__=="__main__":
app=wx.App(False)
window=oranges(parent=None, id=-1)
other_window = Login.apples(parent=None,id=-1)
window.Show()
other_window.Show()
app.MainLoop()
The error is that you included self as an argument in your call to Login.apples(). The first argument in a class method (usually called self) should not be included in function calls (only function definitions), and is treated implicitly in Python. It is used to handle references within the class methods to the class itself (or other class attributes/functions). See this post for information on self
However, once you fix this, your code will still not run with the same error because you given no value to parent or id. You will need to provide values for these variables before asking python to call a function with them