Python - GUI checkbox cannot assign the function with arguments to variable - python-3.x

I'm having trouble getting my head around assigning a function to a variable when the function uses arguments. The arguments appear to be required but no matter what arguments I enter it doesn't work.
The scenario is that I'm creating my first GUI which has been designed in QT Designer. I need the checkbox to be ticked before the accept button allows the user to continue.
Currently this is coded to let me know if ticking the checkbox returns anything (which is does) however I don't know how to pass that result onto the next function 'accept_btn'. I thought the easiest way would be to create a variable however it requires positional arguments and that's where I'm stuck.
My code:
class MainWindow(QtWidgets.QMainWindow, Deleter_Main.Ui_MainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setupUi(self)
self.ConfirmBox.stateChanged.connect(self.confirm_box)
self.Acceptbtn.clicked.connect(self.accept_btn)
def confirm_box(self, state):
if self.ConfirmBox.isChecked():
print("checked")
else:
print("not checked")
checked2 = confirm_box(self, state)
def accept_btn(self):
if checked2 == True:
print("clicked")
else:
print("not clicked")
app = QApplication(sys.argv)
form = MainWindow()
form.show()
app.exec_()
The code gets stuck on 'checked2' with the error:
NameError: name 'self' is not defined
I thought there might be other solutions for running this all within one function but I can't seem to find a way whilst the below is required.
self.ConfirmBox.stateChanged.connect(self.confirm_box)
Would extra appreciate if anyone could help me understand exactly why I need the 'self' argument in the function and variable.
Thanks in advance,

If you just need to enable a button when the checkbox is checked, it can be easily done within the signal connection:
self.ConfirmBox.toggled.connect(self.Acceptbtn.setEnabled)
QWidget.setEnabled requires a bool argument, which is the argument type passed on by the toggled signal, so the connection is very simple in this case.
Apart from this, there are some mistakes in your understanding of classes in Python: it seems like you are thinking in a "procedural" way, which doesn't work well with general PyQt implementations and common python usage, unless you really need some processing to be done when the class is created, for example to define some class attributes or manipulate the way some methods behave. But, even in this case, they will be class attributes, which will be inherited by every new instance.
The line checked2 = confirm_box(self, state) will obviously give you an error, since you are defining checked2 as a class atribute. This means that its value will be processed and assigned when the class is being created: at this point, the instance of the class does not exist yet, Python just executes the code that is not part of the methods until it reaches the end of the class definition (its primary indentation). When it reaches the checked2 line, it will try to call the confirm_box method, but the arguments "self" and "state" do not exist yet, as they have not been defined in the class attributes, hence the NameError exception.
Conceptually, what you have done is something similar to this:
class SomeObject(object):
print(something)
This wouldn't make any sense, since there is no "something" defined anywhere.
self is a python convention used for class methods: it is a keyword commonly used to refer to the instance of a class, you could actually use any valid python keyword at all.
The first argument of any class method is always the reference to the class instance, the only exceptions are classmethod and staticmethod decorators, but that's another story. When you call a method of an instanciated class, the instance object is automatically bound to the first argument of the called method: the self is the instance itself.
For example, you could create a class like this:
class SomeObject(object):
def __init__(Me):
Me.someValue = 0
def setSomeValue(Myself, value):
Myself.someValue = value
def multiplySomeValue(I, multi):
I.setSomeValue(I.someValue * multi)
return I.someValue
But that would be a bit confusing...

Related

tkinter: How to pass arguments when <<ComboboxSelected>> is bound to a method?

I am a newbie with the tkinter library and I don't understand how to pass arguments from a combobox to a method I would like to bind on event action?
Example:
class Controller():
def __init__(self):
self.root = tk.Tk()
self.view = View(self.root)
self.view.sidepanel.motor_sel_combo.bind("<<ComboboxSelected>>", lambda event, arg=self.view.sidepanel.motor_sel_combo.get(): self.motor_selection(event, arg))
def motor_selection(self,event, mot_selected):
#print(event) #--> would only print <VirtualEvent event x=0 y=0> #????
#print(event.widget.get())
print(mot_selected)
The output for print(mot_selected) is empty. The tkinter manual does not show a good example.
self.view.sidepanel.motor_sel_combo.get() contains the imho the selected value of the combobox.
I tried as well with functools.partial instead of lambda, but I don't get it to work.
Could you show me the correct syntax with lambda and functools.partial, please?
Thank you.
You're calling self.view.sidepanel.motor_sel_combo.get() at the time you create the lambda, not at the time the combobox value has changed.
You should call the get() method inside the function instead of trying to pass it to the function. The use of lambda here does nothing but make the code unnecessarily complex.
class Controller():
def __init__(self):
self.root = tk.Tk()
self.view = View(self.root)
self.view.sidepanel.motor_sel_combo.bind("<<ComboboxSelected>>",
self.motor_selection)
def motor_selection(self,event):
mot_selected = self.view.sidepanel.motor_sel_combo.get()
...
It was pointed out in a comment by #acw1668 that the function should use event.widget.get() instead of self.view.sidepanel.motor_sel_combo.bet() and that is true. Since we want to call get() on the widget that was bound, using the shorter version is the better solution. It is better for two reasons: because it makes the function loosely coupled to the other code, and because it makes it more clear that the function is operating on the widget it was bound to. That means that the other code could rename that variable and the function will continue to work without having to also be modified.
For such a small program, whether the function is loosely coupled or tightly coupled isn't that big of a deal. I would argue, though, that using event.widget in a callback is a best practice that one should get in the habit of doing.
The OP indicated in comments they want to see a version that uses lambda. I don't recommend it, but the root of the problem with lambda is that the OP was calling the get() method when defining the lambda rather than when it is called. To call the get() method when the lambda runs, the call must be moved inside the body:
self.view.sidepanel.motor_sel_combo.bind(
"<<ComboboxSelected>>",
lambda event: self.motor_selection(event, self.view.sidepanel.motor_sel_combo.get())
)
In this case, motor_selection must be like in the OP's original code and accept two parameters.

How to make a singleton that inherits a normal class, with predefined values, and comparable by `is` without the need of rounded brackets?

My attempt was to create the default instance from inside of a metaclass, but to no avail. At least the reported class is the singleton in the example bellow.
EDIT: Clarifying requirements here: a singleton comparable by using the is keyword, without having to instantiate/call it. Unfortunately, this well known question-answer here doesn't seem to address that.
class MyNormalClass:
def __init__(self, values):
self.values = values
class MySingleton(MyNormalClass, type):
def __new__(mcs, *args, **kwargs):
return MyNormalClass(["default"])
print(MySingleton)
# <class '__main__.MySingleton'>
print(MySingleton.values)
# AttributeError: type object 'MySingleton' has no attribute 'values'
Metaclasses for singletons are overkill. (Search my answers for that, and there should be about 10 occurrences of this phrase).
In your example code in the question, the code inside the class and the metaclass methods is not even being run, not once. There is no black-magic in Python - the program just runs, and there are a few special methods marked with __xx__ that will be called intrinsically by the language runtime. In this case, the metaclass __new__ will be called whenever you create a new class using it as the metaclass, which you code does not show. And also, it would create a new instance of your "singleton" class each time it were used.
In this case, if all you need is a single instance, just create that instance, and let that one be public, instead of its class. You can even us the instance itself to shadow the class from the module namespace, so no one can instantiate it again by accident. If you want values to be immutable, well, you can't
ensure that with pure Python code in any way, but you can make changing values do not work casually with = so that people will know they should not be changing it:
class MySingleton:
__slots__ = ("values",)
def __init__(self, values):
self.values = values
def lock(self, name, value):
raise TypeError("Singleton can't change value")
self.__class__.__setitem__ = lock
MySingleton = MySingleton(["values"])

Inheriting __init_subclass__-parameters

Let's say I have a class that requires some arguments via __init_subclass__:
class AbstractCar:
def __init__(self):
self.engine = self.engine_class()
def __init_subclass__(cls, *, engine_class, **kwargs):
super().__init_subclass__(**kwargs)
cls.engine_class = engine_class
class I4Engine:
pass
class V6Engine:
pass
class Compact(AbstractCar, engine_class=I4Engine):
pass
class SUV(AbstractCar, engine_class=V6Engine):
pass
Now I want to derive another class from one of those derived classes:
class RedCompact(Compact):
pass
The above does not work, because it expects me to re-provide the engine_class parameter. Now, I understand perfectly, why that happens. It is because the Compact inherits __init_subclass__ from AbstractCar, which is then called when RedCompact inherits from Compact and is subsequently missing the expected argument.
I find this behavior rather non-intuitive. After all, Compact specifies all the required arguments for AbstractClass and should be usable as a fully realized class. Am I completely wrong to expect this behavior? Is there some other mechanism that allows me to achieve this kind of behavior?
I already have two solutions but I find both lacking. The first one adds a new __init_subclass__ to Compact:
class Compact(AbstractCar, engine_class=I4Engine):
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(engine_class=I4Engine, **kwargs)
This works but it shifts responsibility for the correct working of the AbstractCar class from the writer of that class to the user. Also, it violates DRY as the engine specification is now in two places that must be kept in sync.
My second solution overrides __init_subclass__ in derived classes:
class AbstractCar:
def __init__(self):
self.engine = self.engine_class()
def __init_subclass__(cls, * , engine_class, **kwargs):
super().__init_subclass__(**kwargs)
cls.engine_class=engine_class
#classmethod
def evil_black_magic(cls, **kwargs):
AbstractCar.__init_subclass__(engine_class=engine_class, **kwargs)
if '__init_subclass__' not in cls.__dict__:
cls.__init_subclass__ = evil_black_magic
While this works fine for now, it is purest black magic and bound to cause trouble down the road. I feel like this cannot be the solution to my problem.
Indeed—the way this works in Python is counter-intuitive—I agree with you on your reasoning.
The way to go to fix it is to have some logic in the metaclass. Which is a pity, since avoiding the need for metaclasses is exactly what __init_subclass__ was created for.
Even with metaclasses it would not be an easy thing—one would have to annotate the parameters given to __init_subclass__ somewhere in the class hierarchy, and then insert those back when creating new subclasses.
On second thought, that can work from within __init_subclass__ itself. That is: when __init_subclass__ "perceives" it did not receive a parameter that should have been mandatory, it checks for it in the classes in the mro (mro "method resolution order"—a sequence with all base classes, in order).
In this specific case, it can just check for the attribute itself—if it is already defined for at least one class in the mro, just leave it as is, otherwise raises.
If the code in __init_subclass__ should do something more complex than simply annotating the parameter as passed, then, besides that, the parameter should be stored in an attribute in the new class, so that the same check can be performed downstream.
In short, for your code:
class AbstractCar:
def __init__(self):
self.engine = self.engine_class()
def __init_subclass__(cls, *, engine_class=None, **kwargs):
super().__init_subclass__(**kwargs)
if engine_class:
cls.engine_class = engine_class
return
for base in cls.__mro__[1:]:
if getattr(base, "engine_class", False):
return
raise TypeError("parameter 'engine_class' must be supplied as a class named argument")
I think this is a nice solution. It could be made more general with a decorator meant specifically for __init_subclass__ that could store the parameters in a named class attribute and perform this check automatically.
(I wrote the code for such a decorator, but having all the corner cases for named and unamed parameters, even using the inspect model can make things ugly)

NameError, python3 get_step not defined

I'm following the book "Python crash Course" and i keep getting this
NameError: name 'get_step' is not defined
It's in python3 and both files are in the same directory.
So..the function is defined inside this class:
and the class is imported to:
where it calls fill_walks, and fill_walks calls get_step. What am i missing here? can someone help me please
You know how when you refer to x_values, y_values, and num_points, you have to say self.x_values, self.y_values, etc? This is true for all attributes of your instance, including methods. So when you want to do get_step() and get_step is defined on your class, you have to call self.get_step().
In addition, when you define a method on your class, by default it's what's called an instance method, and will automatically be called with the instance as the first argument. You've defined fill_walk correctly, with def fill_walk(self): but get_step is currently not defined properly. You either have to do
def get_step(self):
....
or, since get_step doesn't itself need to access any instance attributes, you can mark it as a static method by defining it like so:
#staticmethod
def get_step():
...
in random_walk.py
change
def get_step():
to
def get_step(self): #<--- self?

Knowing the context/scope at instantiation time

Is there any way to know the context in which an object is instantiated? So far I've been searching and tried inspect module (currentcontext) with poor results.
For example
class Item:
pass
class BagOfItems:
def __init__(self):
item_1 = Item()
item_2 = Item()
item_3 = Item()
I'd want to raise an exception in the instantiation of item_3 (because its outside a BagOfItems), while not doing so in item_1 and item_2. I dont know if a metaclass could be a solution to this, since the problem occurs at instantiation not at declaration.
The holder class (BagOfItems) can't implement the check because when Item intantiation happens outside it there would be no check.
When you instantiate an object with something like Item(), you are basically doing type(Item).__call__(), which will call Item.__new__() and Item.__init__() at some point in the calling sequence. That means that if you browse up the sequence of calls that led to Item.__init__(), you will eventually find code that does not live in Item or in type(Item). Your requirement is that the first such "context" up the stack belong to BagOfItem somehow.
In the general case, you can not determine the class that contains the method responsible for a stack frame1. However, if you make your requirement that you can only instantiate in a class method, you are no longer working with the "general case". The first argument to a method is always an instance of the class. We can therefore move up the stack trace until we find a method call whose first argument is neither an instance of Item nor a subclass of type(Item). If the frame has arguments (i.e., it is not a module or class body), and the first argument is an instance of BagOfItems, proceed. Otherwise, raise an error.
Keep in mind that the non-obvious calls like type(Item).__call__() may not appear in the stack trace at all. I just want to be prepared for them.
The check can be written something like this:
import inspect
def check_context(base, restriction):
it = iter(inspect.stack())
next(it) # Skip this function, jump to caller
for f in it:
args = inspect.getargvalues(f.frame)
self = args.locals[args.args[0]] if args.args else None
# Skip the instantiating calling stack
if self is not None and isinstance(self, (base, type(base))):
continue
if self is None or not isinstance(self, restriction):
raise ValueError('Attempting to instantiate {} outside of {}'.format(base.__name__, restriction.__name__))
break
You can then embed it in Item.__init__:
class Item:
def __init__(self):
check_context(Item, BagOfItems)
print('Made an item')
class BagOfItems:
def __init__(self):
self.items = [Item(), Item()]
boi = BagOfItems()
i = Item()
The result will be:
Made an item
Made an item
Traceback (most recent call last):
...
ValueError: Attempting to instantiate Item outside of BagOfItems
Caveats
All this prevents you from calling methods of one class outside the methods of another class. It will not work properly in a staticmethod or classmethod, or in the module scope. You could probably work around that if you had the motivation. I have already learned more about introspection and stack tracing than I wanted to, so I will call it a day. This should be enough to get you started, or better yet, show you why you should not continue down this path.
The functions used here might be CPython-specific. I really don't know enough about inspection to be able to tell for sure. I did try to stay away from the CPython-specific features as much as I could based on the docs.
References
1. Python: How to retrieve class information from a 'frame' object?
2. How to get value of arguments passed to functions on the stack?
3. Check if a function is a method of some object
4. Get class that defined method
5. Python docs: inspect.getargvalues
6. Python docs: inspect.stack

Resources