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.
Related
There is this question to discover existing types:
Getting Python Tkinter Object Type
However I've just developed tooltips (balloons) I've assigned to some buttons and would like to be able to recall all of them as a unique type. Also down the road I'd like to hand-craft canvas objects which will operate like pseudo buttons with <enter>, <leave>, <active>, <press> and <release> events. How might I declare a new object type for them?
If I understand your question correctly you want to check if an instance created is a instance of the custom class, it can be directly done using isinstance:
class CustomTooltip():
pass
cwidget = CustomTooltip()
btn = tk.Button(root)
print(isinstance(cwidget, CustomTooltip)) # True
print(isinstance(b, CustomTooltip)) # False
print(type(cwidget) == CustomTooltip) # Not recommended
It is recommended to use isinstance rather than type, from the docs:
The isinstance() built-in function is recommended for testing the type of an object, because it takes subclasses into account.
I did not quite get your 2nd question, also it is better to keep a single question focused around one main question rather than asking more than one question.
Object Oriented Programming is probably the solution.
If I want to create a "new type" of tkinter button I can sub-class it.
class MySpecialButton(tkinter.Button):
pass
This doesn't do anything special at the moment but will give it a unique type (If it have understood your interpretation correctly)
The following example from another one of my answers, creates a special button with custom behaviour for hovering over the button
class HoverButton(tk.Button):
def __init__(self, master, **kw):
tk.Button.__init__(self,master=master,**kw)
self.defaultBackground = self["background"]
self.bind("<Enter>", self.on_enter)
self.bind("<Leave>", self.on_leave)
def on_enter(self, e):
self['background'] = self['activebackground']
def on_leave(self, e):
self['background'] = self.defaultBackground
With regard to canvas object, You can obviously create classes for these too which can contain methods for moving/resizing the object. As to how to create custom events for these, you can use tag_bind(item, event=None, callback=None, add=None) to bind a call back to a canvas object. A quick example below
import tkinter as tk
class CanvasShape:
def __init__(self, canvas, callback = None):
self.canvas = canvas
self.id = canvas.create_oval(10,10,50,50,fill='blue')
self.canvas.tag_bind(self.id,'<Button-1>',callback)
def clicked(e):
print("You clicked on a shape")
root = tk.Tk()
c = tk.Canvas(root,width=200,height=200)
c.grid()
shape = CanvasShape(c,callback=clicked)
root.mainloop()
This will create a circle that when you click on it will fire an event that is received by the clicked function.
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?
I want to pass the text from the QLineEdit to the next function.
I want person_name to receive the text i entered in QLineEdit.
I tried using the function add_function(self,people_folder,shape), but when I declared in main at final it says that self is not defined.
What should I do, to make it work for person_name to receive the text I entered in the QlineEdit?
Try to instead of
def add_person(people_folder, shape):
person_name= (sys.argv[0])
adding self parameter to add_person method and calling text() from self.lineEdit instead of sys.argv[0]
def add_person(self, people_folder, shape):
person_name = self.lineEdit.text()
when i declared in main at final it says that self is not defined.
this is due to self is referencing to Object (created Class), you are not supposed to add self in method add_person(self,PEOPLE_FOLDER, SHAPE) when calling it. It's added automatically when called, but you need to call from created Object, like
myCamera = camera()
myCamera.add_person(PEOPLE_FOLDER, SHAPE)
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.
When you want to attach a callback to a kivywidget, for example a textinput you can use the bind() function. Example from Kivy docs for a textinput:
def on_text(instance, value):
print('The widget', instance, 'have:', value)
textinput = TextInput()
textinput.bind(text=on_text)
But how do I attach it to an element that was created in the kvlang file?
Get a reference to the element, then call bind as normal. For instance, for the root widget of the application you can use App.get_running_app().root.bind, or for others you can navigate the widget tree via kv ids.
You can call the bind() on the widget referenced by self.ids['id_from_kvlang']. However this cannot be done on class level, you need to operate on the instance. So you need to put it in a function of the class.
The __init__ function is called at the instantiation of the object so you can put it there. However you need to schedule it, so it won't happen instantly, the widgets you are binding to are not there yet so you have to wait a frame.
class SomeScreen(Screen):
def __init__(self,**kwargs):
#execute the normal __init__ from the parent
super().__init__(**kwargs)
#the callback function that will be used
def on_text(instance, value):
print('The widget', instance, 'have:', value)
#wrap the binding in a function to be able to schedule it
def bind_to_text_event(*args):
self.ids['id_from_kvlang'].bind(text=update_price)
#now schedule the binding
Clock.schedule_once(bind_to_text_event)