looking through blogs, tutorials and such, I have not been able to understand why the following code will not display a Label in a Frame. I can perform this task well when I do not define a class, but I want to create this application to be able to create Frames and other widgets more dynamically. Here is the code. init works fine and displays a Frame in the root window appropriately. the code runs with out any errors. I have added print statements and print(type()) statements through out the add_heading function, but still no label gets displayed. Hopefully someone can tell me what I am doing wrong. Thanks in advance
#!/usr/bin/python3
from tkinter import *
class CompFrame(Tk):
def __init__(self, parent, rows, columns, title):
Frame.__init__(self, parent)
self.root = parent
f_w = screen_width/3
f_h = screen_height * .90
self = LabelFrame(root, text=title, width=f_w, height=f_h, bg="light grey")
self.grid_columnconfigure(columns, weight=1)
self.grid(row=rows, column=columns)
self.grid_propagate(False)
def add_heading(self, title):
label_width=12
ftitle = Label(self, text=title)
ftitle.configure(font='Helvetica 24 bold', width=label_width)
ftitle.place(x=".5i", y=".2i")
root = Tk()
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
root.geometry("%dx%d%d%d" % (screen_width,screen_height,0,0))
Calibrator = PhotoImage(file="image1.png")
Feeeder = PhotoImage(file="image2.PNG")
frame1 = CompFrame(root, 0, 0, "Component 1")
frame1.add_heading("NATURAL")
root.mainloop()
The self = LabelFrame(root ... part is guilty, maybe try something like this to keep control on the labelframe :
from tkinter import *
class CompFrame(Frame): # Edit with Bryan Oakley comment
def __init__(self, parent, rows, columns, title):
Frame.__init__(self, parent)
self.root = parent
f_w = screen_width/3
f_h = screen_height * .90
self.labelframe = LabelFrame(root, text=title, width=f_w, height=f_h, bg="light grey")
self.labelframe.grid_columnconfigure(columns, weight=1)
self.labelframe.grid(row=rows, column=columns)
self.labelframe.grid_propagate(False)
def add_heading(self, title):
label_width=12
ftitle = Label(self.labelframe, text=title)
ftitle.configure(font='Helvetica 24 bold', width=label_width)
ftitle.place(x=5, y=2)
Related
Segmentation fault: 11 - not sure what it means, why it has happened. I thought it was an issue with Python on my machine by all other files run fine. I have, of course, tried restarting and re-installing Python but didn't help.
I'm just trying to implement frame switching via a menu bar with tkinter.
Any help greatly appreciated.
# import tkinter modules
from tkinter import *
from tkinter import ttk
import tkinter.font as tkFont
from PIL import ImageTk, Image
from tkcalendar import *
# import modules for restart functionality
import os
import sys
import time
# define self
class tkinterApp(Tk):
def __init__(self,*args, **kwargs):
Tk.__init__(self, *args, **kwargs)
# creating a container
container = Frame(self)
container.pack(side = "top", fill = "both", expand = True)
container.grid_rowconfigure(0, weight = 1)
container.grid_columnconfigure(0, weight = 1)
# initialising frames to an empty array
self.frames = {}
menu_bar = Menu(container)
menu_bar.add_cascade(label="Main Menu", menu=menu_bar)
menu_bar.add_command(label="Welcome page", command=lambda: self.show_frame(welcome_frame))
menu_bar.add_command(label="Book a vehicle", command=lambda: self.show_frame(booking_frame))
menu_bar.add_command(label="Register as new user", command=lambda: self.show_frame(register_frame))
Tk.config(self, menu=menu_bar)
for F in (welcome_frame, register_frame, booking_frame):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row = 0, column = 0, sticky = "nsew")
self.show_frame(welcome_frame)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class welcome_frame(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
# welcome_frame = Frame(self, width=1000, height=800)
# welcome_frame.grid()
welcome = Label(welcome_frame, text="Hello, please use the menu above to navigate the interface")
welcome.grid(row=0, column=4, padx=10, pady=10)
class register_frame(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
welcome = Label(self, text="New user - enter your details below to use the Collyer's car park.")
welcome.grid(row=0, column=4, padx=10, pady=10)
class booking_frame(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
app = tkinterApp()
app.geometry("1000x800")
app.mainloop()
You are trying to make a cascade menu where the cascaded menu is the same menu:
menu_bar.add_cascade(label="Main Menu", menu=menu_bar)
The menu option needs to point to a new menu menu.
main_menu = Menu(menu_bar)
menu_bar.add_cascade(label="Main Menu", menu=main_menu)
I'm guessing you also want to put the menu commands on that menu, too
main_menu.add_command(label="Book a vehicle", command=lambda: self.show_frame(booking_frame))
main_menu.add_command(label="Register as new user", command=lambda: self.show_frame(register_frame))
Unrelated to the question, this code is also wrong:
welcome = Label(welcome_frame, text="Hello, please use the menu above to navigate the interface")
You are trying to use a class as the parent/master of the Label widget. You can't do that. The first parameter needs to be a widget. In this case, it should be self.
You also need to make sure that show_frame is indented the same as the __init__ method of the tkinterApp class.
I am a newbie in Python, I am keen in being able to swap between different frame in Tkinter. But I can't seems to be able to do it with Canvas. Any expert able to help me point out my mistake?
My main goal is to swap effectively between StartPage and PageOne.
import tkinter as tk
class backbone(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self,*args, **kwargs)
container=tk.Frame(self)
container.config(width=600,height=400,bg="beige")
container.pack()
self.frames={}
for F in (StartPage, PageOne):
frame=F(container,self)
self.frames[F]=frame
self.show_frame(StartPage)
def show_frame(self,cont):
frame=self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self,parent,controller):
tk.Frame.__init__(self,parent)
canvas = tk.Canvas(self, width = 600, height = 400, bg='#aaaaff')
canvas.pack()
btn_2=tk.Button(self, text="Start Page", bg='#3ab54a',fg='blue',
command=lambda: controller.show_frame(PageOne))
btn_2.place(relx=0.35, rely=0.79, relwidth=0.3, relheight=0.1)
btn_2.pack()
class PageOne(tk.Frame):
def __init__(self,parent,controller):
tk.Canvas.__init__(self,parent)
canvas = tk.Canvas(self, width = 600, height = 400, bg='#aaaaff')
canvas.pack()
btn_1=tk.Button(self, text="PageOne", bg='#3ab54a',fg='blue',
command=lambda: controller.show_frame(StartPage))
btn_1.place(relx=0.35, rely=0.79, relwidth=0.3, relheight=0.1)
btn_1.pack()
app=backbone()
app.mainloop()
Here ya go. You had a bunch of issues. I commented everything in the code that was fixed or changed.
import tkinter as tk
#prepare some data
ButtonPlace = dict(relx=0.35, rely=0.79, relwidth=0.3, relheight=0.1)
ButtonConfig = dict(bg='#3ab54a', fg='blue', activebackground='#3ab54a', activeforeground='blue')
CanvasConfig = dict(width=600, height=400, highlightthickness=0)
#class names should start with a capital letter
class BackBone(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
#you don't need an inner frame so I got rid of it
#init some vars for storing and managing pages
self.page = 0
self.pages = {}
self.page_names = []
#make a refererence of pages and page names
for C in [StartPage, PageOne]:
self.pages[C.NAME]=C(self)
self.page_names.append(C.NAME)
#you can just use one button for every page
self.btn = tk.Button(self, text="Start Page", command=self.next_page, **ButtonConfig)
self.btn.place(**ButtonPlace)
#init start page
self.btn.invoke()
def next_page(self):
#whatever page is packed ~ forget it
for n, f in self.pages.items():
f.pack_forget()
#get page name
name = self.page_names[self.page]
#pack page associated with name
self.pages[name].pack()
#change button text to the name of this page (same as you had it)
self.btn['text'] = name
#raise the button up in z-order
self.btn.tkraise()
#prime next page number
self.page = (self.page + 1) % len(self.page_names)
class StartPage(tk.Canvas):
#static page name reference
NAME = 'Start Page'
#the proper term is master ~ not parent. controller is no more
def __init__(self, master):
tk.Canvas.__init__(self, master, bg='#aaaaff', **CanvasConfig)
#you don't need a frame. make the whole thing a canvas
#apply StartPage comments to this page, as well
class PageOne(tk.Canvas):
NAME = 'Page One'
def __init__(self, master):
tk.Canvas.__init__(self, master, bg='#ffaaaa', **CanvasConfig)
#this is the proper way to initialize your app
if __name__ == '__main__':
app = BackBone()
app.configure(bg='beige', highlightthickness=0, bd=0)
app.resizable(False, False)
app.mainloop()
I have made my custom infobox class, InfoBox, that I am using in my application. The tk.messagebox.showinfo did not suit my needs to the poor shape. But InfoBox does not adjust its size to fit the widgets I place inside. How can I make it as small as possible without cutting the widgets?
The class receives a string, msg, and a PhotoImage object, image, which are placed in the InfoBox. I added a screenshot of one such InfoBox.
class InfoBox(tk.Toplevel):
def __init__(self, parent, msg, image):
tk.Toplevel.__init__(self, parent)
self.parent = parent
self.msg = msg
self.image = image
self.title = "Gassy"
self.font = font.Font(family="Optima", size=20)
frame_left = tk.Frame(self)
frame_right = tk.Frame(self)
frame_left.grid(row=0, column=0, sticky=tk.NSEW)
frame_right.grid(row=0, column=1, sticky=tk.NSEW)
tk.Label(frame_left, image=self.image).grid(row=0, column=0, sticky=tk.N)
textbox = tk.Text(frame_right, font=self.font)
textbox.grid(row=0, column=0)
textbox.insert(tk.END, self.msg)
textbox.config(state=tk.DISABLED)
tk.Button(frame_left, text="Den er grei!", font=self.font, command=self.destroy).grid(row=1, column=0)
As #kevin mentioned, it works as intended, the textwidget is mostly empty and occupies a large blank area, this is what makes you think that the geometry manager is not shrinking the window to the widgets.
this:
(I removed the images and fonts that were not provided, and unnecessary)
import tkinter as tk
class InfoBox(tk.Toplevel):
def __init__(self, parent, msg):
tk.Toplevel.__init__(self, parent)
self.parent = parent
self.msg = msg
self.title = "Gassy"
frame_left = tk.Frame(self)
frame_right = tk.Frame(self)
frame_left.grid(row=0, column=0, sticky=tk.NSEW)
frame_right.grid(row=0, column=1, sticky=tk.NSEW)
# textbox = tk.Text(frame_right)
# textbox.grid(row=0, column=0)
# textbox.insert(tk.END, self.msg)
# textbox.config(state=tk.DISABLED)
tk.Button(frame_left, text="Den er grei!", command=self.destroy).grid(row=1, column=0)
root = tk.Tk()
info = InfoBox(root, '123 ' * 1000)
root.mainloop()
produces that:
whereas that:
import tkinter as tk
class InfoBox(tk.Toplevel):
def __init__(self, parent, msg):
tk.Toplevel.__init__(self, parent)
self.parent = parent
self.msg = msg
self.title = "Gassy"
frame_left = tk.Frame(self)
frame_right = tk.Frame(self)
frame_left.grid(row=0, column=0, sticky=tk.NSEW)
frame_right.grid(row=0, column=1, sticky=tk.NSEW)
textbox = tk.Text(frame_right)
textbox.grid(row=0, column=0)
textbox.insert(tk.END, self.msg)
textbox.config(state=tk.DISABLED)
tk.Button(frame_left, text="Den er grei!", command=self.destroy).grid(row=1, column=0)
root = tk.Tk()
info = InfoBox(root, '123 ' * 1000)
root.mainloop()
produces this:
Clearly, the Toplevel subclass adjusts its size to the widgets it contains
The test widget is displayed at a certain size, regardless of its content. The Toplevel resizes around the widgets, NOT around whatever is inserted in the text widget; like with a text processor rudimentary window, the text processor does not shrink or expand as text is typed or edited. The same applies here.
The keyword args width and height allow to configure the size (as a number of characters, or lines) of a text widget
I use Python 2.7 and I have a scrollable frame where the canvas is not shrinking to fit the frame I want to make scrollable.
I looked at this question for an answer but it does not work when I run it:
How to resize a scrollable frame to fill the canvas?
When I print the width of the frame inside the canvas, it says 0.
I also ran the code from the answer of this question on my computer :
Scrollable Frame does not resize properly using tkinter in Python
but it will still show the white canvas to the left of the labels, and it does not resize when the labels are deleted.
It looks like this:
This is my code, based on the answer in this question:
Adding a scrollbar to a group of widgets in Tkinter
from Tkinter import *
class Scrollable_frame(Frame):
def __init__(self, parent, title, values):
self.parent = parent
Frame.__init__(self, self.parent)
self.canvas = Canvas(self, borderwidth=0, background="#ffffff")
self.scrollbar = Scrollbar(self, command=self.canvas.yview)
self.innerFrame = Radiobutton_frame(self.canvas,title,values)
self.canvas.configure(yscrollcommand=self.scrollbar.set)
self.canvas.grid(row=0, column=0, sticky= N+S)
self.scrollbar.grid(row=0, column=1, sticky = N+S)
self.canvas.create_window((0,0),window = self.innerFrame,anchor="nw")
self.innerFrame.bind("<Configure>", self.set_canvas_scrollregion)
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
def set_canvas_scrollregion(self, event):
width = event.width - 4
self.canvas.itemconfigure("self.innerFrame ", width=width)
self.canvas.config(scrollregion=self.canvas.bbox("all"))
class Radiobutton_frame(LabelFrame):
def __init__(self, parent, title, values):
"""
In: parent - Canvas
title - String
values - List of Int
"""
self.radiobuttons = {}
self.parent = parent
self.selection = StringVar()
self.selection.set("init")
LabelFrame.__init__(self, self.parent, text = title)
for value in values:
self.add_radiobutton(value)
def add_radiobutton(self, value):
"""
Adds a radiobutton to the frame.
In: item - String
"""
# Associate to same variable to make them function as a group
self.radiobuttons[value] = Radiobutton(master = self,
variable = self.selection,
text = value,
value = value)
self.radiobuttons[value].pack(anchor=W)
# Usage example
root = Tk()
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
scrollableFrame = Scrollable_frame(root, "Canvas not resizing", range(30))
scrollableFrame.grid(row=0, column=0, sticky=N+S+E+W)
if __name__ == '__main__':
root.mainloop()
I don't think above question's code snippet fits to a Minimal, Complete, and Verifiable example but at the very least it's runnable.
You have three mistakes compared to that of: How to resize a scrollable frame to fill the canvas?
The most significant of which is that in the linked question, the OP uses the option tags where you don't. Replace:
self.canvas.create_window((0,0),window = self.innerFrame,anchor="nw")
with:
self.canvas.create_window((0,0),window = self.innerFrame, anchor="nw", tags="my_tag")
Another mistake is that you're binding the event of a frame's resizing as opposed to the actual Canvas' resizing, also pointed out in Bryan's comment here. Replace:
self.innerFrame.bind("<Configure>", self.set_canvas_scrollregion)
with:
self.canvas.bind("<Configure>", self.set_canvas_scrollregion)
Lastly, tkinter doesn't seem to accept space character with tags, replace:
self.canvas.itemconfigure("self.innerFrame ", width=width)
with:
self.canvas.itemconfigure("my_tag", width=width)
Finally, you should have:
from Tkinter import *
class Scrollable_frame(Frame):
def __init__(self, parent, title, values):
self.parent = parent
Frame.__init__(self, self.parent)
self.canvas = Canvas(self, borderwidth=0, background="#ffffff")
self.scrollbar = Scrollbar(self, command=self.canvas.yview)
self.innerFrame = Radiobutton_frame(self.canvas,title,values)
self.canvas.configure(yscrollcommand=self.scrollbar.set)
self.canvas.grid(row=0, column=0, sticky= N+S)
self.scrollbar.grid(row=0, column=1, sticky = N+S)
self.canvas.create_window((0,0),window = self.innerFrame,anchor="nw",
tags="my_tag")
self.canvas.bind("<Configure>", self.set_canvas_scrollregion)
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
def set_canvas_scrollregion(self, event):
width = event.width - 4
self.canvas.itemconfigure("my_tag", width=width)
self.canvas.config(scrollregion=self.canvas.bbox("all"))
class Radiobutton_frame(LabelFrame):
def __init__(self, parent, title, values):
"""
In: parent - Canvas
title - String
values - List of Int
"""
self.radiobuttons = {}
self.parent = parent
self.selection = StringVar()
self.selection.set("init")
LabelFrame.__init__(self, self.parent, text = title)
for value in values:
self.add_radiobutton(value)
def add_radiobutton(self, value):
"""
Adds a radiobutton to the frame.
In: item - String
"""
# Associate to same variable to make them function as a group
self.radiobuttons[value] = Radiobutton(master = self,
variable = self.selection,
text = value,
value = value)
self.radiobuttons[value].pack(anchor=W)
# Usage example
root = Tk()
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
scrollableFrame = Scrollable_frame(root, "Canvas not resizing", range(30))
scrollableFrame.grid(row=0, column=0, sticky=N+S+E+W)
if __name__ == '__main__':
root.mainloop()
Tkinter experts, I'm having trouble getting a Canvas to scroll. This is my second GUI, and I've done something similar before, so I don't know what I'm doing wrong. I'd appreciate any help you can offer.
Here's a minimal version of what I'm trying to do. I'm using python 3.4.3 on Windows 10.
import tkinter as tk
import tkinter.font as tk_font
import tkinter.ttk as ttk
import random
def get_string_var(parent, value=''):
var = tk.StringVar(parent)
var.set(value)
return var
class SummaryFrame(ttk.Frame):
def __init__(self, parent, **kwargs):
ttk.Frame.__init__(self, parent, **kwargs)
var_names = ['label_'+str(num) for num in range(1, 20)]
self.vars = {}
for name in var_names:
self.vars[name] = get_string_var(self)
self._add_summary_labels(self, self.vars, 1)
#staticmethod
def _add_summary_labels(frame, vars, start_row):
current_row = start_row
for name in vars:
tk.Label(frame, text=name, anchor=tk.N+tk.W).grid(row=current_row, column=0, sticky=tk.N+tk.S+tk.W+tk.E)
text_label = tk.Label(frame, wraplength=200, textvariable=vars[name], anchor=tk.N+tk.W, justify=tk.LEFT)
text_label.grid(row=current_row, column=1, sticky=tk.W)
current_row += 1
def set_summary_fields(self, info):
for name in info:
if name in self.vars:
self.vars[name].set(info[name])
class OuterFrame(ttk.Frame):
def __init__(self, parent, **kwargs):
ttk.Frame.__init__(self, parent, **kwargs)
self.canvas = tk.Canvas(self)
scrollbar = ttk.Scrollbar(self, orient=tk.VERTICAL, command=self.canvas.yview)
self.canvas.configure(yscrollcommand=scrollbar.set)
self.summary = SummaryFrame(self.canvas)
self.summary.pack(fill=tk.BOTH, expand=1)
self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
scrollbar.pack(side=tk.LEFT, fill=tk.Y, expand=1)
label_text = {}
for i in range(1, 20):
label_text['label_'+str(i)] = "information " * random.randint(1, 20)
self.set_fields(label_text)
def set_fields(self, info):
self.summary.set_summary_fields(info)
self.canvas.configure(scrollregion=(1, 1, self.summary.winfo_width(), self.summary.winfo_height()))
if __name__ == "__main__":
root = tk.Tk()
frame = OuterFrame(root)
frame.pack(fill=tk.BOTH, expand=1)
root.mainloop()
The scrollbar should change when the contents of the inner SummaryFrame expands, but doesn't. It remains grayed out and inoperable. What am I doing wrong?
Short answer: you are configuring the scrollregion to be one pixel by one pixel.
You are configuring the scrollregion based on the width and height of the self.summary, but you are doing this before the window has a chance to be mapped to the screen. The width and height, therefore, are both one.
You need to wait for the window to be drawn before computing the width and height of the window.