Why tkinter don't throw/raise an exception? - python

The problem is if you run this code below and resize the window then you will get
unexpected behavior. Widgets are not rendered like they should and the backgrounds
have some wierd colors.
I know if you set a argument then you should
fill it with a valide value. If you do fill it with an empty string like I did
it don't throw a <_tkinter.TclError: unknown color name "">.
My Question is why we
don't get an error? Is that a tkinter bug? Because if you set the bg argument of Canvas
as an empty string you will get an error: unknown color name ""
from tkinter import Tk, Frame, Button, Entry, Canvas
class Gui(Frame):
def __init__(self, master):
self.master = master
self.font1 = font=("Segoe UI",18,"bold")
self.font2 = font=("Segoe UI",28,"bold")
Frame.__init__(self, self.master, bg="")
self.grid()
self.create_widgets()
def create_widgets(self):
self.frame_container = Frame(self, bg="")
self.frame_container.grid(row=0, column=0)
self.button_test_1 = Button(self, text="Test1", bg="yellow")
self.button_test_1.grid()
self.can = Canvas(self.frame_container, bg="orchid1")
self.can.grid(row=0, column=0)
self.entry_test = Entry(self.frame_container)
self.entry_test.grid(row=0, column=1)
if __name__ == "__main__":
root = Tk()
bug_gui = Gui(root)
root.mainloop()

#Idlehands Thanks for citing Fredrik Lundh's effbot.org explanation on background. I think I finally understand Fredrik Lundh's documentation. Namely,
bg="" is a valid argument that will prevent the updating of the Frame widget's background, and
the background or bg of a Frame widget defaults to the application background color.
Hence, when the Tk window is resized, e.g. when the right edge of the Tk window is move to the left to reach the Canvas widget's right edge and followed by moving right back to its original position, the following phenomenon happens:
Firstly, the Frame widget's width reduces in size. When this happens, I suspect the Tk widow background colour that filled the Frame widget is lost.
Secondly, when the Frame widget's width next returns to its original size, it's background needs to be updated (i.e. filled) with a defined colour. Because bg="" prevents updating, i.e. prevents filling the Frame widget background with a defined background colour, the Frame widget's background will default to the "application background colour". Meaning it will be filled with the exact color of the area directly behind the Frame widget and the Tk window, as if it became 'transparent'.
Answering #Turjak_art question, tkinter did not yield an Error or Exception because bg="" is a valid argument of the Frame widget.
Edit:
Image showing Frame background with fast resizing.
Image showing Frame background with slow resizing.

Related

Adaptive resizing for a tkinter Text widget

I have been attempting to create a application that contains two Text() widgets, both of which can dynamically resize when the window size is changed. Before I have always used the root.pack() manager, with fill='both' and expand=True.
While this works for LabelFrames and most other widgets, it does not work when a Text widget is resized smaller then its original dimensions. Is there a way to have dynamically resizing Text widgets?
Ex.
import tkinter as tk
window = tk.Tk()
editor = tk.Text(bg='red')
editor.pack(side='top',fill='both',expand=True)
output = tk.Text(bg='green')
output.pack(side='top',fill='both',expand=True)
window.mainloop()
Tkinter will try to honor the requested size of a text widget. Since you didn't specify a size, the text widget will request a size of 80x24. When you resize the window smaller, pack tries to make room for everything at its requested size, and it does so in stacking order.
As the window shrinks, there's room for all of the first text widget but not enough for both. Because there's not enough room, it has to subtract space from the remaining widgets. Thus, it starts chopping off the last text widget.
To combat this, you can set the requested size of the text widgets to a small value that will fit in almost any window size, and then force them to grow by setting the size of the window as a whole. This way, pack will first allocate enough space for each small window, and then expand them equally when there's extra space.
For example:
import tkinter as tk
window = tk.Tk()
window.geometry("400x400")
editor = tk.Text(bg='red', width=1, height=1)
output = tk.Text(bg='green', width=1, height=1)
editor.pack(side='top',fill='both',expand=True)
output.pack(side='top',fill='both',expand=True)
window.mainloop()
The other solution is to use grid which lets you specify that rows and columns should be of uniform size.
import tkinter as tk
window = tk.Tk()
editor = tk.Text(bg='red')
output = tk.Text(bg='green')
editor.grid(row=0, column=0, sticky="nsew")
output.grid(row=1, column=0, sticky="nsew")
window.grid_columnconfigure(0, weight=1)
window.grid_rowconfigure((0,1), weight=1, uniform=1)
window.mainloop()

How can I make my background image not block out any existing widgets in tkinter?

I am working on a tkinter project, where I set the background image (not a solid color). I was using .grid() for my widgets, but then I realized I need to display a widget at the very bottom of the tkinter window. To continue using grid for this, I separated my widgets into two frames; 1 frame that contained most of the widgets and one that just contained the widget I want to be displayed at the bottom. Then, I packed my frames, one on the top and the other at the bottom.
However, after I did this, my background image went on top of my existing widgets and blocked them out, so they can't be seen anymore. What can I do to fix this?
Here is the method I used to display the background photo:
from tkinter import *
filename = PhotoImage(file="image.png")
background_label = Label(root, image=filename) # root is the Tk() object
background_label.place(x=0, y=0, relwidth=1, relheight=1)
# Other code here
mainloop()

why do the key arguments that regulate the size of a tk canvas fail to work after a button is added?

I was trying to build a window large enough that contains some widgets in it. So I passed some key arguments to regulate the size of the canvas and it worked as expected. However, when I added a button inside the canvas, the window returned to its original small size(which may be the default size).
I tested the value of width of height at the end of the code:
print(window["width"]) gives 700
print(window["height"]) gives 800
This further buffles me because if the width and height properties have the value of what I entered, why would the addition of the button prevent the window from demonstrating these properties?
import tkinter as tk
root=tk.Tk()
window=tk.Canvas(root,width=700,height=800)
window.pack()
button=tk.Button(window,text="test button") #(1)
button.pack()#(2)
# the Canvas shows the wanted size when (1) and (2)is deleted
root.mainloop()
Canvas or Frame will shrink to the size it needs when a widget has been packed or grided. But you can pass propagate to explicitly tell it not to:
window=tk.Canvas(root,width=700,height=800)
window.pack()
window.propagate(0)
Alternatively you can use place method on your Button widget instead:
button.place(relx=0.5,rely=0.5)

Tkinter Scrollbar not working

I have a piece of tkinter code running on python 3.4 that is a large frame placed in a canvas with a vertical scrollbar, however the scrollbar is grayed out and doesn't seem to be linked to the size of the frame. The code I'm using is essentially:
class EntryWindow:
def __init__(self, master):
self.master = master
self.master.minsize(750, 800)
self.master.maxsize(1000, 800)
self.canvas = tk.Canvas(self.master, borderwidth=0, bg='#ffffff')
self.vsb = tk.Scrollbar(self.master)
self.master_frame = tk.Frame(self.canvas)
self.vsb.pack(side="right", fill='y')
self.canvas.pack(side='left', fill='both', expand=True)
self.canvas.create_window((0,0), window=self.master_frame, anchor='nw', tags='self.master_frame')
self.canvas.config(yscrollcommand=self.vsb.set)
self.master_frame.grid()
###build widgets and place into master_frame
...
The master_frame is filled with about 36 widgets placed using the grid geometry manager and reaches a vertical height of about 2000 pixels, but the scrollbar doesn't work.
I saw a post about using ttk scrollbar, but when I imported that I couldn't get it to work. The input statement I used was:
import tkinter as tk
import tkinter.ttk as ttk
and then replaced the line self.vsb = tk.Scrollbar(self.master) with self.vsb = ttk.Scrollbar(self.master) but that didn't fix it either. I also tried removing the min/max sizing on master (those aren't the final values, I've been playing with it).
Is there something I'm doing wrong? I feel like it might be in the line where I set the canvas.config() but I read the documentation and it seems to be right. Tomorrow I plan on trying to load the frame into the canvas after I've built the frame.
But in the meantime, and in case that doesn't work, any help would be great! Thanks!
You must do three things when configuring a scrolling canvas:
have the canvas notify the scrollbar when it scrolls, by configuring the yscrollcommand attribute of the canvas to point to the scrollbar
have the scrollbar control the canvas when it is dragged, by configuring the command attribute of the scrollbar
tell the canvas what part of the virtual canvas should be scrollable by configuring the scrollregion attribute of the canvas
You are neglecting to do #3. After adding the widgets to master_frame you should do this:
self.canvas.configure(scrollregion=self.canvas.bbox("all")
The above will tell the canvas and scrollbar that the area of the canvas that is scrollable is the area that encompasses all of the objects on the canvas.
Finally, you need to remove the following line of code, because you are already adding the frame to the canvas with create_window. Frames added to a canvas with pack, place or grid won't scroll:
# remove this line
self.master_frame.grid()
For a working example of a scrollable frame, see Adding a scrollbar to a group of widgets in Tkinter

Specifying the dimensions of a Tkinter Text Widget in pixels?

I'm trying to create a text widget in Tkinter (for Python) whose font size can change, but when this happens I don't want the widget to resize itself. All of the text in the widget is the same font style. What I've got so far:
root = Tk()
t = Toplevel(root)
fnt = tkFont.Font(family="Helvetica",size=36,weight="bold",underline=1)
txt = Text(t, font=fnt, width=20, height=6)
txt.grid(row=0,column=0)
b = Button(t, text="click", command=change)
b.grid(row=1, column=0)
txt.insert(END, "This is text!")
Where change is defined as:
def change():
txt.delete(1.0, END)
fnt.config(size=100)
txt.insert(END, "This is text!")
Now, when I click on the button, the text does indeed get bigger, but the entire widget resizes itself to compensate! I assume that this is because the size of the widget is specified in terms of "lines" and "characters", instead of pixels. How can I stop the widget from resizing?
I've tried not changing the font of the widget, but instead just inserting text with a tag that specifies a new font, which works, but then the problem is that when I manually type new text to the left and right of the inserted text, it is the default style and not the size I want.
I found the answer to my question on this page: How to stop Tkinter Text widget resize on font change?
The key is to build a Frame to put the text widget in, to specify the Frame size in pixels, and to call grid_propagate(False) on the Frame to prevent it from resizing when the text widget wants to resize.

Categories

Resources