Filling frame with color - python

I'm trying to fill the frame with the buttons so it extends the background-color to the end of the window both sides, this worked when doing .pack() but not .grid().
My code:
from tkinter import *
class App:
def __init__(self):
self.root = Tk()
self.width = 800
self.height = 400
self.root.geometry("{}x{}".format(self.width, self.height))
self.root.resizable(False, False)
self.menu_bar()
self.tool_bar()
self.name = Label(self.root, text="Tester", bg="black", fg="white")
self.root.mainloop()
def menu_bar(self):
self.menu = Menu(self.root)
self.root.config(menu=self.menu)
self.subMenu = Menu(self.menu)
self.menu.add_cascade(label="File", menu=self.subMenu)
self.subMenu.add_command(label="New Project...")
self.subMenu.add_command(label="Properties")
self.subMenu.add_separator()
self.subMenu.add_command(label="Do nothing")
def tool_bar(self):
self.toolbar = Frame(self.root, bg="#555555")
self.insert_button = Button(self.toolbar, text="Insert", bg="#555555", fg="white", activeforeground="white",
activebackground="#008CBA", borderwidth=0)
self.insert_button.grid(row=0, column=0)
self.print_buttom = Button(self.toolbar, text="Print", bg="#555555", fg="white", activeforeground="white",
activebackground="#008CBA", borderwidth=0,
command=self.root.quit)
self.print_buttom.grid(row=0, column=1)
self.toolbar.grid(row=0, column=0, sticky=EW)
if __name__ == '__main__':
App()
Thanks for answers in advance.

You can set a weight to your column.
class App:
def __init__(self):
self.root = Tk()
...
self.root.columnconfigure(0,weight=1)
...

Related

creating new window from inside a class is merging content to the parent window in tkinter python

I am trying to create a new window from within a window in Tkinter but all my new content is added to the parent window, not in the new window.
the first window
the second window
Here is my code:
from tkinter import *
from tkinter import font
import tkinter as tk
NORMAL_PADDING = 3
class AllPages(tk.Frame):
def __init__(self, master):
super().__init__(master)
self.pack()
self.font = font.Font(family='JetBrains Mono', name='jbm', size=12, weight='normal')
self.posts = [
{
"title": "huhu"
}
]
self.posts_container = tk.Frame()
for post in self.posts:
postf = tk.Frame(self.posts_container, bg="#ffffff", borderwidth=1, relief="solid")
post_t = tk.Label(postf, text=post["title"], bg="#ffffff", borderwidth=0, font=self.font)
post_edit = tk.Button(postf, text="Edit", bg="#ffffff", borderwidth=0, relief="solid", highlightbackground="#ffffff", highlightcolor="#ffffff", highlightthickness=1, font=self.font, command= lambda: self.modify_post_form(post))
post_t.pack(side="left")
post_edit.pack(side="right")
postf.pack(anchor=W, fill='x', pady=NORMAL_PADDING)
self.posts_container.pack(fill='both', ipadx=NORMAL_PADDING, padx=NORMAL_PADDING)
def modify_post_form(self, post):
newW = Toplevel()
app = CreatePostForm(newW)
class CreatePostForm(tk.Frame):
def __init__(self, master, post=None):
super().__init__(master)
self.pack()
if not ("jbm" in font.names()):
self.font = font.Font(family='JetBrains Mono', name='jbm', size=12, weight='normal')
else:
self.font = font.nametofont("jbm")
self.label_width = 20
self.input_width = 40
self.title_container = tk.Frame()
self.title_label = tk.Label(self.title_container, text="Title:", font=self.font, width=self.label_width, anchor=W)
self.title_input = tk.Entry(self.title_container, width=self.input_width, borderwidth=1, relief="solid", font=self.font)
self.title_label.pack(side="left")
self.title_input.pack(side="right")
self.title_container.pack(anchor=W, pady=NORMAL_PADDING)
self.title = tk.StringVar()
self.title_input["textvariable"] = self.title
root = tk.Tk()
myapp = AllPages(root)
myapp.pack()
myapp.mainloop()
The first window is created by the AllPages() class. then AllPages().modify_post_form() creates a new window using Toplevel() and pass it to CreatePostForm() class. CreatePostForm() should then populates the new window with some new content but whatever CreatePostForm() creates is going to the parent window, not to the new window created using Toplevel().
How can I fix this?
Thank you.
The problem in the above code is that both classes didn't specify the window it should create content into. Here is the working code:
from tkinter import *
from tkinter import font
import tkinter as tk
NORMAL_PADDING = 3
class AllPages(tk.Frame):
def __init__(self, master):
super().__init__(master)
self.pack()
self.font = font.Font(family='JetBrains Mono', name='jbm', size=12, weight='normal')
self.posts = [
{
"title": "huhu"
}
]
self.posts_container = tk.Frame(self)
for post in self.posts:
postf = tk.Frame(self.posts_container, bg="#ffffff", borderwidth=1, relief="solid")
post_t = tk.Label(postf, text=post["title"], bg="#ffffff", borderwidth=0, font=self.font)
post_edit = tk.Button(postf, text="Edit", bg="#ffffff", borderwidth=0, relief="solid", highlightbackground="#ffffff", highlightcolor="#ffffff", highlightthickness=1, font=self.font, command= lambda: self.modify_post_form(post))
post_t.pack(side="left")
post_edit.pack(side="right")
postf.pack(anchor=W, fill='x', pady=NORMAL_PADDING)
self.posts_container.pack(fill='both', ipadx=NORMAL_PADDING, padx=NORMAL_PADDING)
def modify_post_form(self, post):
newW = Toplevel()
app = CreatePostForm(newW)
class CreatePostForm(tk.Frame):
def __init__(self, master, post=None):
super().__init__(master)
self.pack()
if not ("jbm" in font.names()):
self.font = font.Font(family='JetBrains Mono', name='jbm', size=12, weight='normal')
else:
self.font = font.nametofont("jbm")
self.label_width = 20
self.input_width = 40
self.title_container = tk.Frame(self)
self.title_label = tk.Label(self.title_container, text="Title:", font=self.font, width=self.label_width, anchor=W)
self.title_input = tk.Entry(self.title_container, width=self.input_width, borderwidth=1, relief="solid", font=self.font)
self.title_label.pack(side="left")
self.title_input.pack(side="right")
self.title_container.pack(anchor=W, pady=NORMAL_PADDING)
self.title = tk.StringVar()
self.title_input["textvariable"] = self.title
root = tk.Tk()
myapp = AllPages(root)
myapp.pack()
myapp.mainloop()
Thank you to all the people who commented and helped me.

How to use 2nd Tkinter window to change image in 1st?

Quick summary: The green button is supposed to change when an image is selected, but it doesn't:
I have a Tkinter window-A with a button that when pressed will use a separate Python file to create a new window-B. Window-B has two buttons: new screen-snip or select image from folder. The method used for this is supposed to then change self.image_selected so that it can be used to update the button in window-A to have this new image.
I've tried both lines of code below, and neither are working. I'm not getting any errors either:
self.button.configure(image = img.image_selected) # first try
self.button['image'] = img.image_selected # second try
Here is my code (simplified for clarity):
import tkinter as tk
import get_image_or_snip
class ButtonImg:
def __init__(self, master):
self.newWindow = None
self.master = master
self.title = "My Minimum Reproducible Example"
self.fontA = ("arial", 20, "bold")
self.canvas = tk.Canvas(height = 5)
self.canvas.pack()
self.button = tk.Button(bg="#93d9cc", height = 5, text = "Select Image",
font = self.fontA, command = self.changeImage)
self.button.pack(fill="both")
def changeImage(self):
self.newWindow = tk.Toplevel(self.master)
img = get_image_or_snip.AcquireImage(self.newWindow)
self.button.configure(image = img.image_selected)
#self.button['image'] = img.image_selected
root = tk.Tk()
app = ButtonImg(root)
root.mainloop()
Here is the aforementioned get_image_or_snip.py code:
import tkinter as tk
from tkinter import filedialog
from PIL import ImageTk, Image
import snipping_tool
class AcquireImage:
def __init__(self, master):
self.master = master
self.fontA = ("arial", 20, "bold")
self.frame = tk.Frame(master, bg="#1B2631")
self.frame.pack(fill="both", expand=True)
self.button1 = tk.Button(self.frame, text="Select Image File", padx=10, pady=10, bg="#d9a193",
font = self.fontA, command =lambda: self.show_dialogs(1))
self.button1.grid(row=0, column=0, sticky="nsew")
self.button2 = tk.Button(self.frame, text="Get Screen Snip", padx=10, pady=10, bg="#d9a193",
font = self.fontA, command=lambda: self.show_dialogs(2))
self.button2.grid(row=0, column=1, sticky="nsew")
self.image_selected = None
self.master.mainloop()
def show_dialogs(self, method):
if method == 1:
ret = filedialog.askopenfilename()
load_img = Image.open(ret)
self.image_selected = ImageTk.PhotoImage(load_img)
if method == 2:
ret = snipping_tool.get_snip()
self.image_selected = ret
def main():
root = tk.Tk()
AcquireImage(root)
root.mainloop()
if __name__ == '__main__':
main()
If you add print() before and after get_image_or_snip.AcquireImage(self.newWindow) in changeImage() then you should see only first text because you run second mainloop() and it never ends this loop and never go back to changeImage() and never runs self.button.configure(image=img.image_selected)
You should use only one mainloop() and eventually use
root.wait_window(self.newWindow)
to wait until you close second window and then it will run self.button.configure(image=img.image_selected)
But there are other problems.
It removes image from memory memory when second windows is destroyed so you have to assign it to variable in first window.
Button use height in chars when you sent text but when you assign image then it use height in pixels and you have to change it from 5 to image.height()`
All code in one file
import tkinter as tk
from tkinter import filedialog
from PIL import ImageTk
class AcquireImage:
def __init__(self, master):
self.master = master
self.fontA = ("arial", 20, "bold")
self.frame = tk.Frame(master, bg="#1B2631")
self.frame.pack(fill="both", expand=True)
self.button1 = tk.Button(self.frame, text="Select Image File", padx=10, pady=10, bg="#d9a193",
font=self.fontA, command=lambda:self.show_dialogs(1))
self.button1.grid(row=0, column=0, sticky="nsew")
self.button2 = tk.Button(self.frame, text="Get Screen Snip", padx=10, pady=10, bg="#d9a193",
font=self.fontA, command=lambda:self.show_dialogs(2))
self.button2.grid(row=0, column=1, sticky="nsew")
self.image_selected = None
def show_dialogs(self, method):
if method == 1:
ret = filedialog.askopenfilename(initialdir='/home/user/images/')
if ret:
self.image_selected = ImageTk.PhotoImage(file=ret)
self.master.destroy()
elif method == 2:
self.image_selected = snipping_tool.get_snip()
class ButtonImg:
def __init__(self, master):
self.newWindow = None
self.master = master
self.title = "My Minimum Reproducible Example"
self.fontA = ("arial", 20, "bold")
self.canvas = tk.Canvas(height=5)
self.canvas.pack()
self.button = tk.Button(bg="#93d9cc", height=5, text="Select Image",
font=self.fontA, command=self.changeImage)
self.button.pack(fill="both")
def changeImage(self):
print('open second window')
self.newWindow = tk.Toplevel(self.master)
img = AcquireImage(self.newWindow)
self.master.wait_window(self.newWindow)
print('close second window')
if img.image_selected: # check if image was selected
self.image = img.image_selected
self.button.configure(image=self.image, height=self.image.height())
root = tk.Tk()
app = ButtonImg(root)
root.mainloop()
BTW: If you want to change image without closing second window then you should send first window (or button from first window) as argument to second window and change image in show_dialogs()
import tkinter as tk
from tkinter import filedialog
from PIL import ImageTk
class AcquireImage:
def __init__(self, master, first_window):
self.master = master
self.first_window = first_window
self.fontA = ("arial", 20, "bold")
self.frame = tk.Frame(master, bg="#1B2631")
self.frame.pack(fill="both", expand=True)
self.button1 = tk.Button(self.frame, text="Select Image File", padx=10, pady=10, bg="#d9a193",
font=self.fontA, command=lambda:self.show_dialogs(1))
self.button1.grid(row=0, column=0, sticky="nsew")
self.button2 = tk.Button(self.frame, text="Get Screen Snip", padx=10, pady=10, bg="#d9a193",
font=self.fontA, command=lambda:self.show_dialogs(2))
self.button2.grid(row=0, column=1, sticky="nsew")
self.image_selected = None
def show_dialogs(self, method):
if method == 1:
ret = filedialog.askopenfilename(initialdir='/home/user/images/')
if ret:
self.image_selected = ImageTk.PhotoImage(file=ret)
self.first_window.image = self.image_selected
self.first_window.button.configure(image=self.first_window.image, height=self.first_window.image.height())
elif method == 2:
self.image_selected = snipping_tool.get_snip()
class ButtonImg:
def __init__(self, master):
self.newWindow = None
self.master = master
self.title = "My Minimum Reproducible Example"
self.fontA = ("arial", 20, "bold")
self.canvas = tk.Canvas(height=5)
self.canvas.pack()
self.button = tk.Button(bg="#93d9cc", height=5, text="Select Image",
font=self.fontA, command=self.changeImage)
self.button.pack(fill="both")
def changeImage(self):
self.newWindow = tk.Toplevel(self.master)
img = AcquireImage(self.newWindow, self) # first window as `self` but you can send `self.button`
root = tk.Tk()
app = ButtonImg(root)
root.mainloop()
EDIT: Doc about creating Dialog Windows

How to return the value from Entry,at the same time, destroy widow?

My English is not good。
I want to use a button to return value from Entry,tkinter,at the same time ,destroy the root.
Like Vba function input.
My code:
import tkinter as tk
class InputBox:
def __init__(self, title, default):
self.HEIGHT = 150
self.WIDTH = 300
self.root = tk.Tk()
self.root.width = self.WIDTH
self.root.height = self.HEIGHT
self.canvas = tk.Canvas(self.root, height=self.HEIGHT, width=self.WIDTH)
self.canvas.pack()
self.label = tk.Label(self.root, text=title)
self.label.config(font=('helvetica', 20))
self.label.place(relx=0.5, rely=0.1, relwidth=0.4, relheight=0.2, anchor="center")
self.content=tk.StringVar()
self.content.set(default)
self.entry = tk.Entry(self.root, font=('helvetica', 20), justify='center', textvariable=self.content)
self.entry.place(relx=0.5, rely=0.4, relwidth=0.8, relheight=0.3, anchor="center")
self.entry.focus_set()
# Here is the button call to the getInfo() function
self.buttonInputBox = tk.Button(self.root, text="Yes", bg='#cccccc', font=60,
command=lambda: self.getInfo())
self.buttonInputBox.place(relx=0.5, rely=0.8, relwidth=0.3, relheight=0.3, anchor="center")
self.root.mainloop()
def getInfo(self):
return self.entry.get()
self.root.destroy()
def main():
print(InputBox('RP', 'haha'))
if __name__ == '__main__':
main()

Tkinter using append to print to a label

I have been trying to return the fuction printLabel to print "Hello
world!", but I am not too sure how to progress further:
I would like to use lambda in order to print my append strings in the label when the button is clicked but this displays without the button being clicked. My
code is as follows:
from tkinter import *
class Example(Frame):
def printLabel(self):
self.hello = []
self.hello.append('Hello\n')
self.hello.append('World!')
print(self.hello)
return(self.hello)
def __init__(self, root):
Frame.__init__(self, root)
self.buttonA()
self.viewingPanel()
def buttonA(self):
self.firstPage = Button(self, text="Print Text", bd=1, anchor=CENTER, height = 13, width = 13, command=lambda: self.printLabel())
self.firstPage.place(x=0, y=0)
def viewingPanel(self):
self.panelA = Label(self, bg='white', width=65, height=13, padx=3, pady=3, anchor=CENTER, text="{}".format(self.printLabel()))
self.panelA.place(x=100, y=0)
def main():
root = Tk()
root.title("Tk")
root.geometry('565x205')
app = Example(root)
app.pack(expand=True, fill=BOTH)
root.mainloop()
if __name__ == '__main__':
main()
I made a few modifications to your code, and it should work the way you want:
from tkinter import *
class Example(Frame):
def printLabel(self):
self.hello.append('Hello\n')
self.hello.append('World!')
return(self.hello)
# Added 'updatePanel' method which updates the label in every button press.
def updatePanel(self):
self.panelA.config(text=str(self.printLabel()))
# Added 'hello' list and 'panelA' label in the constructor.
def __init__(self, root):
self.hello = []
self.panelA = None
Frame.__init__(self, root)
self.buttonA()
self.viewingPanel()
# Changed the method to be executed on button press to 'self.updatePanel()'.
def buttonA(self):
self.firstPage = Button(self, text="Print Text", bd=1, anchor=CENTER, height = 13, width = 13, command=lambda: self.updatePanel())
self.firstPage.place(x=0, y=0)
# Changed text string to be empty.
def viewingPanel(self):
self.panelA = Label(self, bg='white', width=65, height=13, padx=3, pady=3, anchor=CENTER, text="")
self.panelA.place(x=100, y=0)
def main():
root = Tk()
root.title("Tk")
root.geometry('565x205')
app = Example(root)
app.pack(expand=True, fill=BOTH)
root.mainloop()
if __name__ == '__main__':
main()

Why am I getting a blank Tkinter box when typing it this code in Python?

Most of this code was provided by my professor, and I have to make changes to his format. However, his original code is not even providing ANYTHING in the tk box when I run the program. I made some changes, but nothing is quite working. Any advice??
This is my main code:
from Tkinter import *
import todo
master = Tk()
class todoApp():
def __init__(self, master):
self.master = master
self.frame = Frame(master)
self.frame.grid()
self.todo = todo.todoList()
self.saveButton = Button(self.frame, text="Save", command=self.save)
self.saveButton.grid()
self.restoreButton = Button(self.frame, text="Restore", command = self.restore)
self.restoreButton.grid(row=1, column=1)
self.addButton = Button(self.frame, text="Add", command=self.add)
self.addButton.grid(row=0,column=2)
self.doneButton = Button(self.frame, text="Done", command=self.done)
self.doneButton.grid(row=0,column=3)
self.button = Button(self.frame, text="Quit", command=self.quit)
self.button.grid(row=0, column=4)
label = Label(self.frame, text="New Task: ")
label.grid()
self.entry = Entry(self.frame)
self.entry.grid(row=1, column=1, columnspan=4)
frame1 = LabelFrame(self.frame, text="Tasks")
frame1.grid(columnspan=5, sticky=E+W)
frame1.columnconfigure(0,weight=1)
self.tasks = Listbox(frame1)
self.tasks.grid(sticky=E+W)
frame2 = LabelFrame(self.frame, text="Completed")
frame2.grid(columnspan=5, sticky=E+W)
frame2.columnconfigure(0,weight=1)
self.completed = Listbox(frame2)
self.completed.grid(sticky=E+W)
def save(self):
self.todo.saveList("tasks.txt")
def restore(self):
self.todo.restoreList("tasks.txt")
items = self.todo.getTasks()
self.tasks.delete(0,END)
for item in [ "study", "grocery shop", "cram for finals!", "sleep", "build a gingerbread house" ]:
self.tasks.insert(END,item)
items = self.todo.getCompleted()
self.completed.delete(0,END)
for item in items:
self.completed.insert(END,item)
def add(self):
task = self.entry.get()
self.todo.addTask(task)
self.tasks.insert(END,task)
def done(self):
selection = self.tasks.curselection()
if len(selection) == 0:
return
task = self.tasks.get(selection[0])
self.todo.completeTask(task)
self.tasks.delete(selction[0])
self.completed.insert(END,task)
def quit(self):
self.frame.quit()
self.master.destroy()
master.mainloop()
The todo import is:
class todoList:
def __init__(self):
self.todo = []
self.done = []
def addTask(self,task):
self.todo.append(task)
def completeTask(self,task):
if self.todo.count(task) > 0:
self.todo.remove(task)
self.done.append(task)
def getTasks(self):
return self.todo
def getCompleted(self):
return self.done
You havent created an instance of the class todoApp,
add this line here:
def quit(self):
self.frame.quit()
self.master.destroy()
a = todoApp(master)
master.mainloop()
You do need to mess around with the positioning though, when I ran it, it looked a bit messy.
edit:
is this exactly what you're looking for?
from Tkinter import *
import todo
master = Tk()
class todoApp():
def __init__(self, master):
self.master = master
self.frame = Frame(master)
self.frame.grid()
self.todo = todo.todoList()
self.buttonframe = LabelFrame(self.frame, text='controls')
self.buttonframe.grid(row=0, column=0, columnspan=2)
self.saveButton = Button(self.buttonframe, text="Save", command=self.save)
self.saveButton.grid()
self.restoreButton = Button(self.buttonframe, text="Restore", command = self.restore)
self.restoreButton.grid(row=0, column=1)
self.addButton = Button(self.buttonframe, text="Add", command=self.add)
self.addButton.grid(row=0,column=2)
self.doneButton = Button(self.buttonframe, text="Done", command=self.done)
self.doneButton.grid(row=0,column=3)
self.button = Button(self.buttonframe, text="Quit", command=self.quit)
self.button.grid(row=0, column=4)
label = Label(self.frame, text="New Task: ")
label.grid(row=1, column=0)
self.entry = Entry(self.frame)
self.entry.grid(row=1, column=1)
frame1 = LabelFrame(self.frame, text="Tasks")
frame1.grid(columnspan=5, sticky=E+W)
frame1.columnconfigure(0,weight=1)
self.tasks = Listbox(frame1)
self.tasks.grid(sticky=E+W)
frame2 = LabelFrame(self.frame, text="Completed")
frame2.grid(columnspan=5, sticky=E+W)
frame2.columnconfigure(0,weight=1)
self.completed = Listbox(frame2)
self.completed.grid(sticky=E+W)
def save(self):
self.todo.saveList("tasks.txt")
def restore(self):
self.todo.restoreList("tasks.txt")
items = self.todo.getTasks()
self.tasks.delete(0,END)
for item in [ "study", "grocery shop", "cram for finals!", "sleep", "build a gingerbread house" ]:
self.tasks.insert(END,item)
items = self.todo.getCompleted()
self.completed.delete(0,END)
for item in items:
self.completed.insert(END,item)
def add(self):
task = self.entry.get()
self.todo.addTask(task)
self.tasks.insert(END,task)
def done(self):
selection = self.tasks.curselection()
if len(selection) == 0:
return
task = self.tasks.get(selection[0])
self.todo.completeTask(task)
self.tasks.delete(selection[0])
self.completed.insert(END,task)
def quit(self):
self.frame.quit()
self.master.destroy()
a = todoApp(master)
master.mainloop()
It seems to me like you haven't created an instance of your class.
In the bottom of your file, add this:
myApp = todoApp(master)
master.mainloop()
It could be that they must be in reverse order (I'm at work so I can't try it out right now).

Categories