How to disable text shaking when I click a button? - python

I am working with buttons in Tkinter, Python.
The thing is when I click in one button the text of the button shakes. It might be a default behavior for this widget and I don't know how to disable it and make it static.

I assume that you mean the relief change from raised to sunken when you click a button.
This is what I found on http://wiki.tcl.tk/1048 (click 'Show Discussion' to see it):
Unfortunately, the relief used when you click is hardcoded (as
'sunken'), so you can't configure it per-widget without hacking the Tk
internals for the binding for buttons.
So the simplest way around this would be to always make the button appear sunken
MyButton = Tkinter.Button(
self.frame,
text = "Foobar",
command = self.foobar,
relief=Tkinter.SUNKEN
)
The disadvantage of that is that it might make the button look unresponsive.
You can also use a widget other than a button to be used as a clickable item (suggested by Joel Cornett). Here is a simple example with a label used as a button:
import Tkinter
class main:
def __init__(self,root):
# make a label with some space around the text
self.lbl1 = Tkinter.Label(root,
width = 16, height = 4,
text = "Foobar")
self.lbl1.pack()
# Call a function when lbl1 is clicked
# <Button-1> means a left mouse button click
self.lbl1.bind("<Button-1>", self.yadda)
self.lbl1.bind("<Enter>", self.green)
self.lbl1.bind("<Leave>", self.red)
def yadda(self, event):
self.lbl1.config(text="Clicked!")
def green(self, event):
self.lbl1.config(bg="green")
def red(self,event):
self.lbl1.config(bg="red")
if __name__ == "__main__":
root = Tkinter.Tk()
main(root)
root.mainloop()

Related

Make Button in Python Tkinter Stop Moving when Pressed

So I am working on making an application inside of Tkinter and I am not that experienced in it, but I noticed that when you press a button it moves the text inside down and to the right just a couple pixels and I can't figure out how to make it not do that. Any help finding what parameter I need to change would be greatly appreciated.
I tried finding any docs on the subject but whenever I looked it up I would only get things that pertained to how to place a button on the window or in a frame/canvas with place, grid, pack.
will you can change the relief value to be SUNKEN and adjust the border values like this
Button(root, text = 'Click me !', borderwidth=5, relief=SUNKEN)
the button will look a little weird
or you can use widget other than a button just like this answer (https://stackoverflow.com/a/10674950/20411925)
import tkinter
class main:
def __init__(self,root):
# make a label with some space around the text
self.lbl1 = tkinter.Label(root,
width = 16, height = 4,
text = "Foobar")
self.lbl1.pack()
# Call a function when lbl1 is clicked
# <Button-1> means a left mouse button click
self.lbl1.bind("<Button-1>", self.yadda)
self.lbl1.bind("<Enter>", self.green)
self.lbl1.bind("<Leave>", self.red)
def yadda(self, event):
self.lbl1.config(text="Clicked!")
def green(self, event):
self.lbl1.config(bg="green")
def red(self,event):
self.lbl1.config(bg="red")
if __name__ == "__main__":
root = tkinter.Tk()
main(root)
root.mainloop()

Using grid() with TopLevel window not laying out widgets as expected

I'm trying to use the grid() layout manager to apply to a Toplevel window that successfully opens, but I can only get pack() to work. It seems that using the grid(row=x, column=y) isn't working - what am I missing please?
I'm using the following code:
def messageWindow():
# create child window
win = Toplevel()
win.geometry("400x400")
# display message
message = "This is the child window"
my_label = Label(win, text=message)
my_label.grid(row=5, column=8)
# quit child window and return to root window
# the button is optional here, simply use the corner x of the child window
#close_btn = Button(win, text='Close', command=win.destroy).place(x=150, y=200) -- this works but I want to use grid()
close_btn = Button(win, text='Close', command=win.destroy).grid(row=1, column=1)
When this is triggered using a menu command, the following result is returned:
enter image description here
I would expect the grid() layout manager to distinguish between the row/column parameters that I have specified for the label and button widgets.
The same grid() layout manager does work when referring to the parent window=Tk().

Python - Simple tkinter menu

I need help from you.
I want to code a simple menu with tkinter, but i have problem with that.
What I want to do - In my menu,there are 2 items: "first", "second". When i click on the first, program must write 'The first' and then when I click on the second, it must write second, but the first one there will be not yet.
Can anybody help me? Thx.
I mean something like this
from tkinter import *
root = Tk()
def do_something():
# this function e.g. write 'The first'
pass
def do_something_other():
# this function e.g. write 'The second' (but 'The first' there will be not yet)
main_menu = Menu(root)
root["menu"] = main_menu
submenu1 = Menu(main_menu)
submenu1.add_command(label="Item1", command=do_something)
submenu1.add_command(label="Item2", command=do_something_other)
main_menu.add_cascade(label="Program", menu=submenu1)
My goal is, that the canvas will be changing after clicking on the Item1/Item2
You can use a Tkinter Label widget to display text in your GUI. The Label widget has a method called config that you can use to access options of the widget, to include its text attribute.
Here's a simple example. You'll have to modify it to work with your menus and specific needs:
root = Tk()
def callback(): # this function will access the
label.config(text='Updated Text') # config method of label to change it
label = Label(root, text='Original text') # make the label and set default text
btn = Button(root, text='Change text', command=callback) # make the button, which executes the callback func
label.pack()
btn.pack()
mainloop()

Why does Tkinter hang when I call tkSimpleDialog.askstring from a lambda?

I'm developing a GUI application that models an essay. Among other things, the user can create a new topic and then populate that topic with notes. At the moment, I have two ways of creating new topics: through a dropdown option in the menu (the menu command) and through a button on the main screen (the button command). The button starts life with the text "New Topic". When the user presses the button, the program makes a new topic, asks the user to name the topic using tkSimpleDialog.askstring, and then sets the button's text to be the name of the topic and the number of notes in that topic. The button's command then changes to be adding a note to that topic.
While developing the program, I first verified that the menu command worked. It calls askstring successfully, creating a new popup window that handles input in the way I wanted. However, as soon as I added the button command, the call to askstring failed, even when called via the menu command. The window that should have the askstring dialog is whited out and the program hangs. If I comment out the button command, it works again. If I comment out the menu command, it hangs.
Here's the code where I add the command to the menu:
TopicBtn.menu.add_command(label="New Topic", underline=0,
command=self.newTopic)
Here's the code for newTopic():
def newTopic(self, button=None):
""" Create a new topic. If a Button object is passed, associate that Button
with the new topic. Otherwise, create a new Button for the topic. """
topicPrompt = "What would you like to call your new topic?"
topicName = tkSimpleDialog.askstring("New Topic", topicPrompt)
if topicName in self.topics.keys():
print "Error: topic already exists"
else:
newTopic = {}
newTopic["name"] = topicName
newTopic["notes"] = []
newTopic["button"] = self.newTopicButton(newTopic, button)
self.topics[topicName] = newTopic
self.addToTopicLists(newTopic)
Here's the code for newTopicButton():
def newTopicButton(self, topic, button=None):
""" If a Button object is passed, change its text to display the topic name.
Otherwise, create and grid a new Button with the topic name. """
if button is None:
button = Button(self.topicFrame)
index = len(self.topics)
button.grid(row=index/self.TOPICS_PER_ROW, column=(index %
self.TOPICS_PER_ROW), sticky=NSEW, padx=10, pady=10)
else:
button.unbind("<Button-1>")
buttonText = "%s\n0 notes" % topic["name"]
button.config(text=buttonText)
button.config(command=(lambda s=self, t=topic: s.addNoteToTopic(t)))
return button
And, finally, here's the code for the button command:
for col in range(self.TOPICS_PER_ROW):
button = Button(self.topicFrame, text="New Topic")
button.bind("<Button-1>", (lambda e, s=self: s.newTopic(e.widget)))
button.grid(row=0, column=col, sticky=NSEW, padx=10, pady=10)
Anybody have any idea why binding the lambda expression to the button makes askstring hang?
Edit: Thanks for the comments. Here's a minimal example that exhibits the behavior:
from Tkinter import *
import tkSimpleDialog
class Min():
def __init__(self, master=None):
root = master
frame = Frame(root)
frame.pack()
button = Button(frame, text="askstring")
button.bind("<Button-1>", (lambda e, s=self: s.newLabel()))
button.grid()
def newLabel(self):
label = tkSimpleDialog.askstring("New Label", "What should the label be?")
print label
root = Tk()
m = Min(root)
root.mainloop()
Note that switching from button.bind("<Button-1>", (lambda e, s=self: s.newLabel())) to button = Button(frame, text="askstring", command=(lambda s=self: s.newLabel())) fixes the bug (but doesn't give me a reference to the button that was pressed). I think the problem has something to do with capturing the event as one of the inputs to the lambda.
The problem you encountered here is due to the call to wait_window in the dialog you are using (you never call it yourself, but the code that implement the dialog does). For instance, the following code replicates the problem after (likely) two button clicks:
import Tkinter
def test(event=None):
tl = Tkinter.Toplevel()
tl.wait_window(tl)
root = Tkinter.Tk()
btn = Tkinter.Button(text=u'hi')
btn.bind('<Button-1>', test)
btn.pack(padx=10, pady=10)
root.mainloop()
This call to wait_window effectively does what the update command does, and is a typical example of why calling update is a bad thing to do. It enters in conflict with the <Button-1> event being handled, and hangs. The problem is that you will have to live with wait_window being used, since it belongs to the dialog's code. Apparently, if you bind to <ButtonRelease-1> then this conflict never happens. You could also use the command parameter in the button, which works fine too.
Lastly, I suggest the following to create the buttons in a cleaner manner based on what you want to achieve:
for i in range(X):
btn = Tkinter.Button(text=u'%d' % i)
btn['command'] = lambda button=btn: some_callback(button)
I figured out a workaround. From the minimum-example testing, it appears that the problem comes from making a separate call to bind and thereby accepting the event as an input to the lambda. If anyone can explain why that might be happening, I'll accept their answer over mine, but I'll accept this one for now.
The workaround is not to use a separate bind function but to create an array of buttons and then pass the correct entry in the array as the parameter to the lambda function (you can't pass the button itself, since it's being created in the line that has the lambda function).
Here's the code:
from Tkinter import *
import tkSimpleDialog
class Min():
def __init__(self, master=None):
root = master
frame = Frame(root)
frame.pack()
buttons = [None] * 2
for i in range (2):
buttons[i] = Button(frame, text="askstring",
command=(lambda s=self, var=i: s.newLabel(buttons[var])))
buttons[i].grid()
def newLabel(self, button):
label = tkSimpleDialog.askstring("New Label", "What should the label be?")
button.config(text=label)
print label
root = Tk()
m = Min(root)
root.mainloop()

How to update a Tix.ComboBox's text?

I have a Tix.ComboBox with an editable text field. How do I force the variable holding the value for the text to update?
Let me give a more concrete explanation. I have a combo box and a button. When I click the button, it pops up a message box with the value of the combo box. Let's say the combo box text field currently has the value "thing1". If I type "new" into the box and then click on the button with my mouse, it will pops up the message "thing1". If I type "new" in the box and then tab focus away from the combo box and then click the button the pop up message says "new".
Ho do I force the combo box to update it's value to new without requiring that I tab away from the combo box?
I have included sample code.
import Tix
import tkMessageBox
class App(object):
def __init__(self, window):
window.winfo_toplevel().wm_title("test")
self.window = window
self.combo = Tix.ComboBox(window)
self.combo.insert(Tix.END, 'thing1')
self.combo.insert(Tix.END, 'thing2')
self.combo.entry['state'] = "normal"
self.combo['editable'] = True
self.combo.pack()
button = Tix.Button(window)
button['text'] = "Go"
button['command'] = self.go
button.pack()
def go(self):
tkMessageBox.showinfo('info', self.combo['value'])
if __name__ == '__main__':
root = Tix.Tk()
App(root)
root.mainloop()
woo!
solved it on my own.
Use
self.combo['selection']
instead of
self.combo['value']
NOTE: copy of Moe's answer that can be selected as chosen answer
woo!
solved it on my own.
Use
self.combo['selection']
instead of
self.combo['value']

Categories