I was surprised when a function that I wrote in python/tkinter and binded to Ctrl-b behaved strangely (specific: it was losing the value of the selected text, so that text.index(SEL_FIRST) was undefined).
I was surprised when, after changing the more improbable things I binded it instead to something else - and it worked!
I searched but didnt find anything: is Control-b binded to something default in tkinter???
alessandro
If you are talking about the text widget, from the official tk text widget documentation:
"The Left and Right keys move the
insertion cursor one character to the
left or right; they also clear any
selection in the text...Control-b and
Control-f behave the same as Left and
Right, respectively."
Thank you Bryan, here's the link - obviously on effbot http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm:
(...)
You could use the bind_class method to
modify the bindings on the class
level, but that would change the
behavior of all text widgets in the
application. An easier solution is to
prevent Tkinter from propagating the
event to other handlers; just return
the string “break” from your event
handler:
def ignore(event):
return "break"
text.bind("<Return>", ignore)
or
text.bind("<Return>", lambda e: "break")
By the way, if you really want to
change the behavior of all text
widgets in your application, here’s
how to use the bind_class method:
top.bind_class("Text", "<Return>", lambda e: None)
But there are a lot of reasons why you
shouldn’t do this. For example, it
messes things up completely the day
you wish to extend your application
with some cool little UI component you
downloaded from the net. Better use
your own Text widget specialization,
and keep Tkinter’s default bindings
intact:
class MyText(Text):
def __init__(self, master, **kw):
apply(Text.__init__, (self, master), kw)
self.bind("<Return>", lambda e: "break")
Related
I was wondering if there is any way to get around using global variables in the callback functions that are used in bind in tkinter.
What I refer to is:
canvas = Canvas(root, width=500, height=500)
canvas.bind('<B1-Motion>', func)
where func is now some function that is triggered when the mouse is dragged. What I want is something like:
canvas.bind('<B1-Motion>', func(arg))
In combination with:
def func(event, arg):
commands
I can see from https://docs.python.org/3/library/tkinter.html that one argument, which is the event itself, is given to the callback function, but it seems like waste of potential to not give this method any way to modify its callback in a different way.
Maybe I am mistaken and there is some technical reason why that is impossible in general or maybe there is an alternative to bind.
I was basically expecting something like:
buttoname = Button(...,...,..., command = Lambda: func(arg))
If anyone has any pointers, it would be much appreciated.
regards
Use a lambda that receives the event parameter and passes it along.
canvas.bind('<B1-Motion>', lambda e: func(e, arg))
c is the canvas, and I want to bind certain key( (ex)d ) to another function using c.bind_all()
IDK the event name that I should use..
c.bind_all('<KeyPress-D>', func)
dosen't work..
According to the canonical tcl/tk documentation there are three ways to bind to a normal character (other than "<" or space):
c.bind_all("d", func)
c.bind_all("<d>", func)
c.bind_all("<KeyPress-d>", func)
When you use Qt_Designer or Qt_Creator to design a form, objectName for any given widget is always set to something. But if you create a widget in code AND you need the objectName later, you have to assign it explicitly. So then the widget assignment takes at least two lines of code. This seems very inelegant.
Example:
button1 = QPushButton('button caption') # at this point objectName is some kind of empty string
button1.setObjectName('button1')
If you need to find the widget later (i.e. with findChild), you must have objectName set, otherwise you're out of luck.
Is there some way to automatically set objectName without extra lines of code? I looked at the PyQt5 Reference Guide and could not find such a feature. A similar question was asked on Code Review, but got no answers. Two lines of code isn't the end of the world, but it seems redundant. Somehow I'm required to assign this name twice once for Python (first line) and again for Qt.
You can pass objectName as a keyword argument when creating the button.
button1 = QPushButton('button caption', objectName='button1')
This can extend this to any Qt property during initialization:
button1 = QPushButton(text='button caption', objectName='button1', icon=icon1)
Moreover, signals can be connected when constructing an object, too:
button1 = QPushButton(text='button caption', objectName='button1', clicked=someMethod)
The added named argument is equivalent to button1.clicked.connect(someMethod)
I have managed to get a widget to appear by calling a function, then make it disappear by using the destroy method.
Unfortunately, the only way I managed to do that is by making the object global, which I understand is a bad way of doing things. I have tried several times to destroy the object without using global, but none of them worked.
Here's the relevant code:
def hayHijos():
print("ii")
z=var9.get()
if z==1:
var10 = StringVar()
global numHijosMat
numHijosMat=OptionMenu(app,var10,1,2,3,4,5,6,7,8,9,10)
numHijosMat.grid(column=2)
elif z==0:
print("aa")
numHijosMat.destroy()
var9 = IntVar()
hijosEntrePartes = Checkbutton(app, text="Hijos entre las partes", variable=var9, command=hayHijos)
hijosEntrePartes.var=var9
hijosEntrePartes.grid()
Two general possibilities.
Either you create a class context to keep track of elements such as widgets using class variables (self.widget for example). Therefor have a look at the class documentation
You return and pass the widget to / from your function.
This is not very suitable for callbacks but a general approach
def function(var, widget=None):
""" Widget is an optional argument. If not passed, it is initialized with None """
if var.get()==1:
# some code to create your widget
# make sure to use 'widget = ...' to create it
# to have the reference for your return
# call at the end of the function
else:
# some code to destroy your widget
# e.g. widget.destroy()
return widget
Using this code makes it easy for you to use the widget without making it global. Another variable is used in a global behaviour in your code. "var9". You should also pass this one on.
You would need to adapt your callback using maybe a lambda to pass both.
More recommended would be the class approach over here as lambdas often lack in scope of readability of code and reusability of code. (Could call it also "bad habit" but IMHO this is highly influenced by use case)
If you want to reuse the widget and only want to make it appear / disappear as the use of a Checkbutton suggests, I would rather recommend grid_remove method instead of widget.destroy method
Consider the following code:
text = Entry(); text.pack()
def show(e):
print text.get()
text.bind('<Key>', show)
Let's say I put the letters ABC in the Entry, one by one. The output would be:
>>>
>>> A
>>> AB
Note that when pressing A, it prints an empty string. When I press B, it prints A, not AB. If i don't press anything after C, it will never be shown. It seems that the Entry content is only updated after the binded command has returned, so I can't use the actual Entry value in that function.
Is there any way to get an updated Entry value to use inside a binded command?
You could replace the <Key> event with the <KeyRelease> event. That should work.
Here is a list of events: http://infohost.nmt.edu/tcc/help/pubs/tkinter/events.html#event-types
The reason for this has to do with Tk "bindtags". Bindings are associated with tags, and the bindings are processed in tag order. Both widget names and widget classes are tags, and they are processed in that order (widget-specific bindings first, class bindings second).
For that reason, any time you press a key your widget specific binding will fire before the class binding has a chance to modify the widget.
There are many workarounds. The simplest is to bind to <KeyRelease> since the class bindings happen on the key press. There are other solutions that involve either adding or rearranging bind tags, or using the built-in data validation features of the entry widget. Which is best depends on what you're really trying to accomplish.
For more information on the data validation functions, see this question: Interactively validating Entry widget content in tkinter
For a more comprehensive answer, see Tkinter: set StringVar after <Key> event, including the key pressed