Python: QT5 include ui.py in main python script - python

this might be an basic question, but I didn't found a working solution yet:
This is my first Python UI Script... I have the idea of putting the UI in one file without any functionality and putting all the actual code and algorithms into another file, but I don't know if this is good practice or not and how to make this work.
I have created an UI with GTDesigner and converted this into MainWindowPy.py by using pyuic5.
This gave me:
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
[...]
def retranslateUi(self, 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_())
Now I want to leave this MainWindowPy.py untouched, because the UI can change in the further delevopment.
How can I link and run this file in my MainApplication.py script and how can I access UI-elements like buttons?
I tried wrapping the whole UI-code in a function and then imported this as a module in MainApplication.py and called the function through this module. It worked, but that's not "untouched".
I know about subprocess.Popen(), but maybe there is a better way?

The code generated in this case is a mixin class. To use it in a different file you simply import it and create a new class with two base classes, one which is the Qt Class you want it to be and the second which is the mixin class from your uic generated file. In the init of your new class you must run the init method of the primary base class, and the setupUi method of the mixin class:
from MainWindowPy import Ui_MainWindow
from PyQt5 import QtWidgets
class myMainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(myMainWindow, self).__init__(parent) # call init of QMainWindow, or QWidget or whatever)
self.setupUi(self) # call the function that actually does all the stuff you set up in QtDesigner
now to use it, you initiate your custom class that you made
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = myMainWindow()
MainWindow.show()
sys.exit(app.exec_())

Related

How to call "flash.ocx" with python by filename

I want to create a player using "flash.ocx".
It looks like this.
from PyQt5 import QtCore, QtGui, QAxContainer, QtWidgets
import pickle
class Ui_Flash(QAxContainer.QAxWidget):
def __init__(self, parent=None):
super(Ui_Flash, self).__init__()
self.resize(800, 600)
self.setControl("{D27CDB6E-AE6D-11cf-96B8-444553540000}")
self.dynamicCall("LoadMovie(long,string)", 0, r"test.swf")
self.show()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
ui = Ui_Flash()
ui.show()
sys.exit(app.exec_())
However, this method is implemented using the COM interface. It can use either "ProgID" or "CLSID".
But I want to find an implementation method that can be called directly by the file name.
It looks like ...
self.setControl("D:\\Flash.ocx") # The "self.setControl" func is an example, which does not mean that the example is correct.
You can use pyqt or other UI designs, such as Tkinter.
So, how do I do it?
Thank you for your help.

Use separate interface file with PyQt

I have an interface file generated from QtDesigner that I want to keep the same in case of changes.
Have a main file called application.py handle all the functions, and one file strictly for interface stuff.
I am using PyQt5.
I have not been able to find any tutorials on this specific question, any pointers would be helpful.
Code from YatsiInterface.py (shorten)
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_YatsiWindow(object):
def setupUi(self, YatsiWindow):
YatsiWindow.setObjectName("YatsiWindow")
YatsiWindow.resize(800, 516)
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap("Terraria.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
YatsiWindow.setWindowIcon(icon)
self.windowLayout = QtWidgets.QWidget(YatsiWindow)
self.windowLayout.setObjectName("windowLayout")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.windowLayout)
self.quitButton.setObjectName("quitButton")
self.buttonsLeftLayout.addWidget(self.quitButton)
YatsiWindow.setCentralWidget(self.windowLayout)
self.statusbar = QtWidgets.QStatusBar(YatsiWindow)
self.statusbar.setObjectName("statusbar")
YatsiWindow.setStatusBar(self.statusbar)
self.retranslateUi(YatsiWindow)
QtCore.QMetaObject.connectSlotsByName(YatsiWindow)
def retranslateUi(self, YatsiWindow):
_translate = QtCore.QCoreApplication.translate
YatsiWindow.setWindowTitle(_translate("YatsiWindow", "Yatsi - Server Interface"))
self.quitButton.setText(_translate("YatsiWindow", "Quit"))
How can I use self.quitButton.clicked.connect(QtCore.QCoreApplication.instance().quit()) with the above code? I know to import the file with from YatsiInterface import Ui_YatsiWindow but I'm in the dark as how to create button functions without editing the interface file.
Edit:
I'll add my broken code below.
import sys
from YatsiInterface import Ui_YatsiWindow
from PyQt5 import QtCore, QtGui, QtWidgets
app = QtWidgets.QApplication([])
YatsiWindow = QtWidgets.QMainWindow()
ui = Ui_YatsiWindow()
ui.setupUi(YatsiWindow)
# Here's the bad part
ui.setupUi.btn.clicked.connect(QtCore.QCoreApplication.instance().quit)
# Up there ^
YatsiWindow.show()
sys.exit(app.exec_())
Thanks for your help.
This would be the way to connect the signal
ui.quitButton.clicked.connect(QtCore.QCoreApplication.instance().quit)
That being said, you probably don't want to do this. You should probably connect to the .close method of the window. When the window closes, by default, Qt will exit the event loop and quit.
ui.quitButton.clicked.connect(YatsiWindow.close)
from YatsiInterface import Ui_YatsiWindow
from PyQt5 import QtCore, QtGui
class my_application(QtGui.QWidget, Ui_YatsiWindow):
def __init__(self):
super(my_application, self).__init__()
self.setupUi(self)
self.quitButton.clicked.connect(self.my_mythod)
def my_method(self):
pass #all your code for the buttons clicked signal
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
ui = my_application()
ui.show()
sys.exit(app.exec_())
This my_application class is inheriting your user interface class. Now, you can write your button related functions without editing the user interface file.
self.quitButton.clicked.connect(self.close)
this will close your user interface when button is clicked.

Ui_MainWindow has no attribute 'show'

I'm writing a program which will have multiple windows. I have a main program (attached) that calls the Ui files (that have been converted into .py). The main window and customise window open correctly (the first two listed) but neither the third or fourth windows open correctly, giving me the error
'Ui_MainWindow' object has no attribute 'show'
The main program;
from PyQt4 import QtCore, QtGui
import sys
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
mainwin = main_menu_ui.Ui_MainWindow()
mainwin.show()
sys.exit(app.exec_())
def openCustomise(self):
customiseOpen = question_set_menu_ui.Ui_MainWindow()
customiseOpen.show()
sys.exit(app.exec_())
def openQuiz(self):
quizOpen = quiz_window_ui.Ui_MainWindow()
quizOpen.show()
sys.exit(app.exec_())
def addNewSet(self):
addNewOpen = question_set_edit_ui.Ui_MainWindow()
addNewOpen.show()
sys.exit(app.exec_())
Sorry if I'm missing something obvious, I'm learning Qt/Python for college.
The auto-generated UI class that you are importing extends object and doesn't have a show method (open up the .py file for yourself and verify this).
In general, you should structure your GUIs like this:
from PyQt4 import QtCore, QtGui
import sys
from layout_file import main_menu_ui
class MyForm(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = main_menu_ui()
self.ui.setupUi(self)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
mainwin = MyForm()
mainwin.show()
sys.exit(app.exec_())
You import your UI from your autogenerated UI file. You have a class that contains your GUI logic. It then sets up your UI layout from your imported UI in its __init__() method.

PySide: QMetaObject.connectSlotsByName emits warnings "No matching signal..." but still works..?

In Qt Designer, I created a QDialog window and used pysideuic to compile that to a base class which contains a setupUi method initialising all GUI elements and which I extend to implement the functionality, as so:
class MyDialog(QtGui.QDialog, ui_file.Ui_main_dialog):
def __init__(self, parent=None):
QtGui.QDialog.__init__(self, parent)
ui_file.Ui_main_dialog.__init__(self)
self.setupUi(self)
This setupUi method has calls to QtCore.QObject.connect for the signal-slot connections I created in Qt Designer, where I also added new slots to the GUI. These slots are non-existent in the base class generated by pysideuic and I added them to the MyDialog class, e.g.
def on_list_selection_changed(self):
self.run_btn.setEnabled(len(self.modules_list.selectedIndexes()) > 0)
For this example, the slot is called on_list_selection_changed() (with empty parameter list) in Qt Designer.
On initialisation, MyDialog.__init__ calls Ui_main_dialog.setupUi, which eventually calls QtCore.QMetaObject.connectSlotsByName (the latter two with the MyDialog instance's self which is currently being created). This emits the following line on sys.stderr:
QMetaObject::connectSlotsByName: No matching signal for on_list_selection_changed()
Still, the signal behaves correctly and the slot is called when the connected modules_list.itemSelectionChanged() (modules_list is a QListWidget).
So here is my question: why do I receive this warning? What can I do so it doesn't appear, given that it seems to be irrelevant?
Edit: Since I didn't receive any answers in the last 5 months, I thought I give a complete example to make reproducing the problem easier.
This example differs from the question above in that it only uses a QLineEdit instance. Here is the code:
import sys
from PySide import QtCore, QtGui
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
self.lineEdit = QtGui.QLineEdit(Dialog)
self.lineEdit.setObjectName("lineEdit")
QtCore.QObject.connect(self.lineEdit, QtCore.SIGNAL("textChanged(QString)"), Dialog.on_lineEdit_changed)
QtCore.QMetaObject.connectSlotsByName(Dialog)
class MainWindow(QtGui.QMainWindow, Ui_Dialog):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
Ui_Dialog.__init__(self)
Ui_Dialog.setupUi(self, self)
#QtCore.Slot(unicode) # signal with no arguments
def on_lineEdit_changed(self, text):
print 'Changed to', text
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
mw = MainWindow()
mw.show()
sys.exit(app.exec_())
Note that the code for the Ui_Dialog class is generated by the pysideuic from the Qt Designer's .ui file, but I shortened it a bit to better highlight the problem.
I have the same issue in C++. connectSlotsByName is hard-coded to output that message if it finds a slot it can't connect automatically, even though you don't need the automatic connection because you've done it explicitly yourself. You can use qInstallMsgHandler to suppress warnings in general or write the messages somewhere better but I don't think there's any way to tell connectSlotsByName that you don't care about this case in particular.
I'm late but in case someone else comes here for this same question.
PySide auto connects the signals, this is how it worked for me since I didn't want to be connecting manually.
Here I use python 3.9 and PySide2, but it is the same for PySide6 and also for other versions of python.
import sys
import typing
from PySide2.QtCore import QObject, SignalInstance, Signal, Slot
from PySide2.QtWidgets import QMainWindow, QApplication
from .ui_mainwindow import Ui_MainWindow # Don't forget to add your import
class MyQObject(QObject):
mySignal: SignalInstance = Signal()
otherSignal: SignalInstance = Signal(str)
def __init__(self, parent: typing.Optional[QObject] = None) -> None:
super().__init__(parent)
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self):
QMainWindow.__init__(self)
# Instantiate before self.setupUI
self.myQObject = MyQObject(self)
self.myQObject.setObjectName("syncProduct") # do not forget this
# Load UI from designer & init
self.setupUi(self)
#Slot()
def on_myQObject_mySignal(self):
print("Handle mySignal")
#Slot(str)
def on_myQObject_otherSignal(self, text: str):
print(f"Handle otherSignal {text}")
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())

Open a GUI file from another file PyQT

I've created many GUI interfaces in PyQT using QT Designer, but now I'm trying to open an interface from another one, and I don't know how to do it..
Start.py is the file which run the GUI Interface Authentification_1 and Acceuil_start.py is the file which run the GUI interface Acceuil_2.py, now I want from Start.py to lunch Acceuil_start.py.
Do you have any idea about that ? Thank you.
Here's my code :
Start.py :
import sys
from PyQt4 import QtCore, QtGui
from Authentification_1 import Ui_Fenetre_auth
from Acceuil_2 import Ui_MainWindow #??? Acceuil_2.py is the file which I want to open
class StartQT4(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_Fenetre_auth()
self.ui.setupUi(self)
def authentifier(val): #Slot method
self.Acceuil = Acceuil() #???
self.Acceuil.show() #???
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = StartQT4()
myapp.show()
sys.exit(app.exec_())
Acceuil_start.py
import sys
from PyQt4 import QtCore, QtGui
from Authentification_1 import Ui_Fenetre_auth
from Acceuil_2 import Ui_MainWindow
class StartQT4(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = StartQT4()
myapp.show()
sys.exit(app.exec_())
First, you should to name your GUI classes so they have a different name, and not the generic one, so you could distinct them.
Why you would need to do that? Well - simply, because it makes sense - if every class is representing different type of dialog, so it is the different type - and it should be named differently. Some of the names are/may be: QMessageBox, AboutBox, AddUserDialog, and so on.
In Acceuil_start.py (you should rename class in other module, too).
import sys
from PyQt4 import QtCore, QtGui
from Authentification_1 import Ui_Fenetre_auth
from Acceuil_2 import Ui_MainWindow
class Acceuil(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = Acceuil()
myapp.show()
sys.exit(app.exec_())
in the parent class, when you want to create the window, you are close (but it should work in any case):
def authentifier(val): #Slot method
self.Acceuil = Acceuil(self) # You should always pass the parent to the child control
self.Acceuil.show() #???
About parent issue: If your widget/window is creating another widget, setting creator object to be parent is always a good idea (apart from some singular cases), and you should read this to see why is that so:
QObjects organize themselves in object trees. When you create a QObject with another object as parent, it's added to the parent's children() list, and is deleted when the parent is. It turns out that this approach fits the needs of GUI objects very well. For example, a QShortcut (keyboard shortcut) is a child of the relevant window, so when the user closes that window, the shorcut is deleted too.
Edit - Minimal Working Sample
To see what I am trying to tell you, I've built a simple example. You have two classes - MainWindow and
ChildWindow. Every class can work without other class by creating separate QApplication objects. But, if you import ChildWindow in MainWindow, you will create ChildWindow in slot connected to singleshot timer which will trigger in 5 seconds.
MainWindow.py:
import sys
from PyQt4 import QtCore, QtGui
from ChildWindow import ChildWindow
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
QtCore.QTimer.singleShot(5000, self.showChildWindow)
def showChildWindow(self):
self.child_win = ChildWindow(self)
self.child_win.show()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = MainWindow()
myapp.show()
sys.exit(app.exec_())
ChildWindow.py:
import sys
from PyQt4 import QtCore, QtGui
class ChildWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setWindowTitle("Child Window!")
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = ChildWindow()
myapp.show()
sys.exit(app.exec_())
To reference the other dialog from Start.py, you must prefix it by the module name, which in this case is Acceuil_start. As such, it is OK if there are duplicated function names in each module. So, you would have:
def authentifier(val): #Slot method
dlg = Acceuil_start.StartQT4()
dlg.exec_()
However, if you want these to run from the same process, keep in mind that you can't have two app objects. You would probably want to structure Acceuil_start.py to act like a dialog, rather than a main window. If these are two distinct main windows, you might find it easier to just invoke another Python interpreter with the Acceuil_start.py as a parameter.

Categories