How can I bind a property to another property in Kivy? - python

I want to bind a property of a widget to a property of a child widget. Thus when the root widget property is changed, the change is propagated to the child property as well.
I tried it this way:
self._Child._MyProperty = self._MyProperty
This works... sometimes. But sometimes it does not work. I cannot find out when it works or why and under which conditions it does not work.
In all cases I have a binding to a method in the root widget as well:
self.bind(_MyPropert = self._MyPropertyChange)
This method is called in all cases, but sometimes the change is not propagated to the child property.
This does not work even if it feels very natural:
self.bind(_MyProperty = self._Child._MyProperty)
But in Kivy, I could do:
<RootWidget>
<ChildWidget>
_MyProperty: self.parent._MyProperty
The problem is I want to do it in Python, not in Kivy.

To bind one property to another you should use the setter event:
self.bind(_MyProperty=self._Child.setter('_MyProperty'))

Related

tkinter: Best way to tag an arbitrary widget for retrieval

While building my own widgets based on the stock ones, I've been adding a "name" field to every widget class of mine for easier access later, like:
class MyFrame(tk.Frame):
def __init__(self, master, name, *args, *kwargs):
super().__init__(master, *args, *kwargs)
self.name = name
After setting up the entire window layout, I could do something like:
mywidget = allWidgets['myWidgetName']
But I couldn't help but wonder if there is a better approach by using a widget's built-in attributes, without adding a new tag. I know that winfo_children() helps traverse the widget tree, but I do need random access.
Based on #BryanOakley 's tip, I finally discovered that all Tkinter widgets have a ._name attribute, whose default value is !<classname>, all lowercase even if the classname uses mixed cases.
I could then assign any name to ._name after instantiating my widget class.
It's understandable that this detail falls out of the official doc because this is not part of the public interface, but knowing this instantly saved memory for my own work.
However, I guess that it'd be my own responsibility to maintain the uniqueness of widget names if I started playing around with it.
I have to thank Bryan a million for his resourcefulness when it comes to Tkinter.

PyQt widget seems to "forget" its parent

I have a strange issue when adding a widget to a PyQt5 application.
The following is the actual code, stripped off of everything that doesn't seem related (like translateUI):
class OllRoot(preferences.Group):
"""Basic openLilyLib installation"""
def __init__(self, page):
super(OllRoot, self).__init__(page)
self.setParent(page)
self.changedRoot()
layout = QGridLayout()
self.setLayout(layout)
self.directory = widgets.urlrequester.UrlRequester()
self.directory.changed.connect(self.changedRoot)
layout.addWidget(self.directory, 0, 1)
def changedRoot(self):
print("Self:", self)
print("Parent:", self.parent())
self.parent().changed.emit()
# TODO: Check for proper openLilyLib installation
When the constructor is called, parent() is correctly set to the object that has been passed in as page, so the two proper objects are printed.
Self: <preferences.openlilylib.OllRoot object at 0x7f855a1de288>
Parent: <preferences.openlilylib.OpenLilyLibPrefs object at 0x7f855a1bcb88>
However, when I make a change in the self.directory widget changedRoot is called again (as I've connected it), but now the parent seems to have disappeared:
Self: <preferences.openlilylib.OllRoot object at 0x7f855a1de288>
Parent: <PyQt5.QtWidgets.QWidget object at 0x7f855a1dbc18>
Question:
Am I doing anything wrong with the setParent?
Am I doing anything wrong with the connect?
Does the object somehow "forget" its parent?
PS: A comparable file which served as a model can be found here: https://github.com/wbsoft/frescobaldi/blob/master/frescobaldi_app/preferences/general.py#L56.
Whenever a widget is added to a layout, Qt will automatically re-parent it so that it becomes a child of the widget the layout is set on. Calling setParent (with a different widget) in __init__ will have no lasting effect.
See: Tips for Using Layouts in the Layout Management Overview.

Why does __init__() have to be used twice when I inherit from another class?

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.

traits api creating new trait and trait changed behavior

I often find myself in this situation:
class A:...
B=class
a=Instance(A,())
#on_trait_change('a')##I would really like to be able to do this
def do_something(...)
I think that this currently triggers if you were to reset the entirety of the class. e.g. b=B(). b.a=A() should trigger it. But I would like to control when my custom class signals that it has been 'changed'. Per haps one might like A to signal 'changed' if merely a member of A is changed e.g. b.a.x+=1
If both A and B derive from HasTraits, then changing your decorator to #on_trait_change('a.+') will do what you want. If you change the signature of your do_something to two or more arguments, you'll even be able to detect which attributes of a changed. (See http://traits.readthedocs.org/en/latest/traits_user_manual/notification.html#notification-handler-signatures.)

QWheelEvent.ignore() not passing event through?

In a subclass of QGraphicsView, I implemented the wheelEvent(event) event-handler as follow:
def wheelEvent(self, event):
print "Wheel event received"
event.ignore()
From what I understand, this should actually do little more than printing the above string. The QWheelEvent.ignore() should pass the event to the parent and go about its usual business (namely scrolling). The documentation is explicit about this:
If you reimplement this handler, it is very important that you ignore() the event if you do not handle it, so that the widget's parent can interpret it.
In this case, the QGraphicsView is the main widget, so it has no parent (unless by parent one means the parent class from which it is derived).
In practice however, the string is printed as expected but the view does not scroll its content.
So how do I implement this method and get the QGraphicsView to scroll?
Well if you are never calling the main class, it will be pretty hard for the parent (and yes that's the object you are inheriting from) to do anything, when would the code run?
Adding super(type(self), self) should help (thanks to #mata for the correct python2 syntax)

Categories