I created a lots of menu in tkinter,every menu has its corresponding command,so I have to creat a number of command functions.
If the label content can be an argument then I can pass it to only one function.How can I make it become an argument?
def show_rc_menu(self,event):
self.menu.post(event.x_root, event.y_root)
def creat_right_click_menu(self):
self.menu=Menu()
self.menu.add_command(label='hereiam1',command=self.hello)
self.menu.add_command(label='hereiam2',command=self.hello)
self.menu.add_command(label='hereiam3',command=self.hello)
...
self.menu.add_command(label='hereiam100',command=self.hello)
def resize(self,e):
print e
Use lambda just with one command:
def hello(text=None):
if text:
# Do some things
self.menu=Menu()
self.menu.add_command(label='hereiam1',command=lambda :self.hello('some_text'))
self.menu.add_command(label='hereiam2',command=self.hello)
self.menu.add_command(label='hereiam3',command=self.hello)
You can not influence the parameters that the menu button or another widget will pass into the callback function by itself (none, in this case), but you can use a lambda function to pass a parameter:
self.menu.add_command(label='hereiam1', command=lambda: self.hello('hereiam1'))
and similarly for the other buttons. Then, in your callback function, you test this parameter and execute the different actions:
def hello(self, hint):
if hint == "hereiam1":
print("do stuff for hereiam1")
elif hint == ...:
...
However, unless all those callbacks are very similar, I'd suggest actually using different callback functions for the different menu buttons.
Related
It’s working but I don’t understand why. I was able to play around until I found something that works and it depends upon hidden “default” arguments in callbacks, but I thought the “event” was the only hidden argument. I wanted to pass the class “self” also. I want an event in a tkinter class to call an external function (external to the class) and I want to pass the event and the class (“self”) as arguments. To do this I call internal functions which then call the external functions.
The confusing point is that I must do it differently for a call from a bind and a call from a button command. It works and does what I want but I also want to understand what is happening. I also would like to know if there is a better way. The following code is all within the class.
self.B1 = Button(self.frame_controls, text = "Go", command=lambda: self.process_go_internal(self))
self.canvas.bind('<Configure>', self.process_configure_internal)
def process_go_internal(event, rt):
process_go_external(rt, event)
def process_configure_internal(self, event):
process_configure(self, event)
Hmmm, it's difficult to see what you are doing when you are not supplying working code. However I'll provide an example of how you could do what I think you are trying to do:
The bind() function generates an event but the Button() does not, so you will have to treat them accordingly. I have them call different methods within the class.
In the button_press() method I supply a default value for event as there is no event passed by the Button.
As for passing a reference to the instance you can just pass it as you would any name.
from tkinter import *
root = Tk()
def external_function(instance, event):
print('external_function instance:', instance)
print('external_functionevent:', event)
print()
instance.do_it() # Try to call the Cheese instance
class Cheese():
def __init__(self):
root.bind('<Configure>', self.configure)
B1 = Button(root, text='Go', command=self.button_press)
B1.pack(padx=100, pady=20)
def configure(self, event):
print('<Configure>')
external_function(self, event) # Passing instance ref and event
def button_press(self, event=None):
print('Button press')
print()
external_function(self, event) # Passing instance ref and event
def do_it(self):
print("I'm doing it!")
print()
c = Cheese()
root.mainloop()
Was this helpful?
Lets say I create a custom Button class called MyButton. I want all created MyButtons to play the same sound when they are pressed. But I also want to add different functionality for specific buttons, for example I want some buttons to change label text, but I also want them to play that sound. Is there a way to do it with inheritance so I don't have to keep in mind that I have to add play sound functionality to every created button?
EDIT: Lets say I have a class MyButton declared as bellow:
class MyButton(Button):
def generic_function_for_all_buttons(self):
print('GENERIC FUNCTION')
now when I try to create a MyButton somewhere else in the code like this:
class TestClass(BoxLayout):
def __init__(**kwargs):
self.buttons = []
self.set_layout()
def button_action(self,button):
button.generic_function_for_all_buttons()
print(button.text)
def set_layout(self):
for i in range(0,100):
button = MyButton(text=i)
button.on_press = functools.partial(button_action, button)
self.buttons.append(button)
self.add_widget(button)
This is not runnable code, just a demonstration of what I want to achieve. Now each time I press MyButton from TestClass, it prints GENERIC FUNCTION and a number between 0-99 based on which button was pressed. But I had to add button.generic_function_for_all_buttons() line and I want to avoid it if possible. If each of the 100 buttons had its own different action like this:
def action_1(self,button):
button.generic_function_for_all_buttons()
print('1')
def action_2(self,button):
button.generic_function_for_all_buttons()
print('2')
def action_3(self,button):
button.generic_function_for_all_buttons()
print('3')
...
That button.generic_function_for_all_buttons() is 100 lines of code I want to avoid. I thought it must be possible somehow with inheritance,e.g I add on_press method to MyButton class like this:
class MyButton(Button):
def on_press(self):
print('GENERIC FUNCTION')
but then it simply ignores it.
Solution
Implement on_touch_down method in class MyButton
Check for collision using collide_point() function
Snippets
class MyButton(Button):
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
print('GENERIC FUNCTION')
print(f"MyButton.text={self.text}")
return True # consumed touch and stop propagation / bubbling
return super(MyButton, self).on_touch_down(touch)
class TestClass(GridLayout):
def __init__(self, **kwargs):
super(TestClass, self).__init__(**kwargs)
self.cols = 20
self.buttons = []
self.set_layout()
def set_layout(self):
for i in range(0,100):
button = MyButton(text=str(i))
self.buttons.append(button)
self.add_widget(button)
Kivy » Touch event basics
By default, touch events are dispatched to all currently displayed
widgets. This means widgets receive the touch event whether it occurs
within their physical area or not.
...
In order to provide the maximum flexibility, Kivy dispatches the
events to all the widgets and lets them decide how to react to them.
If you only want to respond to touch events inside the widget, you
simply check:
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
# The touch has occurred inside the widgets area. Do stuff!
pass
Output
I would create a base class called MyButton and create child button classes that inherit from MyButton.
Then using Inheritance and Polymorphism you can keep the attributes & methods, such as the sound, the same across all child buttons, but have different labels without needing to have unique attributes for each child.
Polymorphism will also allow you to iterate through all the children, as the attributes have the same name.
See article linked describing this a little more:
https://www.pythonspot.com/polymorphism/
Why, if I put a "normal" call to the method button1Click() in the bind call, does my program not even start? By removing the parenthesis the problem is solved.
I'm using this program as reference: Thinking in Tkinter
Also, why should I add the event argument to my button1Click() method?
from Tkinter import *
class MyApp:
def __init__(self, parent):
self.myParent = parent
self.myContainer1 = Frame(parent)
self.myContainer1.pack()
self.button1 = Button(self.myContainer1)
self.button1.configure(text="OK", background= "green")
self.button1.pack(side=LEFT)
self.button1.bind("<Button-1>", self.button1Click) # <--- no () !
def button1Click(self, event):
self.button2 = Button(self.myContainer1, text="lol")
self.button2.bind("<Button-1>", self.button1Click)
self.button2.pack()
root = Tk()
myapp = MyApp(root)
root.mainloop()
bind() expects something that is callable and that expects an argument.
If you pass self.button1Click(), you effectively pass it None, because that is what is returned by this call.
As the call is to be performed by the clickable object, you are not supposed to call it yourself.
So, next step: You pass it self.button1Click, and you clock the button. Then the given "thing" is tried to be called with an event object as argument, but that fails, because the method is not prepared for that.
So you have 2 options:
Either you modify the method so it can be called with an event object, such as def button1Click(self, event):,
or you wrap it in a lambda call: lambda event: self.button1Click().
In the latter case, you give the bind() method a callable which accepts exactly one argument, and does the call as wanted at the time of calling (thus the ()).
You can call the method button1Click "normally" using lambda. What might be happening right now is that it would be getting called anyhow.
For ex: command=lambda:self.button1Click()
You can pass more arguments if you like by putting them in the parenthesis.
You need to use the event argument because whenever you bind a method you are passing event object too automatically. In your case,its two arguments-the event object & self.
I am trying to access the label of a button in Tkinter, when the button is pressed. This involves returning a reference to the target button pressed.
Currently, as I have to input arguments, this is done by binding the command option to the lambda function i.e.
button['command'] = lambda: fun_to_call(arg)
Is there any way to return the instance? I have checked the TKDocs and it does not cover. Also, I have tried using a separate list of strings instead to get the label. However, it only returns the last element of the list (I believe this is due to the lambda function not binding the specific element to the list when creating the button instance. I have previously used this list to generate the list of buttons.)
In short, an event-based function bound to the button which returns its parent (the button being pressed).
def add_callback(control, fun):
def inner():
return fun(control)
control['command'] = inner
...
def test_callback(button):
print "button instance:", button
b = Button(text="click me")
add_callback(b, test_callback)
more declarative:
def with_callback(control, fun):
def inner():
return fun(control)
control['command'] = inner
return control
...
b = with_callback(Button(text="click me"), test_callback)
If I have two objects that will call the same methode then an event happens is it possible to see which of them that invoked the event?
To make it cleaer. If I have two buttons and one methode that are called then I click on them. What can I do in this methode to see whitch button that was clicked?
...
buttonA.Bind(wx.EVT_BUTTON ,self.methode)
buttonB.Bind(wx.EVT_BUTTON ,self.methode)
...
...
def methode(self,event)
#get the button that was clicked
Try this:
...
buttonA.Bind(wx.EVT_BUTTON ,self.methode)
buttonB.Bind(wx.EVT_BUTTON ,self.methode)
...
...
def methode(self, event)
#get the button that was clicked
button = event.GetEventObject()
print button.GetLabel()
The simplest approach would be to create two separate methods:
buttonA.Bind(wx.EVT_BUTTON, self.method_from_A)
buttonB.Bind(wx.EVT_BUTTON, self.method_from_B)
If these two methods share code, then they could both call some other helper method.
Instead of naming them something arbitrary like method_from_X, try to pick names that would clarify why the cases are different. For names, focus on the "why" rather than on implementation details.
If you really want to have a single callback method, you can follow the instructions here on Passing Arguments to Callbacks:
http://wiki.wxpython.org/Passing%20Arguments%20to%20Callbacks