Why the text is not displayed on the label? - python

I see the text if I enlarge the window manually, so the text is there! (On the label) but if not enlarged the window I can not see, I see only a piece of the label
How can I fix it? Do I have to resize?
I've tried changing the font size, the size of the label and some other things and I could not even solve the problem.
This is the code:
from Tkinter import *
import ttk
import time
import threading
def start():
start_stop.config(text="QUIT", command=stop, image=photo2)
thread = threading.Thread(target=progBar, args=())
thread.daemon = True
thread.start()
def progBar():
for i in xrange(300):
if i < 50:
lbl1.config(height=0, width=4, font=('times', 400, 'bold'),
text="AAAA!!")
lbl1.config(fg='black')
if i % 5:
lbl1.config(bg='white')
else:
lbl1.config(bg='red')
if i == 50:
lbl1.destroy()
if i >= 200:
s = ttk.Style()
s.theme_use('clam')
if i % 5:
s.configure("red.Horizontal.TProgressbar",
foreground='#205F8C', background='#205F8C')
else:
s.configure("red.Horizontal.TProgressbar",
foreground='red', background='red')
pbar_det.config(style="red.Horizontal.TProgressbar")
pbar_det.step(0.33)
master.update()
# Busy-wait
time.sleep(0.1)
master.destroy()
def stop():
master.destroy()
master = Tk()
photo1 = PhotoImage(file="Press1.gif")
photo2 = PhotoImage(file="Press2.gif")
ws = master.winfo_screenwidth() # width of the screen
hs = master.winfo_screenheight() # height of the screen
width = ws
height = 120
x = ws-width # Window's coords
y = hs-height
start_stop = Button(master, text='START', command=start, image=photo1)
start_stop.grid(row=0, column=1, pady=2, padx=2, sticky=E+W+N+S)
pbar_det = ttk.Progressbar(master, orient="horizontal", length=ws-450,
mode="determinate")
pbar_det.grid(row=0, column=0, pady=2, padx=2, sticky=E+W+N+S)
lbl1 = Label(master)
lbl1.grid(row=0, column=1, pady=2, padx=2)
lbl2 = Label(master)
lbl2.grid(row=0, column=1, pady=2, padx=2)
master.geometry('%dx%d+%d+%d' % (width, height, x, y))
master.mainloop()

Try anchoring the text within the label to the left.
lbl1.config(anchor="nw") # nw = north-west = top left

I see at least four problems in the code:
you are forcing the size of the widget to a specific height when you call master.geometry(...). This prevents the window from growing or shrinking to fit the internal widgets
you are specifying a really huge font (400 points) which can't fit in the height you've picked. Because of the huge size of the font relative to the space you've given it (by explicitly setting the height of the window) what you see is the natural whitespace above the letters that is present in all fonts).
you are putting two labels and one button on top of each other in the same row and column
you are using threading. Tkinter isn't thread safe. Your code might work, but it might not.
If you remove the call to master.geometry(), the window will expand to fit the height of the text. Or, you can not make the font so huge.

Related

White blink when creating new window?

I'm creating this program that it's not complete yet, a small detail that annoys me, when you run the program and click "OPEN" it creates a new Toplevel window inside the def click_selettore function in the beginning, and i notice that the black screen on the left "blinks" for a split second, for a split second is white and then it become black i hope you notice this, the photo attached it's a frame of the Toplevel opening window and i notice the white frames that are loading i think, and they are white for a split second. How to solve this and not making the new window white for the split second? Thanks, here is the code it's a bit long but the probelm is only the white blinks when creating the window inside the "click selettore" function, if you help me i would really appreciate it
from tkinter import *
import customtkinter
from ctypes import windll
windll.shcore.SetProcessDpiAwareness(1)
def click_selettore(event):
bg = "#414141"
win.attributes('-topmost', 0)
win2 = Toplevel(win)
win2.focus()
win2.resizable(False,False)
win2.grab_set()
win2.configure(bg=bg)
win2.title("Selettore colori")
frame_schermo = Frame(win2)
frame_schermo.grid(row=0,column=0,padx=(20,0),pady=(20,0))
schermo = Label(frame_schermo, bg="#000000", width=45, height=9, relief="solid", borderwidth=3)
schermo.grid(row=0,column=0)
frame_slider = Frame(win2,bg=bg)
frame_slider.grid(row=0,column=1,sticky=N,pady=(20,0),padx=(50,0))
etc_rosso = Label(frame_slider, text="Rosso", bg=bg, fg="#C70000", font=("Ink Free", 20, "bold"))
etc_rosso.grid(row=0,column=0,pady=(11,0))
slider_rosso = customtkinter.CTkSlider(frame_slider, from_=0, to=255,width=230,progress_color='#C70000',fg_color='#a34949',button_hover_color='#3f9feb',button_color='#1f82d1')
slider_rosso.grid(row=0,column=1,sticky=N,padx=(10,0),pady=(23,0))
slider_rosso.set(0)
casella_rosso = customtkinter.CTkEntry(master=frame_slider,text_font=('Courier',15,'bold'),width=55)
casella_rosso.grid(row=0,column=2,pady=(11,0),padx=(7,20))
casella_rosso.insert(0,0)
etc_verde = Label(frame_slider, text="Verde", bg=bg, fg="#3fc441", font=("Ink Free", 20, "bold"))
etc_verde.grid(row=1,column=0,pady=(11,0))
slider_verde = customtkinter.CTkSlider(frame_slider, from_=0, to=255, width=230, progress_color='#3fc441', fg_color='#3d803e', button_hover_color='#3f9feb', button_color='#1f82d1')
slider_verde.grid(row=1, column=1, sticky=N,padx=(10,0),pady=(23,0))
slider_verde.set(0)
casella_verde = customtkinter.CTkEntry(master=frame_slider, text_font=('Courier', 15, 'bold'), width=55)
casella_verde.grid(row=1, column=2,pady=(11,0),padx=(7,20))
casella_verde.insert(0, 0)
etc_blu = Label(frame_slider, text="Blu", bg=bg, fg="#010178", font=("Ink Free", 20, "bold"))
etc_blu.grid(row=2,column=0,pady=(11,0))
slider_blu = customtkinter.CTkSlider(frame_slider, from_=0, to=255, width=230, progress_color='#010178',fg_color='#222275', button_hover_color='#3f9feb', button_color='#1f82d1')
slider_blu.grid(row=2, column=1, sticky=N,padx=(10,0),pady=(23,0))
slider_blu.set(0)
casella_blu = customtkinter.CTkEntry(master=frame_slider, text_font=('Courier', 15, 'bold'), width=55)
casella_blu.grid(row=2, column=2,pady=(11,0),padx=(7,20))
casella_blu.insert(0, 0)
frame_sotto_slider = Frame(win2,bg=bg)
frame_sotto_slider.grid(row=1,column=0,pady=(45,0),padx=(35,0))
etc_esadecimale_titolo= Label(frame_sotto_slider,text='Esadecimale -> ',font=('Courier',16),fg='white',bg=bg)
etc_esadecimale_titolo.grid(row=0,column=0)
campo_esadecimale = customtkinter.CTkEntry(master=frame_sotto_slider,text_font=('Courier',16),width=105)
campo_esadecimale.grid(row=0,column=1,padx=(6,0))
btn_ok = customtkinter.CTkButton(win2,text='Ok',text_font=('Courier',16),width=50,corner_radius=10,hover_color='#3a76a6')
btn_ok.grid(row=3,column=1,sticky=E,pady=(50,10),padx=(0,10))
win.eval(f'tk::PlaceWindow {str(win2)} center')
win2.mainloop()
win = Tk()
win.configure(bg="#2b2b2b")
etichetta_selettore_colori = Label(win,text='OPEN',relief="solid",borderwidth=4,font=('Helvetica',17))
etichetta_selettore_colori.grid(row=0,column=0,padx=(20,0),pady=20)
etichetta_selettore_colori.bind("<Button-1>",click_selettore)
win.eval('tk::PlaceWindow . center')
win.mainloop()

Tkinter displaying output in a entry box using when using nested classes

So the problem is that when using the second window (which you navigate to using the next page button). When I enter the 8-bit binary numbers in each box the premise is that it should add them together and display them in the labeled added binary box. but at the moment it does not seem to set the value correctly to do that and it just prints in the command line what I set it to which shows that the method is working.
Any insight into how to fix this would be greatly appreciated.
FYI yes it is deliberately done for 8-bit binary and yes it needs to use nested classes. as that is what the assignment requires the use of.
sorry if it is just a small error and I am being clumsy somewhere but I have been stumped for some time.
Here is the revised minimal reproducible example:
from tkinter import *
from tkinter.ttk import *
from tkinter import ttk
import tkinter
import math
import sys
def BinaryAddition(value1, value2):
if len(value1) != 8:
return "Binary number 1 is not 8 bits"
if len(value2) != 8:
return "Binary number 2 is not 8 bits"
res = bin(int(value1,2) + int(value2,2))
Ob = res[2:]#removing the ob prefix
return Ob
class MainWindow():
FONT = ("Consolas", 16)
TxtMaxLen = 32
class SecondWindow():
def __init__(self2):
self2._window = tkinter.Tk()
self2._window.title("Converter")
self2._window["bg"] = "#EFFFA3"
f = ("Times bold", 16)
TxtMaxLen = 32
self2._window.geometry("820x240")
label = tkinter.Label(self2._window, text="Binary Number 1: ", font= f)#label defined for number input box
label.grid(row=0, column=0, padx=10, pady=5)#positioning / dimensions of input box
label = tkinter.Label(self2._window, text="Binary Number 2: ", font= f)#label defined for number input box
label.grid(row=1, column=0, padx=10, pady=5)#positioning / dimensions of input box
#input box for binary number 1
self2.input1 = tkinter.Entry(self2._window, width= TxtMaxLen, font= f) #defined input box
self2.input1.grid(row=0, column=1, pady=5)#postioning / dimensions of input box
self2.input1.focus()
#input box for binary number 2
self2.input2 = tkinter.Entry(self2._window, width=TxtMaxLen, font= f) #defined input box
self2.input2.grid(row=1, column=1, pady=5)#postioning / dimensions of input box
self2.input2.focus()
separator = tkinter.ttk.Separator(self2._window,orient=tkinter.HORIZONTAL)
separator.grid(row=3, column=1, pady=15)
self2._bt_Add = tkinter.Button(self2._window, text="Add", font= f, command = self2.AdditionSelection)#button defined for bin
self2._bt_Add.grid(row=1, column=2, padx=5, pady=5)#postioning / dimensions of button box
#labels for output box of combined binary number
label = tkinter.Label(self2._window, text="Added Binary: ", font= f)#label defined for number input box
label.grid(row=4, column=0, padx=10, pady=5)#positioning / dimensions of input box
#label for output box for if there was or was not an overflow
label = tkinter.Label(self2._window, text="OverFlow: ", font= f)#label defined for number input box
label.grid(row=5, column=0, padx=10, pady=5)#positioning / dimensions of input box
#output box for the added binary number
self2.stringvar_Combined = tkinter.StringVar()
txt_output = tkinter.Entry(self2._window, textvariable=self2.stringvar_Combined, width=TxtMaxLen, state="readonly", font= f)#entry box set to readonly to act as a display box
txt_output.grid(row=4, column=1, pady=5)
#output box for if there was or was not an overflow
self2._stringvar_OverFlow = tkinter.StringVar()
txt_output = tkinter.Entry(self2._window, textvariable=self2._stringvar_OverFlow, width=TxtMaxLen, state="readonly", font= f)#entry box set to readonly to act as a display box
txt_output.grid(row=5, column=1, pady=5)
separator = tkinter.ttk.Separator(self2._window,orient=tkinter.VERTICAL)
separator.grid(row=3, column=2, pady=15)
PrevPageButton = tkinter.Button(self2._window, text="Previous Page", font=f, command=MainWindow)
PrevPageButton.grid(row=5, column = 2, padx = 5,pady = 5)
def set_values(self, BinAdd):
self.stringvar_Combined.set(BinAdd)
def AdditionSelection(self2):
try:
BinV1 = self2.input1.get().strip().replace(" ", "")
BinV2 = self2.input2.get().strip().replace(" ", "")
BinAdd = BinaryAddition(BinV1, BinV2)
self2.set_values(BinAdd)
print(BinAdd)
print("hi there")
except Exception as ex:
print("NOPE it aint workin")
def __init__(self):
self._window = tkinter.Tk()
self._window.title("Converter")
self._window["bg"] = "#20A3FF" #background colour
self._window.geometry("820x240")#setting window size
#setting up button for binary additon page
NextPageButton = tkinter.Button(self._window, text="Next Page", font=MainWindow.FONT, command=self.SecondWindow)
NextPageButton.grid(row=4, column = 3, padx = 5,pady = 5)
self._window.destroy
def mainloop(self):
self._window.mainloop()
if __name__ == "__main__":
win = MainWindow()
win.mainloop()
Main problem: to create second window you should use Toplevel() instead of Tk().
And when I use Toplevel then it show text in window.
And if you want to see only one window then you should first destroy() old window and later you can use Tk() to create new window (with new mainloop()).
But this can make other problem. It delete all values in window and when you will back some window then it will not have old value. It may be better to create two Frames and replace them in Tk() without destroying them - and they will keep old values.
Rest are only suggestions how to create more readable code:
don't put SecondWindow inside MainWindow
don't use self2
use lower_case_names for functions and variables
More in PEP 8 -- Style Guide for Python Code
Code with some changes.
It display value (I also added .zfill(8) to display 8 digits)
But this creates new window everytime when you change window - next or previous - so you may have two main windows, etc. It would need more changes.
import tkinter # PEP8: `import *` is not preferred
from tkinter import ttk
import math
import sys
def binary_addition(value1, value2):
if len(value1) != 8:
return "Binary number 1 is not 8 bits"
if len(value2) != 8:
return "Binary number 2 is not 8 bits"
res = bin(int(value1,2) + int(value2,2))
return res[2:].zfill(8) #removing the ob prefix
class MainWindow():
FONT = ("Consolas", 16)
TxtMaxLen = 32
def __init__(self):
self._window = tkinter.Tk()
self._window.title("Converter")
self._window["bg"] = "#20A3FF" #background colour
self._window.geometry("820x240")#setting window size
#setting up button for binary additon page
next_page_button = tkinter.Button(self._window, text="Next Page", font=MainWindow.FONT, command=SecondWindow)
next_page_button.grid(row=4, column = 3, padx = 5,pady = 5)
def mainloop(self):
self._window.mainloop()
class SecondWindow():
def __init__(self):
self._window = tkinter.Toplevel()
self._window.title("Converter")
self._window["bg"] = "#EFFFA3"
f = ("Times bold", 16)
TxtMaxLen = 32
self._window.geometry("820x240")
label = tkinter.Label(self._window, text="Binary Number 1: ", font= f)#label defined for number input box
label.grid(row=0, column=0, padx=10, pady=5)#positioning / dimensions of input box
label = tkinter.Label(self._window, text="Binary Number 2: ", font= f)#label defined for number input box
label.grid(row=1, column=0, padx=10, pady=5)#positioning / dimensions of input box
#input box for binary number 1
self.input1 = tkinter.Entry(self._window, width= TxtMaxLen, font= f) #defined input box
self.input1.grid(row=0, column=1, pady=5)#postioning / dimensions of input box
self.input1.focus()
#input box for binary number 2
self.input2 = tkinter.Entry(self._window, width=TxtMaxLen, font= f) #defined input box
self.input2.grid(row=1, column=1, pady=5)#postioning / dimensions of input box
self.input2.focus()
separator = tkinter.ttk.Separator(self._window,orient=tkinter.HORIZONTAL)
separator.grid(row=3, column=1, pady=15)
self._bt_add = tkinter.Button(self._window, text="Add", font= f, command = self.addition_selection)#button defined for bin
self._bt_add.grid(row=1, column=2, padx=5, pady=5)#postioning / dimensions of button box
#labels for output box of combined binary number
label = tkinter.Label(self._window, text="Added Binary: ", font= f)#label defined for number input box
label.grid(row=4, column=0, padx=10, pady=5)#positioning / dimensions of input box
#label for output box for if there was or was not an overflow
label = tkinter.Label(self._window, text="OverFlow: ", font= f)#label defined for number input box
label.grid(row=5, column=0, padx=10, pady=5)#positioning / dimensions of input box
#output box for the added binary number
self.stringvar_combined = tkinter.StringVar()
txt_output = tkinter.Entry(self._window, textvariable=self.stringvar_combined, width=TxtMaxLen, state="readonly", font= f)#entry box set to readonly to act as a display box
txt_output.grid(row=4, column=1, pady=5)
#output box for if there was or was not an overflow
self._stringvar_overflow = tkinter.StringVar()
txt_output = tkinter.Entry(self._window, textvariable=self._stringvar_overflow, width=TxtMaxLen, state="readonly", font= f)#entry box set to readonly to act as a display box
txt_output.grid(row=5, column=1, pady=5)
separator = tkinter.ttk.Separator(self._window,orient=tkinter.VERTICAL)
separator.grid(row=3, column=2, pady=15)
prev_page_button = tkinter.Button(self._window, text="Previous Page", font=f, command=MainWindow)
prev_page_button.grid(row=5, column = 2, padx = 5,pady = 5)
def set_values(self, value):
self.stringvar_combined.set(value)
def addition_selection(self):
try:
bin_1 = self.input1.get().strip().replace(" ", "")
bin_2 = self.input2.get().strip().replace(" ", "")
bin_add = binary_addition(bin_1, bin_2)
self.set_values(bin_add)
print("bin_add:", bin_add)
except Exception as ex:
print("NOPE it aint workin", ex)
if __name__ == "__main__":
win = MainWindow()
win.mainloop()

make tkinter window shrink, as widgets are deleted

I'm making some program, where I input a bunch of stuff into an entry and it gets printed into a row. I also added a feature where you can delete a row. However, when I delete a row, the window does not shrink. The way I actually made the program was by having 2 frames; the main frame with the buttons and entries, and the output or text frame. When I delete a row, it actually appends the data from a list, deletes the frame and all the widgets and reprints the rows, but with out the row I deleted.
The issue with my code, is that when I delete a row, the rows that weren't deleted start to get smaller and compress and secondly, the bottom of the window doesn't move upwards, leaving a blank white space.
Any help would be appreciated, thanks.
actually appending, labelling and printing the row is in function append_entry() and my delete function is delete_row()
from tkinter import *
global main_window
def quit():
main_window.destroy()
def entry_labels():
leader_label = Label(main_frame, text = 'Customer Name')
leader_label.grid(column=0, row=0)
location_label = Label(main_frame, text = 'Receipt Number')
location_label.grid(column=0, row=1)
numcampers_label = Label(main_frame, text = 'Item Hired')
numcampers_label.grid(column=0, row=2)
weather_label = Label(main_frame, text = 'Number Hired')
weather_label.grid(column=0, row=3)
row_label = Label(main_frame, text= 'Row')
row_label.grid(column=3, row=2)
def button():
print_button = Button(main_frame, text = "Print Details", command = append_entry)
print_button.grid(column=3, row=1)
quit_button = Button(main_frame, text= "Quit", command=quit)
quit_button.grid(column=4, row=0)
delete_row_button = Button(main_frame, text = 'Delete Row', command = delete_row)
delete_row_button.grid(column=4, row=3)
def entry():
global name_entry
name_entry = Entry(main_frame)
name_entry.grid(column=1, row=0)
global receipt_entry
receipt_entry = Entry(main_frame)
receipt_entry.grid(column=1, row=1)
global hired_entry
hired_entry = Entry(main_frame)
hired_entry.grid(column=1, row=2)
global num_hired_entry
num_hired_entry = Entry(main_frame)
num_hired_entry.grid(column=1, row=3)
global delete_row_entry
delete_row_entry = Entry(main_frame)
delete_row_entry.grid(column=4, row=2)
def table_headers():
row_header = Label(main_frame, text='Row', font = 'Arial 10 bold')
row_header.grid(column=0, row=4)
customer_header = Label(main_frame, text='Customer Name', font = 'Arial 10 bold')
customer_header.grid(column=1, row=4)
receipt_header = Label(main_frame, text='Receipt Number', font = 'Arial 10 bold')
receipt_header.grid(column=3, row=4)
item_header = Label(main_frame, text='Item Hired', font = 'Arial 10 bold')
item_header.grid(column=2, row=4)
num_header = Label(main_frame, text='Number Hired', font = 'Arial 10 bold')
num_header.grid(column=4, row=4)
def append_entry():
global second_frame
second_frame = Frame(main_window)
second_frame.grid(column=0, row=6)
leader_error_var.set("")
location_error_var.set("")
numcamper_error_var.set("")
weather_error_var.set("")
global name_count
name_count = 0
global ROWS_ABOVE
ROWS_ABOVE = 6
try:
name_entry_str = str(name_entry.get())
hired_entry_str = str(hired_entry.get())
receipt_entry_int = str(receipt_entry.get())
num_hired_entry_int = str(num_hired_entry.get())
if len(name_entry.get()) != 0:
input_data_col1.append([name_entry_str])
input_data_col2.append([hired_entry_str])
input_data_col3.append([receipt_entry_int])
input_data_col4.append([num_hired_entry_int])
counters['total_entries'] += 1
print(input_data_col1)
print(input_data_col2)
print(input_data_col3)
print(input_data_col4)
while name_count < counters ['total_entries']:
global name
name = Label(second_frame, text=(input_data_col1[name_count][-1])) ##using -1 selects the latest entry in the list
name.grid(column=1, row=name_count + ROWS_ABOVE, padx=50)
item = Label(second_frame, text=(input_data_col2[name_count][-1]))
item.grid(column=2, row=name_count + ROWS_ABOVE, padx=50)
row = Label(second_frame, text=name_count)
row.grid(column=0, row=name_count + ROWS_ABOVE, padx=60)
receipt = Label(second_frame, text=(input_data_col3[name_count][-1]))
receipt.grid(column=3, row=name_count + ROWS_ABOVE, padx=50)
num = Label(second_frame, text=(input_data_col4[name_count][-1]))
num.grid(column=4, row= name_count + ROWS_ABOVE, padx=50)
name_count += 1
name_entry.delete(0,END)
receipt_entry.delete(0,END)
hired_entry.delete(0,END)
num_hired_entry.delete(0,END)
except:
leader_error_var.set("Check inputs")
#location_error_var.set("please enter a valid num")
#numcamper_error_var.set("numcamper error test")
weather_error_var.set("")
name_entry.delete(0,END)
receipt_entry.delete(0,END)
hired_entry.delete(0,END)
num_hired_entry.delete(0,END)
def delete_row():
user_del =int(delete_row_entry.get())
counters['total_entries'] -= 1
input_data_col1.pop(user_del)
input_data_col2.pop(user_del)
input_data_col3.pop(user_del)
input_data_col4.pop(user_del)
data = [input_data_col1,input_data_col2,input_data_col3,input_data_col4]
for widget in second_frame.winfo_children():
widget.destroy()
append_entry()
print(input_data_col1)
print(input_data_col2)
print(input_data_col3)
print(input_data_col4)
def error_prevention():
#leader_error_var.set("leader error test")
#location_error_var.set("location error test")
#numcamper_error_var.set("numcamper error test")
#weather_error_var.set("weather error test")
#weather_error_var.set("_______________")
leader_error = Label(main_frame, textvariable = leader_error_var, fg = 'red')
leader_error.grid(column=2, row=0)
location_error = Label(main_frame, textvariable = location_error_var, fg = 'red')
location_error.grid(column=2, row=1)
numcamper_error = Label(main_frame, textvariable = numcamper_error_var, fg = 'red', width = 13)
numcamper_error.grid(column=2, row=2)
weather_error = Label(main_frame, textvariable = weather_error_var, fg = 'red')
weather_error.grid(column=2, row=3)
def main():
global main_window
main_window = Tk()
global input_data_col1
input_data_col1 = []
global input_data_col2
input_data_col2 = []
global input_data_col3
input_data_col3 = []
global input_data_col4
input_data_col4 = []
global input_data
input_data = []
global main_frame
main_frame = Frame(main_window)
main_frame.grid(row=0,column=0)
global counters
counters = {'total_entries':0, 'name_count':0}
#global number
#number = {'total_entries':0}
def stringvars():
global location_error_var
location_error_var = StringVar()
location_error_var.set("")
global numcamper_error_var
numcamper_error_var = StringVar()
numcamper_error_var.set("")
global leader_error_var
leader_error_var = StringVar()
leader_error_var.set("")
global weather_error_var
weather_error_var = StringVar()
leader_error_var.set("")
stringvars()
entry_labels()
entry()
error_prevention()
button()
table_headers()
main()
main_window.mainloop()
Under the code
for widget in second_frame.winfo_children():
widget.destroy()
add this block of code
second_frame.pack()
it will be like this
for widget in second_frame.winfo_children():
widget.destroy()
second_frame.pack()
I hope this helps you
You can use
main_window.geometry("1200x800+100+100")
to change size and position of main_window. Here the window width will be 1200 and height will be 800, positioned 100px to the top left corner of screen. The +100+100 is optional.
You may add geometry() to delete_row callback to resize the frame. Though you might have to calculate the proper size after widget deletion.
As to "when I delete a row, the rows that weren't deleted start to get smaller and compress",
That's due to how grid layout works. If there are two widgets on the same grid row, the grid height will be equal the the higher widget height. When the higher widget is removed, the grid will resize to the smaller widget height and appear to 'shrink'.
To solve that, you can try add padding to the widget, the syntax is
widget.grid(column=1,row=1,padx=(10,10),pady=(10,10))
Alternatively, you may try other layout management: .place will give you absolute layout control. Or you can use pack and let tkinter decide the proper position.

Python tkinter - scrollbar of canvas cuts off frame content

I am currently writing a simple logbook application for my sailing holidays. Therefore I wanted to create a small gui for entering data in lines for the logbook.
I am using grid layout with frames and canvas for the main frame, to show a scroll bar, when there are to many lines.
Everything is looking fine, until the command
self.canvasCenter.configure(scrollregion=self.canvasCenter.bbox("all"))
is called. If I comment this line out, the canvas is using the full frame. If this line is active - the canvas is using only 50% of the available screen and date is cut off.
I tried to strip down the code to the necessary things (not using any additional files, configs, classes, etc.). So that it is executable stand alone:
#!/usr/bin/python
# -*- coding: iso-8859-1 -*-
import sys
if sys.version_info.major == 2:
# We are using Python 2.x
import Tkinter as tk
elif sys.version_info.major == 3:
# We are using Python 3.x
import tkinter as tk
#from lib import logBookData
#from config import *
#Titel of the application shown in title bar
appTitle = "My APP"
#Layout geometry (size of the window)
lWidth = 768
lHeight = 900
layoutGeometry = '900x768'
#Colours for foreground and background for labels and entry fields
#headerBG = "Light Blue"
headerBG = "white"
labelBG = "white"
labelFG = "black"
#Number of columns (keep in Sync with headers and width list below!)
cNumber = 14
#Column width for number of columns defined above
cWidth = [5,7,5,5,5,5,5,5,5,5,5,5,5,10]
cHeaders = ["C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "C10", "C11", "C12", "C13", "C14" ]
class logBookGUI(tk.Tk):
def __init__(self,parent):
tk.Tk.__init__(self,parent)
self.parent = parent
self.lineCount = 0
self.initialize()
def initialize(self):
#create a masterframe
self.masterFrame = tk.Frame(self, bg=headerBG, bd=4, relief="groove") #, width = lWidth, height = lHeight)
self.masterFrame.grid(sticky=tk.NSEW)
self.masterFrame.columnconfigure(cNumber, weight=1)
#create Headerframe for Standard Info
self.headerFrame = tk.Frame(self.masterFrame, bg=headerBG, bd=2, relief="groove")
self.headerFrame.grid(row=0, column=0, sticky=tk.NSEW)
self.headerFrame.columnconfigure(cNumber, weight=1)
#Label Bootsname
boatNameLabel = tk.Label(self.headerFrame, text="Bootsname", anchor="w", fg=labelFG,bg=labelBG)
boatNameLabel.grid(column=0, row=0, sticky=tk.NSEW)
#Field Bootsname
self.boatName = tk.StringVar()
self.boatNameEntry = tk.Entry(self.headerFrame,textvariable=self.boatName, width = cWidth[0])
self.boatNameEntry.grid(column=1,row=0, sticky=tk.NSEW)
self.boatName.set(u"Solva")
for i in xrange(cNumber-2):
button = tk.Button(self.headerFrame,text=u"T %s" %i, command=self.OnButtonClick)
button.grid(column=i,row=0, sticky=tk.NSEW)
button = tk.Button(self.headerFrame,text=u"TEST", command=self.OnButtonClick)
button.grid(column=cNumber-2,row=0, sticky=tk.NSEW)
button = tk.Button(self.headerFrame,text=u"Neue Zeile", command=self.newLineClick)
button.grid(column=cNumber-1,row=1, sticky=tk.NSEW)
#create center frame
self.centerFrame = tk.Frame(self.masterFrame, bg=headerBG, bd=2, relief="groove")
self.centerFrame.grid(row=1, column=0, sticky=tk.NSEW)
self.centerFrame.columnconfigure(cNumber, weight=1)
#create a canvas for center frame
self.canvasCenter = tk.Canvas(self.centerFrame)
self.canvasCenter.grid(row=0, column=0, sticky=tk.NSEW)
self.canvasCenter.columnconfigure(cNumber, weight=1)
# Create a vertical scrollbar linked to the canvas.
vsbar = tk.Scrollbar(self.centerFrame, orient=tk.VERTICAL, command=self.canvasCenter.yview)
vsbar.grid(row=0, column=cNumber, sticky=tk.NSEW)
self.canvasCenter.configure(yscrollcommand=vsbar.set)
# Create a frame on the canvas to contain the content.
self.contentFrame = tk.Frame(self.canvasCenter, bg="Red", bd=2, relief="groove")
self.contentFrame.grid(row=0, column=0, sticky=tk.NSEW)
self.contentFrame.columnconfigure(cNumber, weight=1)
column = 0
for header in cHeaders:
label = tk.Label(self.contentFrame, text=header, anchor="w", fg=labelFG,bg=labelBG,borderwidth=2,relief="groove", width = cWidth[column])
label.grid(column = column, row=1, sticky="EW")
column += 1
self.addLine()
# Create canvas window to hold the centerframe.
self.canvasCenter.create_window((0,0), window=self.contentFrame, anchor=tk.NW)
# Update buttons frames idle tasks to let tkinter calculate buttons sizes
self.contentFrame.update_idletasks()
self.canvasCenter.configure(scrollregion=self.canvasCenter.bbox("all"))
self.grid_columnconfigure(cNumber,weight=1)
self.resizable(True,False)
self.update()
self.geometry("%sx%s" % (lHeight, lWidth))
def newLineClick(self):
self.addLine()
def OnButtonClick(self):
pass
def OnPressEnter(self,event):
pass
def addLine(self):
tkLine = []
try:
for i in xrange(cNumber):
self.entryVar = tk.StringVar()
self.entry = tk.Entry(self.contentFrame,textvariable=self.entryVar,width=cWidth[i])
self.entry.grid(column=i, row = self.lineCount + 3, sticky='EW')
tkLine.append(self.entryVar)
self.canvasCenter.configure(scrollregion=self.canvasCenter.bbox("all"))
except IndexError:
print ("Error in config file. Number of columns and given columns do not match - check file config.py")
sys.exit()
self.update()
self.lineCount += 1
if __name__ == "__main__":
app = logBookGUI(None)
app.title(appTitle)
app.mainloop()
Could someone give me an advice how to use scrollregion, so that the whole size of the frame is used?
Many thanks,
Gernot

Multiple commands to a button in tkinter

I'm a car park admittance thingy for college. Basically what is does when it's run, a window comes up, asks the user to enter make, model, colour, and reg plate. Then it saves this data to a list, array or whatever. The user presses a button to enter their car into the car park, and also to see what cars are currently in the car park. When I press admit vehicle, I need the data to be saved to this list/array/tree, and also for a integer variable to decrease by one. Here's the relevant code. There is more, but this is the relevant bits.
# Admit Button
btn_admit = ttk.Button(bottom_frame)
btn_admit.config(text='Admit Vehicle')
btn_admit.bind('<Button-1>', self.admit) # I need this to reduce the variable as well
...
def admit(self, event):
self.useful_msg.set("Vehicle Admitted") # This is only here to show a message currently it does nothing else
This is the 'spaces available' variable:
self.num_spaces = IntVar(mid_frame)
self.num_spaces.set = 0
lbl_num_spaces = Label(mid_frame)
lbl_num_spaces.config(textvariable=self.num_spaces, bg='yellow')
Finally, this is the code for the window that shows the tree of cars that are in the car park (with some example cars hard-coded for now):
class ShowCarsGui:
def __init__(self, master):
self.master = master
self.master.geometry('1200x600+100+100')
# Frames
top_frame = tk.Frame(self.master)
tree_container = tk.Frame(self.master)
bottom_bar = tk.Frame(self.master)
# Widgets:
# Logo
carpark_icon = tk.PhotoImage(file='car.gif')
lbl_carpark_icon = tk.Label(top_frame)
lbl_carpark_icon.config(image=carpark_icon)
lbl_carpark_icon.image = carpark_icon
# Header
lbl_header = tk.Label(top_frame)
lbl_header.config(text="Vehicles in car park", font='helvetica 32 bold')
# Tree(ttk)
self.tree = ttk.Treeview(tree_container)
self.tree["columns"] = ("Make", "Model", "Colour", "Registration")
self.tree["height"] = 10
self.tree["show"] = 'headings' # Gets rid of default first column
vsb = ttk.Scrollbar(tree_container)
vsb.configure(orient='vertical', command=self.tree.yview)
hsb = ttk.Scrollbar(tree_container)
hsb.configure(orient='horizontal', command=self.tree.xview)
self.tree.configure(yscroll=vsb.set, xscroll=hsb.set)
self.tree_populate()
# Button
quit_button = tk.Button(bottom_bar)
quit_button.config(text='Quit', width=25)
quit_button.bind('<Button-1>', self.close_window)
# Positioning frames
top_frame.grid_rowconfigure(0, minsize=150) # Make row 150 pixels high
top_frame.grid(row=0)
tree_container.grid(row=1)
bottom_bar.grid(row=2)
# Top
lbl_carpark_icon.grid(row=0, column=0, padx=10, sticky='w')
lbl_header.grid(row=0, column=1, padx=20)
# Middle
self.tree.grid(column=0, row=0, sticky='nsew')
vsb.grid(column=1, row=0, sticky='ns')
hsb.grid(column=0, row=1, sticky='ew')
# Bottom
quit_button.grid(row=0, column=0)
def close_window(self, event):
self.master.destroy()
def tree_populate(self):
# Eventually this needs to come from car park object
tree_columns = ("Make", "Model", "Colour", "Registration")
tree_data = [
("Ford", "Ka", "Blue", "FD54 2WE"),
("Vauxhall", "Corsa", "Green", "KJ61 9YH"),
("VW", "Polo", "Silver", "AA54 9TQ"),
("Nissan", "Qashqai", "Red", "YRE 456W"),
("Toyota", "Starlet", "Gold", "J234 WYE"),
]
for col in tree_columns:
self.tree.heading(col, text=col, anchor='w')
for country_data in tree_data:
self.tree.insert("", 0, values=country_data)
Finally here is the code for the entire program:
import tkinter as tk
from tkinter import *
from tkinter import ttk
class CarParkGui:
def __init__(self, master):
self.master = master
self.master.configure(bg='light cyan')
self.master.title("Collyer's Car Park")
self.master.option_add('*Font', 'Georgia 12') # Font for all widgets
self.master.option_add('*Font', 'helvetica 20 bold')
self.master.option_add('*Background', 'light cyan') # background of all widgets
self.master.geometry('1200x500+100+100') # w,h,x,y (top left corner)
self.top() # Build top bar
self.middle() # Define middle frame
self.bottom() # Define Bottom Frame
def top(self):
# Frame for top section
top_frame = Frame(self.master)
# Logo
carpark_icon = PhotoImage(file='car.gif')
lbl_carpark_icon = Label(top_frame) # Instance of tkinter label (parent is frame)
lbl_carpark_icon.config(image=carpark_icon)
lbl_carpark_icon.image = carpark_icon # Have to have this as well as previous one
# Header
lbl_header = Label(top_frame)
lbl_header.config(text='Admit Vehicle', font='helvetica 32 bold')
# Grid positioning for top frame
top_frame.grid_rowconfigure(0, minsize=150) # Make row 150 pixels high
top_frame.grid(row=0)
# Within Frame
lbl_carpark_icon.grid(row=0, column=0, padx=10)
lbl_header.grid(row=0, column=1, padx=20)
def middle(self):
# Frame to contain other widgets
mid_frame = Frame(self.master)
# Label - Car Make
lbl_make = Label(mid_frame)
lbl_make.config(text='Make') # Presentation
# Label - Car Model
lbl_model = Label(mid_frame)
lbl_model.config(text='Model')
# Label - Colour
lbl_colour = Label(mid_frame)
lbl_colour.config(text='Colour')
# Label - Registration
lbl_reg = Label(mid_frame)
lbl_reg.config(text='Registration')
# Label - Spaces
lbl_spc = Label(mid_frame)
lbl_spc.config(text='Spaces')
# Text Entry - Make
self.make = StringVar(mid_frame)
txt_make = Entry(mid_frame)
txt_make.config(textvariable=self.make, width=20)
# Text Entry - Model
self.model = StringVar(mid_frame)
txt_model = Entry(mid_frame)
txt_model.config(textvariable=self.model, width=20)
# Text Entry - Colour
self.colour = StringVar(mid_frame)
txt_colour = Entry(mid_frame)
txt_colour.config(textvariable=self.colour, width=20)
# Text Entry - Registration
self.reg = StringVar(mid_frame)
txt_reg = Entry(mid_frame)
txt_reg.config(textvariable=self.reg, width=20)
# Label for number of space available (WILL BE UPDATED)
self.num_spaces = IntVar(mid_frame)
self.num_spaces.set = 0
lbl_num_spaces = Label(mid_frame)
lbl_num_spaces.config(textvariable=self.num_spaces, bg='yellow')
# Display
mid_frame.grid_columnconfigure(0, minsize=100) # Make row 150 pixels high
mid_frame.grid(row=1, sticky='w')
# Row 0
lbl_make.grid(row=1, column=0)
txt_make.grid(row=1, column=1)
lbl_spc.grid(row=1, column=2)
lbl_num_spaces.grid(row=1, column=3, sticky='w')
# Row 1
lbl_model.grid(row=2, column=0)
txt_model.grid(row=2, column=1, padx='10')
# Row 2
lbl_colour.grid(row=3, column=0)
txt_colour.grid(row=3, column=1, padx='10')
# Row 3
lbl_reg.grid(row=4, column=0)
txt_reg.grid(row=4, column=1, padx='10')
def bottom(self):
# Frame for bottom section
bottom_frame = Frame(self.master)
# Grid reference for bottom frame
bottom_frame.grid(row=2)
# Guidance message (WILL BE UPDATED)
self.useful_msg = StringVar(bottom_frame)
self.useful_msg.set("Enter your vehicle details")
self.lbl_msg = Label(bottom_frame) # Use self so we can change config at
self.lbl_msg.config(textvariable=self.useful_msg, fg='red', width=20)
# Admit Button
btn_admit = ttk.Button(bottom_frame)
btn_admit.config(text='Admit Vehicle')
btn_admit.bind('<Button-1>', self.admit)
# Show Vehicles Button
btn_show = ttk.Button(bottom_frame)
btn_show.config(text='Show Vehicles In Car Park')
btn_show.bind('<Button-1>', self.show)
# Within bottom_frame
# row 0
self.lbl_msg.grid(row=0, column=0)
# row 1
btn_admit.grid(row=1, column=0, sticky='e')
btn_show.grid(row=1, column=1, sticky='e')
def admit(self, event):
self.useful_msg.set("Vehicle Admitted")
def show(self, event):
self.new_window = Toplevel(self.master)
self.app = ShowCarsGui(self.new_window)
___ This is where ShowCarsGui() is ____
def main():
root = Tk()
CarParkGui(root)
root.mainloop()
main()
To do "two things with one button", the correct solution is to create a function that does these "two things", and then associate that function with the button.
btn_admit = ttk.Button(bottom_frame)
btn.configure(command=self.admit)
def admit(self):
self.num_spaces.set(self.num_spaces.get()-1)
self.useful_msg.set("Vehicle Admitted")
Note: the above example uses the command attribute rather than a binding. Using the command attribute is preferable because it has built-in support for keyboard traversal. And, of course, you can do as much or as little as you want in admit -- you aren't limited to one or two things.

Categories