Avoid circular import when accessing instance value from main.py - python

I´m new to Python and programming in general. So maybe there is an easy solution for more experienced programmers.
I already read a lot of question regarding circular imports, but unfortunately there was nothing there that I can apply to my situation if I dont want to move all the code in one file.
I created an userinterface with pyqt (qt creator) and converted the mainwindow.ui to mainwindow.py.
My plan is to split the code into 3 modules. A main module to start the application, an ui module with the class of the main window and a buttons module with classes for the buttons.
My problem is that the functions within the button classes should change a label value of the main window instance. I learned to create the main window instance in the main module. As a result of this I need to import the instance from the main module into the buttons module to change the intended value and that leads to an circular import.
How do I have to organize/structure my code to avoid this?
Here is a short and simplified example for better understanding:
main.py
import sys
from qtpy import QtWidgets
from ui import MainWindow
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
ui.py
from qtpy import QtWidgets
from userinterface.mainwindow import Ui_MainWindow
import buttons
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.button_0 = buttons.NumberButton(0)
self.button_1 = buttons.NumberButton(1)
self.ui.btn_0.clicked.connect(self.button_0.button_clicked)
self.ui.btn_1.clicked.connect(self.button_1.button_clicked)
buttons.py
from main import window
class NumberButton:
def __init__(self, number):
self.number = str(number)
def button_clicked(self):
window.ui.lb_result.setText(self.number)

Your design problem is that your NumberButton class calls one specific window instance. Your have to let your buttons know to which window they belong. Try the following: remove the import statement from buttons.py and add a new parameter window to the __init__ method:
class NumberButton:
def __init__(self, window, number):
self.window = window
self.number = str(number)
def button_clicked(self):
self.window.ui.lb_result.setText(self.number)
Then instantiate in NumberButton like:
...
self.button_0 = buttons.NumberButton(self, 0)
...

If you only import the module python should automatically avoid circular imports. So do import ui and import buttons

Related

How to load a Qt Designer file (.ui) in a QWizardPage using PySide2

I want to design my QWizardPages in Qt Designer and I want to load them into my Python program with PySide2. Previously I have been using PyQt5 without any problems but making the switch to PySide2 seems harder then expected.
The problem I am facing is that when I am adding a QWizardPage to my QWizard , the page is indeed added to the Wizard, but also an other (empty) page is added. I'm not able to find what I'm doing wrong so I was wondering if someone can have a look.
I have tried to add the pages with both the functions addPage() and setPage(), but they give the same results. What I also noticed is that when I explicitely set the Title of the page with setTitle(), the empty (unwanted) page gets this title, but not the page I designed in Qt Designer.
import os
import sys
from PySide2.QtWidgets import QWizard, QWizardPage, QApplication
from PySide2.QtCore import QFile
from PySide2.QtUiTools import QUiLoader
from enum import Enum
class MyWizard(QWizard):
def __init__(self):
super().__init__()
self.setPage(PageNumbers.page_one.value, PageOne(self))
class PageOne(QWizardPage):
def __init__(self, parent):
super().__init__(parent)
ui_file = os.path.join(__file__, '..', 'pageOne.ui')
file = QFile(ui_file)
file.open(QFile.ReadOnly)
loader = QUiLoader()
loader.load(file, parent)
file.close()
self.setTitle("This is another test Title")
class PageNumbers(Enum):
page_one = 1
if __name__ == '__main__':
app = QApplication(sys.argv)
wizard = MyWizard()
wizard.show()
app.exec_()
What I would expect is to have just one QWizardPage showing up with directly the Finish button. Instead I get two QWizardPages as shown in this image:
Can someone tell me what's going on?
(I get the expected result using PyQt5 with the following code: https://pastebin.com/6W2sx9M1)
The developers of PyQt implement functions to be able to create classes based on the .ui that is not implemented in Qt by default (Qt/C++ uses the MOC to do this work), but in the case of PySide2-Qt for python it does not implement it, only has the QUiLoader class that allows to create a widget based on the .ui unlike PyQt that allows filling a class.
In conclusion there is no equivalent in PySide2 of the loadUi function so you can not implement the same logic. PySide2 is not PyQt5, there are own equivalences since they use the same base but they have implementations, limitations and advantages.
Going to the practical problem, considering that the .ui is next to the .py the solution is the following:
import os
import sys
from PySide2 import QtCore, QtWidgets, QtUiTools
from enum import Enum
class PageNumbers(Enum):
page_one = 0
class MyWizard(QtWidgets.QWizard):
def __init__(self):
super().__init__()
ui_file = os.path.join(os.path.dirname(os.path.abspath(__file__)) ,'PageOne.ui')
page_one = create_widget(ui_file, self)
self.setPage(PageNumbers.page_one.value, page_one)
def create_widget(filename, parent=None):
file = QtCore.QFile(filename)
if not file.open(QtCore.QFile.ReadOnly):
return
loader = QtUiTools.QUiLoader()
widget = loader.load(file, parent)
return widget
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
wizard = MyWizard()
wizard.show()
sys.exit(app.exec_())

Accessing an object function from a parent widget's function

I'm making a large program in Python and using PyQt for the GUI. The whole program is divided into different modules so that different people can work on it simultaneously without interfering with the other people's work.
I am working on 3 different modules right now. 1 is the main program window that handles the basic UI and assigns widgets so the main window (this is not important, just so you know why the code doesn't look like a full program.)
First is the widget:
import sys
from PyQt4 import QtCore
from PyQt4 import QtGui
from CustomButton import HoverButton #just a custom button class
from CustomGif import LblLoadingGif #where things go wrong
class Page1(QtGui.QWidget):
def __init__(self, parent=None):
super(Page1, self).__init__(parent)
self.lbl1GIF = LblLoadingGif(self)
self.lbl1GIF.move(400, 45)
self.btnStart = HoverButton(self)
self.btnStart.setText('Start')
self.btnStart.move(35, 400)
self.btnStart.clicked.connect(self.actStartGif)
#the code below works, but then I can only perform 1 action with each button
#self.btnStart.clicked.connect(self.lbl1GIF.actStart)
def actStartGif(self):
self.lbl1GIF.actStart
The code for the custom GIF looks as follows:
import sys
from PyQt4 import QtCore
from PyQt4 import QtGui
class LblLoadingGif(QtGui.QLabel):
def __init__(self, parent=None):
QtGui.QLabel.__init__(self, parent)
self.setStyleSheet('background: url();')
self.setScaledContents(True)
self.resize(100, 100)
self.movLoadGif = QtGui.QMovie('Resources_Images/Loading6.gif', QtCore.QByteArray())
self.movLoadGif.setCacheMode(QtGui.QMovie.CacheAll)
self.movLoadGif.setSpeed(100)
self.setMovie(self.movLoadGif)
self.hide()
def actStart(self, event):
#print('test1')
self.show()
self.movLoadGif.start()
def actStop(self, event):
#print('test1')
self.hide()
self.movLoadGif.stop()
So the problem is that I can use the actStart function just fine when I call it from the button click directly, but not when I call it through another function. I have used a lot of different variations of brackets, self, Page1 when calling the actStart of the custom gif from withing the actStartGif function.
Any help will be appreciated.
When you use connect it is necessary to pass the name of the function since internally it is in charge of calling it, in your case you have to call it directly so you will have to pass its parameters, in this case event:
self.lbl1GIF.actStart({your value for event})
I do not understand why you use event for what happens to you None:
def actStartGif(self):
self.lbl1GIF.actStart(None)

How do I open sub window after I click on button on main screen in PyQt4

HI I am trying to make a simple converter.
I have used PyQt4 designed to make the Gui
I want to know how launch a new window after I click on the individual button.
This is the interface I have created using PyQt4 Designer.
Here is the Image link :
and I want to launch this windows when I click on currency button.
Here is the Image Link:
Here is my code for main.py
from PyQt4 import QtGui
from main_screen import mainscreen
def main():
import sys
qApp = QtGui.QApplication(sys.argv)
aw = mainscreen()
aw.show()
sys.exit(qApp.exec_())
if __name__ == '__main__':
main()
and code for mainscreen.py
from PyQt4 import QtCore, QtGui
from window_main import Ui_MainWindow
class mainscreen(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(mainscreen,self).__init__(parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
How can I open new window after I click on currency button (object name for currency button is "currency_bt")
and do I have to write the code for currency in same window or I have to write in new window.
How do I do it.
I am new to Python Gui programming.
Each GUI form that you create in Qt Designer needs to be converted into a python module using pyuic. So, to start with, you need to do the same for currency.ui that you did for window_main.
Now you can import your currency window class into mainscreen.py, and connect a button to handler so you can display it.
The code would look something like this:
from PyQt4 import QtCore, QtGui
from window_main import Ui_MainWindow
from currency import Ui_CurrencyWindow
class CurrencyWindow(QtGui.QMainWindow, Ui_CurrencyWindow):
def __init__(self, parent=None):
super(CurrencyWindow, self).__init__(parent)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.setupUi(self)
class MainScreen(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainScreen, self).__init__(parent)
self.setupUi(self)
self.currencyButton.clicked.connect(self.handleCurrencyButton)
def handleCurrencyButton(self):
window = CurrencyWindow(self)
window.show()
After looking at this example code, it will probably occur to you that you are going to end up importing a lot of modules, and have a lot of boiler-plate code to write for each one of them (which is not much fun).
So I would advise you to consider changing your GUI design, so that you have one main window containing a tabwidget, and then have a separate tab for each of your converters. This will not only make your application much easier to write, but it should also make it a lot nicer to use.
I'm making my bachelor thesis in PyQt4. First I also wanted to use the designer (generating code is nice), but afterall I was not using it during my work. Maybe it's a matter of taste.
But for your question (I did this without the QtDesigner):
Let's say we have a main window class:
import sys
from PyQt4 import QtCore, QtGui
class mainscreen(QtGui.QMainWindow):
def __init__(self, parent=None):
super(mainscreen,self).__init__(parent)
self.button = QtGui.QPushButton("push")
self.button.clicked.connect(self.pushed)
#pyqtSlot()
def pushed(self):
# in this section here you can create the new window and show it
qApp = QtGui.QApplication(sys.argv)
aw = mainscreen()
aw.show()
sys.exit(qApp.exec_())
There are some good tutorials (http://zetcode.com/gui/pyqt4/ helped me getting started).
Make two programs: main_win.py and second_win.py, then in main_win.py put this lines:
from os import system as sh //In the begin
def openewin(self): //In the class main_win
sh("python second_win.py")
Ready, just connect the push button to function openewin!

PyQt4 new modul

I want to do some things with the PyQt4 framework. So I decided to do some browser like thing. Here is the code. Its just simple:
import sys
from PyQt4 import QtGui, QtCore, QtWebKit
class MainWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.resize(250, 150)
self.setWindowTitle('Testbrowser')
exit = QtGui.QAction(QtGui.QIcon('icons/exit.png'), 'Exit', self)
exit.setShortcut('Ctrl+Q')
exit.setStatusTip('Exit application')
self.connect(exit, QtCore.SIGNAL('triggered()'), QtCore.SLOT('close()'))
self.statusBar()
menubar = self.menuBar()
datei = menubar.addMenu('&Datei')
datei.addAction(exit)
tools = menubar.addMenu('&Tools')
app = QtGui.QApplication(sys.argv)
main = MainWindow()
web = QWebView()
web.load(QUrl("http://google.de"))
main.show()
sys.exit(app.exec_())
I think I understood some of the things here. But what I do not understand is, how can I work with new modules here? The MainWindow class inherits from the QtGui.QMainWindow class, thats ok. But what now? Should I create a whole new class which inherits from QWebView?? Kind of like :
class newclass(QWebView.QtWebKit):
def __init__(self):
QWebView.QtWebkit.__init__(self)
ect
Or how can I do this without a new class? Or how do I do this in general? I saw a webpage on which the author made a simple browser too. But he imported it in a new programm and made an object out of it and then did some stuff. Do I have to do this, or is there a simpler way? How is this all done in PyQt4?
Greets
some reference
http://pyqt.sourceforge.net/Docs/PyQt4/qwebview.html
Accessing elements of a module follows a dot notation convention in Python -- e.g. to access QWebView, you should use
web = QtWebkit.QWebView()
the URL would be
QtCore.QUrl("http://google.de")
If you want to have all of the names available to you without dot notation, you have to import everything:
from PyQt4.QtWebkit import *

Accesing PyQt Gui elements from another file

I am learning PyQt and coming from webdesign, so excuse this question that must have very obvious answer.So I am building a PyQt application and I would like to spread methods to several files to correspond different parts of GUI. How can I access textbox locating in fileA.py from fileB.py. :
#fileA.py
import sys
from PyQt4 import QtGui, QtCore
from gui1 import Ui_MainWindow
import fileB
class MyApp(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
Ui_MainWindow.__init__(self)
self.setupUi(self)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = MyApp()
window.show()
#This works all fine
def pressed():
window.plainTextEdit.appendPlainText("Hello")
window.pushButton.pressed.connect(pressed)
window.button2.pressed.connect(fileB.func3)
sys.exit(app.exec_())
Now, in this file I would like to use textbox from fileA.py
#fileB.py
import fileA
#How do I access window.plainTextEdit from fileA.py
def func3():
print "hello"
fileA.window.plainTextEdit.appendPlainText("Hello")
What am I doing wrong? What would be best way to spread functionality to multiple files if not this?
Thank you for taking time to read this.
You can take advantage of Python's class inheritance, like so:
fileA.py:
import sys
from PyQt4 import QtGui, QtCore
from gui1 import Ui_MainWindow
import fileB
class MyApp(fileB.MyApp, QtGui.QMainWindow):
def __init__(self):
self.MyMethod()
# Should print 'foo'
fileB.py:
import sys
from PyQt4 import QtGui, QtCore
from gui1 import Ui_MainWindow
class MyApp(QtGui.QMainWindow):
def MyMethod(self):
print 'foo'
Well, first off, the code under if __name__ == "__main__" will never be run when you are importing fileA.py, and so fileA.window does not exist. That's what it should do: run only when __name__ is "__main__", i.e. run as a top-level program. Instead, you should import fileA.py, create the QApplication and window again, then access window.plainTextEdit. However, this creates a very tight coupling between the code, as you are directly accessing a widget in MyApp from fileB. It might be better if instead, you expose a method in MyApp that appends to the text box instead of having fileB.py do it directly. So you may want to think about what you want to do and how to structure your program.
Of course, you don't have to structure your code that way; you could simply do
window = MyApp()
window.plainTextEdit.appendPlainText("Hello")
in fileB if you wanted.

Categories

Resources