Appending dynamically created names to a class variable from UI Class [duplicate] - python

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

Related

Python - Sharing variables between different instances of different classes [duplicate]

This question already has an answer here:
Python: How to share data between instances of different classes?
(1 answer)
Closed 4 years ago.
I have been searching for the next answer but for sure I have been searching the wrong keywords.
I used to develop with C++, passing pointers as references between objects. The case is, now I'm trying to build a program in Python where one instance of a class 'General' initializes different instances of a class 'Specific' with the same shared variable.
class General():
def __init__(self):
self._shared_variable = 0
self._specific1 = Specific(self._shared_variable)
self._specific2 = Specific(self._shared_variable)
class Specific():
def __init__(self, shared):
self._shared = shared
def modify_shared_variable(self):
self._shared_variable +=1
So what I'm trying to do is shared this 'shared_variable' within de General scope, so when a 'Specific' instance modifies his internal variable, this change is seeing or mirrored by the other instance. But this is not the case in python. So, every specific instance has its own variable. How can I achieve this?
You can't have references to variables in Python. A variable is just a name, in some namespace (usually the __dict__ of a module, class, or instance object, or the special local namespace inside a function-call frame), for a value.
You can have references to values, but of course numbers are immutable values, so you can't change the number 1 into the number 2.
So, what you can do is create some kind of mutable value that holds the number, and share references to that.
One obvious possibility is to just give each Specific instance a reference to the General instance that created it:
class General():
def __init__(self):
self._shared_variable = 0
self._specific1 = Specific(self)
self._specific2 = Specific(self)
class Specific():
def __init__(self, shared_general):
self._shared_general = shared_general
def modify_shared_variable(self):
self._shared_general._shared_variable +=1
Another possibility is to store a single-element list:
class General():
def __init__(self):
self._shared_variable = [0]
self._specific1 = Specific(self._shared_variable)
self._specific2 = Specific(self._shared_variable)
class Specific():
def __init__(self, shared):
self._shared = shared
def modify_shared_variable(self):
self._shared[0] += 1
(This is really the same thing you're doing in C++, but without the syntactic sugar of arrays and pointers being nearly the same thing…)
Or you can create a simple MutableInteger class that holds an int, proxies non-mutating methods to it, adds a set method to replace it, and handles += and other mutating methods by calling set and returning self, instead of returning a new value.

Keeping classes loosely coupled and sharing data

I've been working in python on a project where I have a GUI which I split up a bunch of the work between classes. I don't know a lot of the best practices for passing data around between classes, and I've frequently run into the issue, where I have to implement something, or change something for work, and I've resorted to making a lot of the classes objects of another class in order to give it the data I need.
Any ideas or suggests would be greatly appreciated on how to keep my classes independent for later modification and still pass the relevant data around without affecting interfaces too much?
As an example
class Window():
def __init__(self, parent=None):
self.parent = parent
def doStuff(self):
#do work here
class ParseMyWork(Window):
def __init__(self, parent=None):
self.parent=parent
I often find myself doing stuff like the above giving objects to class Window
or simply inheriting everything from them as in ParseMyWork
There must be better and cleaner ways of passing data around without making my classes utterly dependent on eachother, where one little change creates a cascade effect that forces me to make changes in a bunch of other classes.
Any answers to the question don't necessarily have to be in python, but it will be helpful if they are
If I'm understanding your question correctly, I would say that inheritance is not necessary in your case. Why not give ParseMyWork a function for dealing with a specific Window task?
class Window():
def __init__(self, parent=None):
self.parent = parent
def doStuff(self):
#do work here
class ParseMyWork():
def __init__(self, parent=None):
self.parent=parent`
def doWindowActivity(self, window):
window.doStuff
Then you can use the function like this
work_parser = ParseMyWork()
window = Window()
work_parser.doWindowActivity(window);
That way you can use your work_parse instance with any window instance.
Apologies in advance for my Python, it's been a while so if you see any rookie mistakes, do point them out.
Keep it simple.py:
def doStuff(window):
#do work here
return window
def parseStuff(stuff):
pass
really.py:
from simple import doStuff, parseStuff
def really_simple(window):
okay = doStuff(window)
return parseStuff(okay)
don't complicate the class:
from really import really_simple
really_simple(window)
imo: classes are overly complicated objects, and in a lot of cases more confusing than they need to be, plus they hold references and modify stuff, and can be difficult to decouple once they have been tied to other classes. if there isn't a clear reason why a class needs to be used, then it probably doesn't need to be used.
Classes are super powerful, so it's good you're getting started with em.
Discalimer: Haven't worked in python for a while now, so things might not be exact. The general idea still applies though.
Getting into your question now:
I would say the best way to achieve what you want is to create an instance of the first object where you will extract information from.
Now when creating a class, it's vital that you have attributes within them that you will want to be stored within it that you would like to retrieve once the class is instantiated.
For example, using your Window class example above, let's say that you have an attribute called resolution. It would look something like this:
class Window():
def __init__(self, parent = None):
self.parent = None
self.resolution = '40x80'
Now the resolution information associated with your Window class is forever part of any Window class instance. Now, the next step would be to create a get method for resolution. This should be done as follow:
class Window():
def __init__(self, parent = None):
self.parent = None
self.resolution = '40x80'
def getResoultion():
return self.resolution
Now, the reason we created this get method is because we can now set a variable to the information that is returned with it.
So let's say that you have everything associated with your Window class in its own file (let's say the file name is called Window.py). In a separate file (let's call it main.py), you can do the following:
import Window
windowInstance = Window()
windowResolution = windowInstance.getResolution()
If you print out the variable windowResolution, you should get that 40x80 printed out.
Now, as a side note, I do believe it is possible to get the information associated with an attribute with an instance of a class by simply doing something like
windowResolution = windowInstance.resolution
but that is bad practice in general. The reason, in a nutshell, is because you are now exposing attribute names of your class which you do not want to do because it makes it easy for a person outside of your code to learn the name where that information is held and change it. This can then lead to a myriad of other problems when it comes to making an overall program work. That is why it is best practice to use getters and setters. I already showed what getters are. Simply a get method for attributes. Setters, as you can probably assume, allow for one to set the information of an attribute to something else. Now you might say "Gabe, if we can create setter methods, what's the point of it if they just change it". My answer to that is to not give a setter method to all attributes. For attributes you don't mind for a person to change, give it a setter method, but for attributes you do not want any outside users to touch, simply don't create a setter method for it. Same goes with getter methods too. Users don't need to see all of the information of all attributes that makes your program work. Here's a better explanation: https://en.wikipedia.org/wiki/Mutator_method
Now, back to your example. Now let's say you have your ParseMyWork class in its own file like we did with your Window class, and let's say that ParseMyWork needs the resolution info from Window class. You can do the following :
import Window
import ParseMyWork
windowInstance = Window()
windowResolution = windowInstance.getResolution()
parseInstance = ParseMyWork(windowResolution)
This will only pass the window resolution information associated with your Window class. Hope this helps.

How come I cannot access an instance of a class defined globally inside another class?

I'm trying to use the ui object within the Pawn class and I define and initiate ui outside everything, so it is global right? I've looked on here for questions relating to using variables outside classes but they seem to all refer to .self which I used in the UI class when initiating the Chess object.
The # represent bits of code I've cut out to help readability.
class UI:
def ___init__(self):
self.chess = Chess()
# Calls main menu function which goes to a new game option
def newGame(self):
self.chess.startGame()
while len(self.chess.pieces[0] == 2): # Which is index for Kings
# Game, which is where *move* is called
class Chess:
def startGame(self):
self.chessBoard = Grid(9,9)
self.pieces = []
self.pawns = []
for i in range(8):
self.pawns.append(PawnObject(x,y,side))
self.pieces.append(self.pawns)
class Grid:
# Init method
def cellOccupied(self,x,y):
# This function checks if a place is empty
# If empty, return false else, true
class Object:
# Sets x, y, width, height
class ChessPiece:
# Child of Object sets side (black or white)
class PawnObject:
def move(self,newX,newY):
if ui.chess.chessBoard.cellOccupied(newX,newY) == False:
# Move
# More code
ui = UI()
Traceback: https://gyazo.com/33e5b37b5290ff88433b29874c117ad7
Am I doing something blindingly wrong? I think the way I've programmed this all is very inefficient as I am still learning so is this a result of that? Thank you.
The problem is that this cascading series of events all happens inside the initialisation function for UI; one class calls the next, all before the original __init__ has had a chance to return. This means that the line that did that initialisation has not completed, so the ui variable does not exist yet.
You should try and move some of this out of that cascade. In particular, I can't see why the pawns should move as a result of initialising the UI class; that doesn't seem to make sense at all.
You should also consider why you need ui to be a global variable; seems more likely that it should be an attribute of one of the classes, perhaps Grid, and the pawn can reference it via that instance.
You're probably using ui in the body of a class, which is executed the moment the interpreter sees the class (and therefore before ui exists). You can only use it inside methods or functions since those are only executed when they're called. In other words:
class UI:
def open(self):
pass
class Chess:
ui.open() # <--- this causes an error because it happens before the last line does
def startGame(self):
ui.open() # <--- this is OK
ui = UI()
Also, I think your message indicates that you wrote global ui somewhere which is only necessary if you're planning on assigning a new value to ui, i.e. ui = something. If you just want to use it, e.g. ui.open(), you don't need the global declaration. But removing it won't solve your problem.
You also need to correct
chessBoard = Grid(9,9)
to
self.chessBoard = Grid(9,9)
to make chessBoard an attribute of Chess, which you need to be able to say ui.chess.chessBoard.

wxPython cannot call function in main class from subclass

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

python / tk - affecting widget in other class

I am attempting to create a ttk.notebook in python where the selection in one tab affects the selection of a widget in a separate tab. Each tab is currently set up as a different class. Is there a way to pass or call a function in one class(tab) and have it change the widget/call a function in the other class(tab)?
in short, i have two functions: lb1 and lb2 (for tk lisboxes). Ideally, I would like the selection function on lb1 to call a function to populate a list in lb2. Each are in different classes.
a general sample of what I am trying to do follows.
class One(ttk.Frame):
...
lb1 = Listbox(listvariable = apps, height = 5)
def lb2_lookup(self, *args):
#this would somehow call a function to populate lb2
self.lb1.bind('<<ListboxSelect>>', self.lb2_lookup)
class Two(ttk.Frame):
lb2 = Listbox(listvariable = lb2apps, height = 5)
Thanks a bunch. I apologize if the code sample makes no sense, but I believe it gets the general point across.
The solution is that for one class to call a function in another, it simply needs to have a reference to that class, or a reference to some sort of controller class that has a reference to the other class.
class One(ttk.Frame):
def __init__(self, master, other_class):
self.other_class = other_class
...
self.lbl.bind('<<ListboxSelect>>`, self.other_class.lb2_lookup)
two = Two(...)
one = One(..., other_class = two)
Another way to accomplish the same thing is to have the class provide an interface, so that you can connect the classes after they are created:
class One(...):
...
def set_target(self, other_class):
self.other_class = other_class
class Two(...):
...
one = One(...)
two = Two(...)
one.set_target(two)
Finally, as written your code is tighly coupled. That means that even a small change in Two might mean you have to modify class One as well. That makes for code that is hard to maintain. You should create an interface that doesn't require one class to know much about the implementation of the other class.
Specifically, in your example you are setting up a binding to call lb2_lookup. But what if you change class Two and rename lb2 to lb3? Do you really want to have to also modify One? Better to create a function in Two that doesn't directly relate to a widget. For example:
class One(...):
...
self.lb1.bind('<<ListboxSelect>>', self.other_class.lookup)
With that, you are now free to reimplement Two however you want. The only requirement is that you keep a method named lookup. However, exactly what lookup does can change as long as it works the same way.
So, for example, right now lookup could return the value from a widget named lb2, but later it could look up data from a widget named foobar. No matter what lookup does, as long as it works in the same way (takes the same arguments, returns the same type of result), you won't have to modify One whenever you change Two.

Categories