Very new to Python here, and I'm trying to create a GUI app that returns a random recipe. Currently the print happens at the terminal, and I'd like it to print in the GUI instead.
from tkinter import *
import os
import random
root = tk.Tk()
def printRecipes():
recipes = [
"Tom Yum Soup",
"Carnitas",
"General Tso's Chicken"
]
print(random.choice(recipes))
canvas = tk.Canvas(root, height=600, width=700, bg="#A8D1BB")
canvas.pack()
magic = tk.Button(root, text="Print", padx=10, pady=5, fg="white", bg="black", command=printRecipes)
magic.pack()
root.mainloop()
This doesn't work, as most of you already know. I've read that I need to use a label or text for it, but the example I've found all involved static print statements like
label = Label(root,text="Recipe")
label.pack
To "print" a value to a GUI Window, a Label is used. .config() is a useful function. It is used to configure the provided widget.
Below magic.pack(), add this code. Notice that there is no text parameter. We will use that later.
label1=Label(root,pady=10,font=("arial",15))
label1.pack()
Next, in the function, where you had print(random.choice(recipes)), we will add:
label1.config(text=random.choice(recipes))
Notice that we used .config() and the text parameter. We configured the label, and added some text to it.
You need a tk.StringVar for this, i.e. a variable which you can change and the label can read from:
label_str = tk.StringVar(value="Some text")
label = tk.Label(root, textvariable=label_str)
Then, to update the value of this label and show it on the GUI:
label_str.set("New text")
label.update()
Related
I really need to be able to delete a button onscreen into a label. All I need to do is remove the button, and put the label in place of it. However, I do not know how to remove buttons.
I am running Windows 10, Python 3.9.2.
Are you looking for something like this?:
import tkinter as tk
def remove_button():
global label
# Get the grid parameters passed in button when it was created
button_grid_info = button.grid_info()
button.grid_forget()
label = tk.Label(button_grid_info["in"], text="This is a Label widget")
# Put the label exactly where the button was:
label.grid(**button_grid_info)
root = tk.Tk()
button = tk.Button(root, text="Click me", command=remove_button)
button.grid(row=1, column=1)
root.mainloop()
grid_forget removes the widget without destroying it. If you used <button>.pack, use pack_forget. If you used <button>.place, use place_forget.
I am working on an app using Tkinter and I have a small question.
For example, I have a label placed like this:
Label = Label(root, text="Hello").place(x=5, y=5)
Is there any way to hide the label when a button is pressed or when a function is called?
Any help will be appreciated
Thanks!
You should simply never chain .place() or any other geometry manager to the widget creator. It returns None.
lbl_name = Label(root, text="Hello")
lbl_name.place(x=5, y=5)
Now you can handle lbl_name as a label object. To hide it you can use:
lbl_name.place_forget()
Unfortunately, now its gone. Therefore, first save the place properties:
lbl_name = Label(root, text="Hello")
lbl_name.place(x=5, y=5)
lbl_name.saved = lbl_name.place_info()
You can now show it again with:
lbl_name.place(lbl_name.saved)
Note: You can print lbl_name.saved. It is a dictionary with all place-properties of the lbl_name Label object.
I'm trying to create new entry boxes when the "ADD entry" is used for my program. I do realize that the 'pack codes' works when I run it individually, but when I combine with existing program which is in grid(), the window is not showing when I run my program.
I also understand that we should not to use both .pack() when I have other things using .grid() in the same program. Hence, my question is, how do I create new entry boxes in grid.
I have tried finding elsewhere but they all suggested pack. For instance: I have looked here here, etc etc, to name a few; but couldn't find anything similar to mine. I would like to add entry boxes below the current entry boxes which is at row 3.
I'm kind of new to Python; (am using Python 2.7 and tkinter module in this program. Thank you very much for the help!
My simplified error codes are as follows:
from Tkinter import *
import tkFileDialog
import tkMessageBox
import Tkinter
import os
class Window:
def __init__(self, master):
self.filename3=""
csvfile=Label(root, text="NUMBERS").grid(row=3, column=0)
bar=Entry(master).grid(row=3, column=3)
self.entryText3 = StringVar()
self.bar = Entry(root, textvariable=self.entryText3).grid(row=3, column=3)
#BUTTONS
self.cbutton= Button(root, text="OK", command=self.process_csv)
self.cbutton.grid(row=15, column=6, sticky = W + E)
####PROBLEM FROM HERE#####
all_entries = []
addboxButton = Button(root, text='ADD', fg="BLUE", command=self.addBox)
addboxButton.pack()
#I have also tried replacing the last 2 lines with the following 2 lines instead but to no avail:
#self.addboxButton = Button(root, text='ADD THA-ID', fg="BLUE", command=self.addBox)
#self.addboxButton.grid(row=3, column=6)
frame_for_boxes = Frame(root)
frame_for_boxes.pack()
def addBox(self):
print "ADD"
next_row = len(all_entries)
lab = Label(frame_for_boxes, text=str(next_row+1))
lab.grid(row=next_row, column=0)
ent = Entry(frame_for_boxes)
ent.grid(row=next_row, column=0)
all_entries.append( ent )
def process_csv(self):
#own program here
print "program"
root = Tk()
window=Window(root)
root.mainloop()
There are few issues with your program other than the one you stated:
Inside the initializer (__init__()) you attached the widgets to root which is not defined within the scope of your Window class. The reasonable way to fix this issue is when you use an instance of Tk(), id est root id est master in Window class, make it as an instance variable. This means the first thing you have to do in the inititializer is this : self.master = master. This will result in you having to replace all root occurrences within __init__() by self.master
The second issue to fix is the one you specified in your question's title: you can not mix the grid() and pack() layout managers for the same widget container. You have to decide which one. Since you placed most of the widgets using grid(), I suggest you to get rid of pack(). This means:
addboxButton.pack() becomes, for example, addboxButton.grid(row=0, column=1)
frame_for_boxes.pack() becomes, for example, frame_for_boxes.grid(row=0, column=0)
The previous list item fixes the problem but it will make you discover other issues within your program which are related:
NameError: global name 'all_entries' is not defined
NameError: global name 'frame_for_boxes' is not defined
This is because those widget variables are not reachable within the scope of addBox() function. To resolve this issue, you have to make those 2 elements as instance variables. This means:
all_entries = [] becomes self.all_entries = []
frame_for_boxes = Frame(root) becomes self.frame_for_boxes = Frame(self.master) (remember we replaced root by self.master in 1.)
The consequence of this error fixing is that you have to use all over inside your program:
self.all_entries instead all_entries
self.frame_for_boxes instead of frame_for_boxes
For scalability reasons, I think you will have at least to make the rest of widgets as instance variables (i.d. prefix them with the self keyword)
As your real project is more complicated than what you show in this MCVE, I encourage you to adopt the SaYa idiom when creating and placing widget elements. This means you will need to replace:
csvfile=Label(root, text="NUMBERS").grid(row=3, column=0)
by
self.csvfile = Label(self.master, text="NUMBERS")
self.csvfile.grid(row=3, column=0)
To avoid unexpected bugs in your program, you must do the same for the remaining widgets you declared in the inititialzer.
There are also other things I would like to mention, but most of them are available on PEP8
What you have to do it to create a command which creates the entries and stores the new entries inside of a variable.
In my case, I use Entry_i and store in Entries but you can use self.Entries to make communication easier. (python 3.5)
def Make_Entry(self, root, Entries, x, y):
Entry_i = Entry(root, bd = 5)
Entry_i.grid(row = x, column = y, sticky = W+E+N+S)
Entries.append(Entry_i)
return(Entries, x+1, y+1)
I am fairly new to TkInter and GUI in python (but I have experience with python in general). I was working on a GUI in TkInter and want to have users enter their name and have TkInter display there name when they click the button. Here is my code so far:
from Tkinter import *
master = Tk()
e = Entry(master)
e.pack()
e.focus_set()
def callback():
print e.get()
b = Button(master, text="get", width=10, command=callback)
b.pack()
separator = Frame(height=2, bd=1, relief=SUNKEN)
separator.pack(fill=X, padx=5, pady=5)
Label(text=callback).pack()
mainloop()
Users will enter their name in the Entry (or e) and I want to display e in the label widget. Any ideas on how I can do this? Thanks.
At the top (below the imports), define name as a StringVar:
name = StringVar()
In the function callback change the content to be:
def callback():
name.set(e.get())
And finally, change your Label widget to:
Label(master, textvariable=name)
So what I have done is created a special object with which when we change its value, the value of all references to it will change also. We can then set our function to change the value to update it globally- and we finish by utilizing this capability by putting this variable as the text attribute in our Label.
Note: I also added the parent argument to your Label. Without it, it wouldn't show up at all.
I want to make popup window using Tkinter.
I can do it so:
import Tkinter
a="some data that use should be able to copy-paste"
tkMessageBox.showwarning("done","message")
But there is one problem that user need to be able to select, copy and paste shown text.
It's not possible to do in such way.
Are there any ways to do it with Tkinter? (or another tools that is supplied with python by default)
Thanks in advance for any tips
From here, it seems a workaround using Entry in Tkinter is doable. Here is the code:
import Tkinter as Tk
root = Tk.Tk()
ent = Tk.Entry(root, state='readonly')
var = Tk.StringVar()
var.set('Some text')
ent.config(textvariable=var, relief='flat')
ent.pack()
root.mainloop()
EDIT: To respond to your comment, I found a way to insert multi-line text, using the Text widget.
Here is a draft of a solution:
from Tkinter import *
root = Tk()
T = Text(root, height=2, width=30, bg='lightgrey', relief='flat')
T.insert(END, "Just a text Widget\nin two lines\n")
T.config(state=DISABLED) # forbid text edition
T.pack()
mainloop()
I'm (still) interested in any better solution :)
You can use buttons for copy and paste. First you need to select. In a text widget it is easily done by
selection=nameoftextwidget.get(SEL_FIRST,SEL_LAST)
Then you can use this for copying easily by the use of selection. If you want to copy/paste it in that same text widget, you can use:
nameoftextwidget.insert(END,"\n"+selection)