I am having difficulties in trying to get a function in a separate class. I have a main class with a few functions, one of which is reset:
class GUI(wx.Frame):
[GUI STUFF HERE]
def reset(self):
self.data = [0]
Within that class i also have before the subroutines to initiate another class:
self.controlPanel = controlPanel(self.panel)
Which initiates another class which is a staticbox with buttons. Within that class I have a function bound to a button event:
def reset(self, event):
GUI.reset()
where the function "reset" is in the main GUI class. I get an error when i try to call reset in the main class, yet I can do it the other way round. Why is this and how can I fix it? I want button events in child classes to call a function in the parent class.
Thanks in advance.
"GUI" is not defined in "controlPanel", you want to call the method of the instance of "GUI".
One way would be to do the following in your button handler:
self.GetParent().reset()
Depending how complex your application this might get out of hand as it will no longer work if you insert another layer in between GUI and controlPanel.
You might want to look into using 'wx.lib.pubsub' and in your controlPanel use 'pub.sendMessage' and in your GUI use 'pub.subscribe'.
wxPython Phoenix pubsub doc
pubsub's doc
Related
Some weeks ago I had to simplify a program drastically, because I couldn't find a solution for this probably simple problem.
Now I'm stuck at the same issue again and hope somebody can help me to fix it.
I'm using PyQt5 and started a QWidget out of my main class Class_One, from which I opened a second class Class_Two with the following lines.
def on_create_search(self):
self.search_form = Class_Two()
self.search_form.show()
In Class_Two I save changes to a JSON file, which should also effect a dictionary and a visible QTreeWidget from Class_One.
So what I want to do is, to execute a method in Class_One, triggered by Class_Two, just as it was called from the running instance of Class_One.
The called method should read in the JSON file and apply the changes to the data and the GUI of the running instance of Class_One.
Calling such a method from another instance of Class_One didn't help me out, because the changes have to be applied to my running class instance of Class_One.
Perhaps there is an even better way, so I'm open for any suggestions.
Use callback.
class Class_One:
def callback(self, arg1, arg2):
# code you want
def on_create_search(self):
self.search_form = Class_Two()
self.search_form.show(self.callback)
class Class_two:
def show(self, callback):
# original code
callback(arg1, arg2)
This question already has answers here:
pyside connection Error "RuntimeError: Failed to connect signal clicked()"
(2 answers)
Closed 4 years ago.
I am fairly new to the concepts of OOP. I am having a hard time even wording my question. Maybe the question has been answered already, but worded differently!
I have a class called Create_Capsules(). It has two functions:
create a cube
create a sphere.
I created a class variable that appends the names of these newly created capsules.
Create Capsule Class (psuedo-code):
class Create_Capsule(object):
def __init__(self):
super(Create_Capsule, self).__init__()
self.user_capsules = []
def cube(self):
#psuedo-code
cap_name = create_cube(name='capsule_cube01')
self.user_capsules.append(cap_name)
return cap_name
def sphere(self):
#psuedo-code
cap_name = create_sphere(name='capsule_sphere01')
self.user_capsules.append(cap_name)
return cap_name
I have another UI Class that has two simple QPushButtons, Cube and Sphere.
The functions Create_Capsules().cube() and Create_Capsules().sphere() are being called from these buttons.
At the end of this process, I want to be able to query the class variable to get a list of all capsules created.
I tried to create an object of the Create_capsule class in the UI and pass that to the functions when the cube/sphere buttons are clicked.
Excerpts from the UI code:
Class toolUI(obj):
addNewBboxLabel = QLabel("Add New Capsule:")
user_capsule = Create_Capsule()
addCubeButton = QPushButton("Cube")
addCubeButton.clicked.connect(self.addCubeClicked(user_capsule))
# addCubeButton.clicked.connect(functools.partial(self.addCubeClicked), user_capsule)
addSphereButton = QPushButton("Sphere")
addSphereButton.clicked.connect(self.addSphereClicked(user_capsule))
# addSphereButton.clicked.connect(functools.partial(self.addSphereClicked), user_capsule)
def addCubeClicked(self, capsule_obj):
"""
Adds a cube to the scene
"""
user_cube = capsule_obj.cube()
print(user_cube)
print(capsule_obj.user_capsules)
def addSphereClicked(self, capsule_obj):
"""
Adds a sphere to the scene
"""
user_sphere = capsule_obj.sphere()
print(user_sphere)
print(capsule_obj.user_capsules)
On executing this, on opening the UI, a cube is being created (even without clicking the cube button in UI). And, I get this error message:
addCubeButton.clicked.connect(self.addCubeClicked(user_capsule))
RuntimeError: Failed to connect signal clicked()
I can choose to not bother about the class variable approach, and instead return the name of the capsule from the addCubeClicked() and addSphereClicked() functions, collect them in the UI and append a list there. But, that feels dirty as I don't want to clutter up my UI function with any other logic.
I feel like I am missing a key concept of OOP here.
In your toolUI class, you should wrap your code inside a method, such as __init__. You may also want to add self. to your widgets.
class toolUI(obj):
def __init__(self):
self.addNewBboxLabel = QLabel("Add New Capsule:")
self.user_capsule = Create_Capsule()
self.addCubeButton = QPushButton("Cube")
self.addCubeButton.clicked.connect(self.addCubeClicked(self.user_capsule))
self.addSphereButton = QPushButton("Sphere")
self.addSphereButton.clicked.connect(self.addSphereClicked(self.user_capsule))
Part of the reason was that your code didn't exist with an unique instance of the class, and self was not a defined variable. Placing it inside a method with a self param will define that variable for use and also allow other methods to access properties belonging to that object (e.g. self.addCubeButton).
I apologize if the question is not a correct statement about what is going on or it turns out to be specific for the example that I describe. It is just what I noticed. I have been learning how to make GUIs using Tkinter in Python 2.7 and I have followed several tutorials, and I have seen several different styles for structuring the code.
One way is the following:
import Tkinter as tk
root = tk.Tk()
root.title("My App")
root.mainloop()
The other way I have seen is:
import Tkinter as tk
class App(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
tk.Tk.title("My App")
root = App()
root.mainloop()
The second method creates a class App that inherits from the tk.Tk class, thus making App a subclass of tk.Tk? def __init__(self): is the constructor for class App I am defining. But then I must also call the __init__() function from the tk.Tk class as well. If that is true, why is that? When I just run root = tk.Tk(), its __init__() function is called then?
Yes, when you run root=tk.Tk() then an instance of the class tk.Tk is instantiated -- which means calling the __init__ of this class.
In the second method you want to create a new class -- which might be useful especially in a larger project, to be able to port it anywhere else.
You cannot append something to the parent's __init__ method, you can just override it by defining it anew. So if you still want all the useful stuff to happen, which is executed in the tk.Tk.__init__(), you have to call it explicitly.
One might argue, that a better style would be to use super() instead of hardcoding the parent class name. But this gets relevant in still more complicated projects...
When you create a class object, it's constructor gets called automatically. This is what is happening in both snippets. The first one instantiates an object for the Tk class so therefore the __init__ method for the Tk class gets called. Where in the second way, you are not creating an object for the Tk class. You are creating an object for your App class. This does not mean it will call the constructor for the class that it has inherited from. You need to call it explicitly.
Here is the strange thing:
Say, you have a tk.Button (RUN) inside a class and you can specify a callback like
self.RUN["command"] = self.run
This refers to a function inside the class, which has access to all members.
In essence, the callback sees a class instance.
You can also create a tk.ListBox. The machinery of getting the selection involves events.
You cannot pass an instance-of-a-class-function as above.
The callback sees only the opaque event/widget things in Tk.
You can do
def CurSelect(evt) :
...
lb.bind('<<ListboxSelect>>',CurSelect)
but there is no way to pass more than the event.
So, when you have several ListBoxes running in the mainloop, what happens?
How can you map event/widget to the class instance that owns the event/widget?
Sure, you could make up some global maps. (Ugly, tried, I hate it.)
Anything better out there?
You can try to use lambda to pass arguments.
arguments = []
widget.bind("<>", lambda event, arg=arguments: cur_select(event, arg))
def cur_select(event, arg):
return
Also, naming convention for Python function: lowercase with words separated by underscores as necessary to improve readability.
You'll have to forgive me, I am trying to teach myself OO but I have come across this problem with composition and 'has-a' relationships.
class Main(object):
def A(self):
print 'Hello'
def B(self):
self.feature = DoSomething()
class DoSomething(object):
def ModifyMain(self):
#Not sure what goes here... something like
Main.A()
def run():
M = Main()
M.B()
A real world example of the above simplification is a PySide application where Main is a MainWindow, and DoSomething is a dynamically created widget that is placed somewhere in the window. I would like DoSomething to be able to modify the status bar of the mainwindow, which is essentially calling (in Main) self.statusbar().
If there is a shortcut in PySide to do this, Tops!! please let me know! However, I'm actually after the more general Pythonic way to do this.
I think I'm close ... I just can't make it work...
Why don't you use a signal and slot instead? That's a more Qt and OOP way of doing this.
In your dynamically created widget class:
self.modifyMain = QtCore.Signal(str)
In your main class:
#QtCore.Slot(str)
def changeStatusbar(self, newmessage):
statusBar().showMessage(newmessage)
in you main class after creating your widget:
doSomething.modifyMain.connect(self.changeStatusbar)
And in you widget class, where you want to change the statusbar of main, you say:
modifyMain.emit("Hello")
None of this is tested as I don't have a PySide installation handy.
There are two problems with your code:
At no time do you call ModifyMain; and
Main.A() will result in an error, because A is an instance method, but you are calling it on a class.
You want something like:
class Main(object):
def A(self):
print 'Hello'
def B(self):
self.feature = DoSomething() # self.feature is an instance of DoSomething
self.feature.ModifyMain(self) # pass self to a method
class DoSomething(object):
def ModifyMain(self, main): # note that self is *this* object; main is the object passed in, which was self in the caller
#Note case - main, not Main
main.A()
def run():
M = Main()
M.B()
if __name__=="__main__": # this will be true if this script is run from the shell OR pasted into the interpreter
run()
Your names all flout the usual python conventions found in PEP8, which is a pretty good guide to python style. I have left them as they were in your code, but don't copy the style in this example - follow PEP8.