Increase start up time of pyqt5 application with custom modules - python

I have a pyqt5 application. In the program you can open various other windows from the MainWindow. The different windows are stored in a separate module and are imported into the main file, which represents the MainWindow. Furthermore I use several custom widgets, which are also stored in a separate module and imported into the main file.
The program runs smoothly, but it takes a few seconds for the program to start. Exactly I can not say what causes the delay at startup. However, it seems to be due to my custom modules.
Is there a possibility to speed up the loading of the modules or to load the modules after the MainWindow is open?
The main file looks something like his:
#other imports
...
#import custom modules
from MyWidgets import MyTreeView
from MyWindows import MySecondWindow
basedir = Path(__file__).parent.absolute()
class App(QMainWindow):
def __init__(self):
super(App, self).__init__()
uic.loadUi(os.path.join(basedir, "gui", "MainWindow.ui"), self)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = App()
window.showMaximized()
window.show()
sys.exit(app.exec_())

Related

How to inherit attributes of instance from PyQt5 code

I am trying to create a GUI using PyQt5. I have an external script with functions, to change values on the screen. However I cannot access the attributes of the Qt object externally.
In my external script I have tried:
Main.MainWindow.label_55...
Main.ui.label_55...
And multiple other variations
*Main is the name of the PyQT5.py file
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_())
Error:
AttributeError: module 'Main' has no attribute 'MainWindow'
From your question I suppose by "accessing externally" you mean "by importing". If you import a module with an "import protection" statement like
if __name__ == "__main__":
the code inside this block does not run. It only runs when you execute the module directly, not via an import. That's why the other module does not see MainWindow.
If that's your main app window you should probably make imports the other way round. The main app imports other modules and calls them, eventually passing the main window or other objects as parameters.

Using qtDesigner with python seamlessly [duplicate]

This question already has answers here:
QtDesigner changes will be lost after redesign User Interface
(2 answers)
Closed 4 years ago.
I've been looking for a better way to work with frontends made using qtDesigner connected to a python backend. All the methods I have found have the following form:
Make GUI in designer
Output to python code using pyuic (usually with -x option)
Write backend code inside this output file
This methodology is not simple to maintain or edit. Any time you change the UI, it completely breaks workflow: you have to reconvert, generate a new file, fix that file back up to where you were before, then finally get back on track. This requires a lot of manual copy-paste of code, which is an invitation to errors on multiple levels (newly generated file layout may not be the same, manually fixing name changes while pasting, etc.). You can also end up losing work if you aren't careful, since you could accidentally overwrite the file and destroy the backend code.
Also, this doesn't use any of the control in qtDesigner like the Signal/Slot and Action editors. These must be here for something, but I can't find a way to actually direct these to call backend functions.
Is there a better way to work with this, possibly using the features of qtDesigner?
You don't have to add your code in the output file :
If you take a 'home.py' generated by PYUIC, containing a QMainWindow which name set by QtDesigner/generated by Puic would be Ui_Home(), your main code could be :
from home import Ui_Home
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class window_home(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
#set up the user interface from Designer
self.ui = Ui_Home()
self.ui.setupUi(parent)
#then do anything as if you were in your output file, for example setting an image for a QLabel named "label" (using QtDesigner) at the root QMainWindow :
self.ui.label.setPixmap(QPixmap("./pictures/log.png"))
def Home():
f=QMainWindow()
c=window_home(f)
f.show()
r=qApp.exec_()
if __name__=="__main__":
qApp=QApplication(sys.argv)
Home()
I found an even cleaner method for working with this, that does not require preemptive conversion after each edit at all. Instead it takes the .ui file itself, so all you need to do is restart the program itself to update the design.
import sys
import os
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5 import uic
path = os.path.dirname(__file__) #uic paths from itself, not the active dir, so path needed
qtCreatorFile = "XXXXX.ui" #Ui file name, from QtDesigner, assumes in same folder as this .py
Ui_MainWindow, QtBaseClass = uic.loadUiType(path + qtCreatorFile) #process through pyuic
class MyApp(QMainWindow, Ui_MainWindow): #gui class
def __init__(self):
#The following sets up the gui via Qt
super(MyApp, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
#set up callbacks
self.ui.NAME OF CONTROL.ACTION.connect(self.test)
def test(self):
#Callback Function
if __name__ == "__main__":
app = QApplication(sys.argv) #instantiate a QtGui (holder for the app)
window = MyApp()
window.show()
sys.exit(app.exec_())
Note that this is Qt5. Qt5 and Qt4 are not API compatible, so it will be a little different in Qt4 (and presumably earlier as well).

Passing string from one py to gui

Can someone tell me how do this?
I got main.py and gui.py (made in pyqt4).
The main.py runs some code, whatever, print "hello world" and I want to pass this string to gui.py in listWidget.addItem().
So in main.py I did:
from gui import Ui_Form
send_str = "hello all"
ui.listWidget.addItem(send_str)
and I get the error:
NameError: global name 'ui' is not defined.
which points to gui.py as in gui.py is
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
Form = QtGui.QWidget()
ui = Ui_Form()
ui.setupUi(Form)
Form.show()
sys.exit(app.exec_())
If i put both code in one py, all if working.
How to separate these two, since i want my main.py to execute in endless loop, and from time to time i want to open gui and check whats new value main.py going to send to window?
First problem is that ui variable is local to the if name == "main"
Second problem is the if itself because that branch will not be activated when you import a module, that is similar to the main() in c
3rd problem: the gui in QT has to run in the main thread. This means that your algorithm needs to run in another thread (there is an alternative with timers but it is ugly and harder to implement)
See a tutorial on QT threads and use the signal-slot mechanism to comunitate between gui thread and algorithm thread.

Different program behavior depending if its main part is enclosed in a function

Right at the beginning I would like to say that I am a very beginner when it comes to writing GUI applications and this is the topic of this question.
I am using Anaconda 4.1.1 as my Python distribution and Spyder as my IDE. I was trying to write a very simple window application using PyQt4 (already installed in Anaconda) or - to be precise - I copied some examples form the Internet and try to make some "experiments" with them. The results are really surprising for me.
The thing is that when I am running the following example in the IPython console:
import sys
from PyQt4 import QtGui
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.show()
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
everything seems to be OK - I can close the window of an application whenever I want and it successfully opens once again when I am running the code once again. But when I make a small modification in the code, namely - getting rid of the main function:
import sys
from PyQt4 import QtGui
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.show()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
then I can run my program only once, because when I close it and try to run it once again - the IPython console displays the following message:
It seems the kernel died unexpectedly. Use 'Restart kernel' to continue using this console.
repeating every few seconds. The Kernel itself displays something like that:
QWidget: Must construct a QApplication before a QPaintDevice
I don't really understand how can it happen because for me these two pieces of code should work exactly the same since the only difference between them is presence of the main function which (I believe) is not necessary to make this program work. Why does it give an error the second way?

PyQt4 SIGNAL/SLOT problem when using sub-directories

Thanks in advance for taking the time to read this. Apologies that it is somewhat verbose. But hopefully it fully explains the problem. Stripped code demonstrating the issue is included.
I'm having an issue with PyQt4 SIGNAL/SLOTS. While I can make everything work fine if I am writing in a single file, I can't make things work if I some of the functions I wish to use are moved to sub-directories/classes.
I've looked through the Python Bindings document I can see how this works when using a single file. But what I am trying to do is this:
main.py file in root dir which contains the MainWindow __init__ code.
This file imports a number of widgets. Each widget is stored in its own sub-directory. All sub-directories contain an __init__.py file. These sub-directories are inside of a directory called 'bin', which is itself in the root dir
Some of these widgets need to have SIGNAL/SLOT links between them This is where I fall down.
So the file structure is:
- main.py
- bin/textEditor/__init__.py
- bin/textEditor/plugin.py
- bin/logWindow/__init__.py
- bin/logWindow/plugin.py
The following code shows the problem. This code creates a very basic main window that contains a central QTextEdit() widget and a dockable QTextEdit() widget. All that happens is that when the text in the central widget is changed, the same text is shown in the dockable widget. The example works. But it does so by connecting the signal textChanged() in the bin/textEditor/plugin.py file that creates the central QTextEdit() with a function in main.py. I would like it to do exactly the same thing but connexted to the updateUi function in bin/textEditor/plugin.py
If anyone could shed some light on this, I would be hugely grateful. I'm sure it is simple. But direction to any tutorials that cover this or statements that I am doing it all very wrong are equally appreciated!. Thanks again for your time:
### main.py
import os
import sys
# Import PyQT modules
from PyQt4.QtCore import *
from PyQt4.QtGui import *
# Start the main class
class MainWindow(QMainWindow):
# Initialise
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
# Name and size the main window
self.setWindowTitle("EDITOR/LOG")
self.resize(800, 600)
import bin.logWindow.plugin as logWindow
logWindow.create(self)
import bin.textEditor.plugin as textEditor
textEditor.create(self)
def updateUi(self):
# I can connect to this function from within bin/textEditor/plugin.py (see
# below) but I want to connect to the function located in
# bin/textEditor/plugin.py instead
text = self.editor.toPlainText()
self.logWidget.setText(text)
# Run the app
def main():
app = QApplication(sys.argv)
form = MainWindow()
form.show()
app.exec_()
# Call main
main()
The code inside of the two plugin files is:
### bin/textEditor/plugin.py
# Import PyQT modules
from PyQt4.QtCore import *
from PyQt4.QtGui import *
def create(self):
# Add a dockable widget
self.logDockWidget = QDockWidget("Log", self)
self.logDockWidget.setObjectName("LogDockWidget")
self.logDockWidget.setAllowedAreas(Qt.LeftDockWidgetArea|
Qt.RightDockWidgetArea)
self.logWidget = QTextEdit()
self.logDockWidget.setWidget(self.logWidget)
self.addDockWidget(Qt.LeftDockWidgetArea, self.logDockWidget)
And
### bin/logWindow/plugin.py
Import PyQT modules
from PyQt4.QtCore import *
from PyQt4.QtGui import *
def create(self):
# Create a text editing box
self.editor = QTextEdit()
# Add to main window
self.setCentralWidget(self.editor)
# connect text change to update log window. This is presumably what I need to
# change so that it connects to the function below instead of the on in main.py
self.connect(self.editor, SIGNAL("textChanged()"), self.updateUi)
def updateUi(self):
text = self.editor.toPlainText()
self.logWidget.setText(text)
For starters, is there a reason you're using a very old version of the PyQt release document? The new one is: here
There are a few things you are doing that are a bit unusual. Generally import statements in python are placed at the top of the file (to more easily see dependencies), but I assume you're doing this to support a more generalized import system for plugins in the future.
It seems like the basic problem is you're trying to connect a signal source to a slot in another object, without storing that other object in a particular place. To do this you probably need to either make the connection in main, make a neutral "updateUi" slot that emits it's own special signal that all the plugins are waiting for, or just keep a reference to those subobjects in main and be careful with the initialization order.

Categories