How to use multiple wxPython project files in one program? - python

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

Related

Avoid circular import when accessing instance value from main.py

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

Qt Creator/Designer Program Doesn't Show?

I have created a simple test program in Qt Designer that allows you to select a folder and display its contents on a window. It looks like this:
I have successfully converted the .ui file to .py without fail. Next, here is my code to run the program, aptly named main.py:
from PyQt4 import QtGui
import sys
import design
import os
class ExampleApp(QtGui.QMainWindow, design.Ui_MainWindow):
def _init_(self):
super(self._class_, self)._init_()
self.setupUI(self)
self.btnBrowse.clicked.connect(self.browse_folder)
def browse_folder(self):
self.listWidget.clear()
directory = QtGui.QFileDialog.getExistingDirectory(self,"Pick a Folder")
if directory:
for file_name in os.listdir(directory):
self.listWidget.addItem(file_name)
def main():
app = QtGui.QApplication(sys.argv)
form = ExampleApp()
form.show()
app.exec_()
if __name__ == '__main__':
main()
In my command prompt, I run the following code:
python main.py
It proceeds to load for a second or two, and then I get this:
Is there something that I am doing wrong? Why isn't my program showing up the way it should be? Any help is appreciated!
These lines are wrong:
def _init_(self):
super(self._class_, self)._init_()
Instead you want something like:
def __init__(self, parent=None):
super(ExampleApp, self).__init__(parent)
Note the double underscores, the different super argument, and passing parent to the super class. I can't test this right now, but it should be much closer to working.
By naming your __init__ method incorrectly it never would've been called. That explains why you get a window but not the one you designed.

What is the purpose of using python `super() `, inside `__init__ ` other than the inheritance?

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

Set text of textbox on button click

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))

Difficulty accessing widgets after loading interface from ui file with loadUi()

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.

Categories

Resources