tkinter button command function not defined - python

I am trying to get the input from text box and trying to write them to a file:
I get the error as: retrieve_input is not defined.
Please help me to rectify my code:
coding:
import tkinter as tki
class App(object):
def __init__(self,root):
self.root = root
# create a Frame for the Text and Scrollbar
txt_frm = tki.Frame(self.root, width=600, height=400)
txt_frm.pack(fill="both", expand=True)
# ensure a consistent GUI size
txt_frm.grid_propagate(False)
self.txt1 = tki.Text(txt_frm, borderwidth=3, relief="sunken", height=4,width=55)
self.txt1.config(font=("consolas", 12), undo=True, wrap='word')
self.txt1.grid(row=0, column=1, sticky="nsew", padx=2, pady=2)
scrollb1 = tki.Scrollbar(txt_frm, command=self.txt1.yview)
scrollb1.grid(row=0, column=2, sticky='nsew')
self.txt1['yscrollcommand'] = scrollb1.set
button = tki.Button(self,text=u"Click command=retrieve_input)
button.grid(column=1,row=0)
def retrieve_input():
input = self.txt1.get("0.0",'END-1c')
with open('hello.txt','w') as f:
f.wite(input)
root = tki.Tk()
app = App(root)
root.mainloop()

In addition to the obvious typos, the problem is this line:
button = tki.Button(self,text="Click", command = self.retrieve_input)
Notice that the first parameter you pass to tk.Button is self. The first argument must be a widget, but you're giving it self which is not a widget. Perhaps you meant to use txt_form instead?

Related

Is there a way to refresh a tkinter frame in which widgets have been reconfigured?

I'd like to allow the user to configure the appearance of the app (background color, font, font size and color, button color, etc). I've got all of the default settings saved into a config file, which the program interprets and saves into variables within a class for easy access between functions. Now, I can save the changes the user made into the config file and those changes will be reflected when the user closes and reopens the app, but I'd like the changes to be instantaneous, so I tried something like this:
import tkinter as tk
class SetColor:
def __init__(self, color):
self.color = 'green'
current = SetColor('green')
root = tk.Tk()
lbl_color = tk.Label(root, text='Choose button color')
lbl_color.grid(row=0, column=0, pady=5, padx=2)
btn_red = tk.Button(root, text='Red', bg=current.color, command=lambda:update_color('red'))
btn_red.grid(row=0, column=1, pady=5, padx=2)
btn_green = tk.Button(root, text='Green', bg=current.color, command=lambda:update_color('green'))
btn_green.grid(row=0, column=2, pady=5, padx=2)
btn_blue = tk.Button(root, text='Blue', bg=current.color, command=lambda:update_color('blue'))
btn_blue.grid(row=0, column=3, pady=5, padx=2)
def update_color(color):
current.color = color
#THIS is where I'm wondering if there's a way to refresh without individually updating each widget as I've done below
btn_red.config(bg=current.color)
btn_green.config(bg=current.color)
btn_blue.config(bg=current.color)
root.mainloop()
This does work, but in my actual app there are a loooooot more widgets that would need updating than in this^ example. So I have a feeling I'm missing something or going about this in the wrong way. Any help greatly appreciated :)
Your best bet is to store the buttons in a list and loop over that list. This way you can seperate different buttons. But if you are sure you want to change the color of every singlle button you can do: for widget in root.winfo_children():
if isinstance(widget, tk.Button):
widget.config(bg=current.color)
#Maarten's answer is perfect for the tkinter button.
There is another option using ttk.Button can be used in such scenario
create the button object with a custom style
btn_green = ttk.Button(root, text='Green', style="color.TButton", command=lambda: update_color('green'))
create style object
style = ttk.Style()
style.theme_use("default")
set the style
style.configure('color.TButton', background=current.color)
# Activate is when you mouse over the button.
style.map('color.TButton', background=[('active', current.color)])
Full example:
import tkinter as tk
from tkinter import ttk
class SetColor:
def __init__(self, color):
self.color = 'green'
def update_color(color):
current.color = color
# Let's set the style
# naming that style variable as color.TButton
# NOTE: .TButton is important, you can add any other pretix though
style.configure('color.TButton', background=current.color)
# Activate is when you mouse over the button.
style.map('color.TButton', background=[('active', current.color)])
current = SetColor('green')
root = tk.Tk()
# Create style Object
style = ttk.Style()
# Setting theme to default (built in themes can be found https://wiki.tcl-lang.org/page/List+of+ttk+Themes)
style.theme_use("default")
lbl_color = ttk.Label(root, text='Choose button color')
lbl_color.grid(row=0, column=0, pady=5, padx=2)
btn_red = ttk.Button(root, text='Red', style="color.TButton", command=lambda: update_color('red'))
btn_red.grid(row=0, column=1, pady=5, padx=2)
btn_green = ttk.Button(root, text='Green', style="color.TButton", command=lambda: update_color('green'))
btn_green.grid(row=0, column=2, pady=5, padx=2)
btn_blue = ttk.Button(root, text='Blue', style="color.TButton", command=lambda: update_color('blue'))
btn_blue.grid(row=0, column=3, pady=5, padx=2)
update_color(current.color)
root.mainloop()
There are a lot more options with ttk style to play around.
Have a look at
Python ttk Style
ttk Themes
I did this and works perfectly. I hope it works for you.
def groups1(): # This function is to place the widgets for Groups.
# Clean widgets immediately after you call the button inside
# the button function.
for widget in frameleft.winfo_children():
widget.destroy()
groups = tkinter.ttk.Label(frameleft, text='Grupos', font=
('URW Gothic', 20))
groups.place(x=20, y=30)

How do I float tkinter buttons vertically on the left side

I want my tkinter buttons, entry and text to float in the middle vertically, on the left side. I've tried using pack() with side=left, but it just stacked them horizontally, if I could stack them vertically that would be perfect. I've tried using grid, but I can't get them into the middle. I've tried anchor w but that didn't work either. Thanks!
import tkinter as tk
class Fullscreen:
def __init__(self):
self.window = tk.Tk()
self.window.title("Example")
self.fullScreenState = True
self.window.attributes("-fullscreen", self.fullScreenState)
self.w, self.h = self.window.winfo_screenwidth(), self.window.winfo_screenheight()
self.window.geometry("%dx%d" % (self.w, self.h))
self.window.bind("<F11>", self.toggleFullScreen)
self.window.bind("<Escape>", self.quitFullScreen)
def room_button():
room_text = room_key.get()
print(room_text)
def message_button():
message_text = message.get(1.0, tk.END)
handle_text = handle.get()
print(message_text)
print(handle_text)
greeting = tk.Label(text="Welcome")
# Pack displays the Label's text in the window.
greeting.grid(column=1, sticky='w')
display_room = tk.Label(text="Room:")
display_room.grid(column=1, sticky='w')
room_key = tk.Entry()
room_key.grid(column=1, sticky='w')
send_room_key = tk.Button(text="Change Rooms", command=room_button)
send_room_key.grid(column=1, sticky='w')
display_message = tk.Label(text="Message:")
display_message.grid(column=1, sticky='w')
message = tk.Text()
message.grid(column=1, sticky='w')
display_handle = tk.Label(text="Handle (optional):")
display_handle.grid(column=1, sticky='w')
handle = tk.Entry()
handle.grid(column=1, sticky='w')
send_message = tk.Button(text="Send Message", command=message_button)
send_message.grid(column=1, sticky='w')
quit_button = tk.Button(text="Quit", command=self.window.destroy)
quit_button.grid(column=1, sticky='w')
self.window.mainloop()
def toggleFullScreen(self, event):
self.fullScreenState = not self.fullScreenState
self.window.attributes("-fullscreen", self.fullScreenState)
def quitFullScreen(self, event):
self.fullScreenState = False
self.window.attributes("-fullscreen", self.fullScreenState)
if __name__ == '__main__':
app = Fullscreen()
Is this how You want them to be:
from tkinter import Tk, Frame, Label, Entry, Button
root = Tk()
root.geometry('500x400')
widget_frame = Frame(root)
widget_frame.pack(side='left')
Label(widget_frame, text='Enter info below').pack()
entry = Entry(widget_frame)
entry.pack()
Button(widget_frame, text='Submit').pack()
root.mainloop()
You can just use a frame that will be packed to the left and pack widgets inside the frame and they will be on the left side and in the center
You can also use this:
import tkinter as tk
root = tk.Tk()
root.geometry("500x400")
label = tk.Label(root, text="Enter info below")
label.grid(row=1, column=1, pady=(150, 0))
entry = tk.Entry(root)
entry.grid(row=2, column=1)
button = tk.Button(root, text='Submit')
button.grid(row=3, column=1)
root.mainloop()
The pady=(150, 0) tell the grid method that it should leave 150 pixels above the widget empty. This will give you more control over where the widgets are placed.

Custom Buttons in Python ctypes MessageBox

I need a python messagebox with custom buttons.
I need something like this. I need it to return the button clicked. Like If I clicked 'A' it would return 'A'
I know about tkinter but I want to use this with pygame and can't get it to work.
Here is what I made with tkinter, not the perfect messagebox, but why not?
For the theme used you have to install it first, like:
pip install ttkthemes
Then the code
# imports
from tkinter import *
from tkinter import ttk
from ttkthemes import themed_tk as tktheme
from PIL import ImageTk, Image
from tkinter import messagebox
# making new themed window
root = tktheme.ThemedTk()
root.title('Take selection')
root.get_themes()
root.set_theme('vista')
# message to be shown by the box
message = 'Here is a custom messagebox.'
# defining functions
def click1():
root.destroy()
return 'A'
def click2():
root.destroy()
return 'B'
def click3():
root.destroy()
return 'C'
# creating white frame
frame1 = Frame(height=139, width=440, bg='white')
frame1.grid(row=0, column=0)
# creating gray frame
frame2 = ttk.Frame(height=50, width=440)
frame2.grid(row=1, column=0)
# importing the image, any image can be used(for the question mark)
dir = Image.open('Blue_question.png')
dir = dir.resize((50, 50), Image.ANTIALIAS)
img_prof = ImageTk.PhotoImage(dir)
img_label = Label(root, image=img_prof, bg='white')
img_label.grid(row=0, column=0, sticky=W, padx=25)
# defining main label
cust_messagebox = Label(root, text=message, font=('Arial', 10), bg='white')
cust_messagebox.grid(row=0, column=0, sticky=W, padx=(95, 0))
# defining buttons
button1 = ttk.Button(root, text='A', command=click1)
button1.grid(row=1, column=0, sticky=W, padx=30, ipadx=10)
button2 = ttk.Button(root, text='B', command=click2)
button2.grid(row=1, column=0, ipadx=10)
button3 = ttk.Button(root, text='C', command=click3)
button3.grid(row=1, column=0, sticky=E, padx=(0, 30), ipadx=10)
# keeping the app alive
root.mainloop()
With bigger messages you might want to increase the width of the frames and the play with the padding of the widgets too.
If any doubts or errors, do let me know.
Cheers

What is a good way to stop 2 labels in the same row and column from covering each other in tkinter?

When button 1 is pressed I want this program to say word 'hi' and when button 2 is pressed I want it to say 'goodbye' in the same spot as it says hi and override what button 1 produced. However, it doesn't override it just merges the 2 labels together. What is a good way to prevent this from happening while making sure both labels appear in the same spot?
from tkinter import *
root = Tk()
def press():
word = Label(root, text='hi')
word.grid(row=0, column=1)
def press_2():
word_2 = Label(root, text='goodbye')
word_2.grid(row=0, column=1)
button_1 = Button(root, text=1, command=press)
button_2 = Button(root, text=2, command=press_2)
button_1.grid(row=0, column=0)
button_2.grid(row=1, column=0)
root.mainloop()
You just need to use one Label and keep changing its text. We can do this efficiently by using a lambda for the widget command. As stated by #Bryan Oakley, using a StringVar adds unnecessary overhead. This is a slightly modified version from my original. In this version we use the config method of the widget to set the text. As a bonus the code is formatted with a class structure. Using a procedural style with tkinter eventually turns into a big mess. Considering the required imports are minimal, we import them directly and do not need to prefix every widget.
from tkinter import Tk, Button, Label
class Application(Tk):
def __init__(self, *args, **kwargs):
Tk.__init__(self, *args, **kwargs)
lbl = Label(self, text='hi')
lbl.grid(row=0, column=1)
btn1 = Button(self, text='1',)
btn1.config(command=lambda m='hi': lbl.config(text=m))
btn1.grid(row=0, column=0)
btn2 = Button(self, text='2')
btn2.config(command=lambda m='goodbye': lbl.config(text=m))
btn2.grid(row=1, column=0)
if __name__ == "__main__":
app = Application()
app.minsize(100, 50)
app.title("My Application")
app.mainloop()
from tkinter import *
root = Tk()
def press():
label['text'] = "hi"
def press_2():
label['text'] = "goodbye"
label = Label(root)
label.grid(row=0, column=1)
button_1 = Button(root, text=1, command=press)
button_2 = Button(root, text=2, command=press_2)
button_1.grid(row=0, column=0)
button_2.grid(row=1, column=0)
root.mainloop()
Put it on the windows firstly,Then change the text config.
A better way to do so would be by using config() method of widgets. But here i dont know if its gonna help out much. But give it a try
from tkinter import *
root = Tk()
def press():
word.config(text='hi')
def press_2():
word.config(text='goodbye')
button_1 = Button(root, text=1, command=press)
button_2 = Button(root, text=2, command=press_2)
button_1.grid(row=0, column=0)
button_2.grid(row=1, column=0)
word = Label(root, text='') #creating a blank label to edit later on in functions
word.grid(row=0, column=1)
root.mainloop()

StringVar().set() Not Adjusting StringVar

I'm a beginner learning Python and mucking around with the tkinter GUI stuff. I'm trying to make a very basic beginner project that allows a user to type something into a text box and click a button, whereupon that input is added to a label in another part of the window.
However, I'm running into an issue where the StringVar that I'm using as an output isn't being updated by the .set() command.
def __init__(self):
self.window = Tk()
self.window.title("Terminal Writer 9000!")
self.terminalString = StringVar()
self.terminalString.set("This is an example message.")
self.allcontent = ttk.Frame(self.window)
self.allcontent.grid(row=0, column=0, sticky="nwse")
self.mainframe = ttk.Frame(self.allcontent)
self.mainframe.grid(row=0, column=0, sticky = "nwse", columnspan=4, rowspan=5)
self.terminal = ttk.Label(self.mainframe, textvariable=self.terminalString, padding=10, relief="sunken")
self.terminal.grid(row=0, column=0, rowspan=5, columnspan=2, sticky="nwse")
# GUI setup for Buttons and Entry box omitted...
play = TerminalWriterApp()
play.window.mainloop()
However, the area used by the terminal Label is blank, even though it should display "This is an example message." While troubleshooting, I made this, which is basically a complete copy/paste of the functional elements of my original code:
from tkinter import *
from tkinter import ttk
window = Tk()
strvar = StringVar()
strvar.set("Test 2")
allcontent = ttk.Frame(window)
allcontent.grid(row=0, column=0, sticky="nwse")
mainframe = ttk.Frame(allcontent)
mainframe.grid(row=0, column=0, sticky="nwse", columnspan=4, rowspan=5)
text = Label(mainframe, text="Test 1")
text.grid(row=0, column=0, sticky="nwse")
text2 = Label(mainframe, textvariable=strvar)
text2.grid(row=1, column=0, sticky="nwse")
window.mainloop()
This code functions as intended, displaying a window with "Test 1" and "Test 2" on separate lines.
Does anyone know why the set() method wouldn't work in this context? (Also, feel free to get mad at my horrible code - I need to learn good habits somehow!)
For some reasons, the label appears when the app takes focus (when you click on it); maybe it is because of the stack nested frames, IDK.
You could use focus_force to constrain the OS to give focus to your app immediately.
from tkinter import *
from tkinter import ttk
class TerminalWriterApp:
def __init__(self):
self.window = Tk()
self.window.title("Terminal Writer 9000!")
self.terminalString = StringVar()
self.terminalString.set("This is an example message.")
self.allcontent = ttk.Frame(self.window)
self.allcontent.grid(row=0, column=0, sticky="nwse")
self.mainframe = ttk.Frame(self.allcontent)
self.mainframe.grid(row=0, column=0, sticky = "nwse", columnspan=4, rowspan=5)
self.terminal = ttk.Label(self.mainframe, textvariable=self.terminalString, padding=10, relief="sunken")
self.terminal.grid(row=0, column=0, rowspan=5, columnspan=2, sticky="nwse")
self.terminal.focus_force()
play = TerminalWriterApp()
play.window.mainloop()

Categories