Messed Up Spacing After Button Click in Tkinter - python

I'm trying to create a GUI using Tkinter for a Pip-boy from Fallout 3. I'm running into a problem where after I click a button in one of the frames, the spacing between the buttons gets messed up. This spacing change happens for all but one of the buttons in the frame (the Lockpick one).
This is what I want the button spacing to look like (what it looks like before I click a button):
This is what happens after I click a button (in this case the Barter one)
Here is the code I am using:
from tkinter import *
# to read descriptions of each skill from a text file
with open("skills.txt") as f:
lines = f.readlines()
# function that updates the label with a different description when the corresponding button is clicked
def show_skill_desc(index):
desc['text'] = lines[index-1]
# makes the window and frame
root = Tk()
root.geometry("1024x600")
root.title("Skills")
frame = Frame(root)
frame.grid()
# creates the label
Label(root, text="Stats").grid(row=0, column=0)
# list of skills which will each have their separate labels
skills_list = ["Barter", "Big Guns", "Energy Weapons", "Explosives", "Lockpick", "Medicine",
"Melee Weapons", "Repair", "Science", "Small Guns", "Sneak", "Speech", "Unarmed"]
# placing the label in the frame
desc = Label(root, text="", padx=30, wraplength=600, justify=LEFT)
desc.grid(row=2, column=1)
# creates a button for each skill
button_list = []
for i in range(12):
button_list.append(Button(root, text=skills_list[i], width=40,
height=2, command=lambda c=i: show_skill_desc(button_list[c].grid_info()['row']), padx=0, pady=0))
button_list[i].grid(row=i+1, column=0)
root.mainloop()
The purpose of the GUI is to display the description of each skill, when the button for a skill is clicked.
Does anyone know why the spacing change happens? Thank you!

Related

python issue adds a new label

take a look at this code
well it happens to just add another label for no reason
import tkinter
from tkinter import *
clicks = 1
def click_count():
global clicks
# making the label that shows how many idk you have
label = Label(frame, text="you have " + str(clicks), font=(('Courrier'), 32))
label.pack()
clicks += 1
label.pack()
#generating the window
root = tkinter.Tk()
root.geometry('500x500')
#making the expandable frame
frame = Frame(root)
frame.pack(expand=YES)
#making the button
button = Button(frame, text= "click", font=(('Courrier'), 32), command=click_count)
button.pack()
root.mainloop()
and then i tried this
and i also tried to remove the label.pack at the end
but it still does the same thing which is adding another label
import tkinter
from tkinter import *
clicks = 1
def click_count():
global clicks
# making the label that shows how many idk you have
label = Label(frame, text="you have " + str(clicks), font=(('Courrier'), 32))
label.pack()
label.destroy()
clicks += 1
label.pack()
#generating the window
root = tkinter.Tk()
root.geometry('500x500')
#making the expandable frame
frame = Frame(root)
frame.pack(expand=YES)
#making the button
button = Button(frame, text= "click", font=(('Courrier'), 32), command=click_count)
button.pack()
root.mainloop()
i was expecting it to add a nmber to the label but it just shows another label
It does not add a Label for no reason. It adds the label because that's what your function tells it to do. Each time you click the button, the function is executed that creates and packs a new Label.
What you should do is create the label at the onset and link it to a variable. Then, you can change the value of this variable in the function.
Also, you don't have to import tkinter twice and it's sensible to update the clicker first and then display the result instead of showing the last value it had. Your approach works in such a small program but the value of clicks will always be one higher than displayed. So, you may get into problems when you use the value.
from tkinter import *
def click_count():
global clicks
clicks += 1
click_counter.set("you have " + str(clicks))
#Initiate root
root = Tk()
root.geometry('500x500')
#Set initial values for click counter
clicks = 0
click_counter = StringVar()
click_counter.set("you have 0")
#making the expandable frame
frame = Frame(root)
frame.pack(expand=YES)
# making the label that shows how many idk you have
label = Label(frame, textvariable=click_counter, font=(('Courrier'), 32)) ## The label gets updated, whenever the value of click_counter changes.
label.pack()
#making the button
button = Button(frame, text= "click", font=(('Courrier'), 32), command=click_count)
button.pack()
root.mainloop()

Buttons not being placed inside the correct frame in tkinter

I am a newbie trying to use tkinter to build a GUI for an application. So far, I have a frame that I'd like to put several buttons into. However, every time I attempt to position this button, it isn't placed properly, being put outside of the frame itself. I wouldn't like to use the place function because of the several buttons I have to dynamically generate coming from an excel sheet so I was hoping to use the grid function instead.
Here is what I have so far
from tkinter import *
from customtkinter import *
window = Tk()
window.geometry("1920x1080")
window.state("zoomed")
window.title("My Company's Description Printer")
main_frame = CTkFrame(window, width=1920, height=1080, fg_color="grey21")
main_frame.place(x=0, y=0)
title = Label(main_frame,
text="My Company",
bg="grey21",
fg="white",
font=("Trajan Pro", 20)).place(x=626, y=30)
button_frame = CTkCanvas(main_frame,
width=800,
height=600,
highlightthickness=3,
highlightbackground="black",
relief="ridge",
bg="grey19").place(x=60, y=110)
test_button = CTkButton(button_frame, text="test").grid(row=0, column=0)
window.mainloop()
Example of code being ran
As you can see, the button is being placed in the top left corner of the entire window rather than the top left corner of the black bordered button frame. Any help would be appreciated. Thank you so much.
Note that button_frame is None because it is the result of .place(...), so the button (test_button is None as well due to same reason) is a child of the root window instead of the instance of CTkCanvas. .place(...) should be called in separate line.
Also .create_window() is used instead of tkinter layout manager to put widget into a canvas:
...
button_frame = CTkCanvas(main_frame,
width=800,
height=600,
highlightthickness=3,
highlightbackground="black",
relief="ridge",
bg="grey19")
# call .place(...) in separate line
button_frame.place(x=60, y=110)
test_button = CTkButton(button_frame, text="test") # don't use .grid(row=0, column=0)
# use .create_window() to put widget into canvas
button_frame.create_window(0, 0, window=test_button, anchor="nw")

How can I create a frame which holds an "empty" Message widget at a certain size

My problem with this code is that the text box that appears when you hover over a button resizes the entire window. What I want is for this frame to have the same size as the Text field which will place the information there.
import tkinter
import re
from tkinter import *
from tkinter import ttk, filedialog
from ctypes import windll
from tkinter.scrolledtext import ScrolledText
class MainApp(Tk):
def __init__(self):
super().__init__()
#self.geometry("500x300")
self.title("My Bioinformatics Toolbox")
def fasta_button_hover(hover):
instructions_field.config(text="This function will allow the user to open, "
"read and see the contents of a .FASTA file. It "
"will also allow the user to save their own sequence "
"as a FASTA file")
def fasta_button_leave(hover):
instructions_field.config(text="")
# Create the menu bar
menu_bar = tkinter.Menu(self)
file_menu = tkinter.Menu(menu_bar, tearoff=0)
# The File cascade in the menu bar
menu_bar.add_cascade(label="File", menu=file_menu)
file_menu.add_command(label="Exit", command=self.destroy)
self.config(menu=menu_bar)
# Create the buttons frame
button_frame = ttk.Frame(self)
#button_frame.configure(height=button_frame["height"], width=button_frame["width"])
#button_frame.grid_propagate(0)
button_frame.pack(side=LEFT)
# Create the text field frame
text_frame = ttk.Frame(self, width=500)
text_frame.pack(side=RIGHT)
# Create the instructions field
instructions_field = Message(text_frame, width=300)
instructions_field.grid(column=0, row=0)
# Create the buttons
fasta_button = ttk.Button(button_frame, text="Read FASTA", width=12)
fasta_button.bind("<Enter>", fasta_button_hover)
fasta_button.bind("<Leave>", fasta_button_leave)
fasta_button.grid(column=0, row=0)
dna_to_rna_button = ttk.Button(button_frame, text="DNA to RNA", width=12)
dna_to_rna_button.grid(column=0, row=1)
example_button = ttk.Button(button_frame, text="Example", width=12)
example_button.grid(column=0, row=2)
example_button2 = ttk.Button(button_frame, text="Example2", width=12)
example_button2.grid(column=0, row=3)
example_button3 = ttk.Button(button_frame, text="Example3", width=12)
example_button3.grid(column=0, row=4)
# A separator line ("rowspan" is set to a safe number of rows)
button_separator = ttk.Separator(button_frame, orient="vertical")
button_separator.grid(column=1, row=0, rowspan=100, sticky="ns")
# Fix for windows DPI scaling
windll.shcore.SetProcessDpiAwareness(1)
app = MainApp()
app.mainloop()
I want my window to have this size full size app at all time, but when I'm not hovering it has this size small puny app
My guess is that I'm missing something from the text_frame, but I can't figure it out...
Edit to update indentation
This amendment to your code seems to achieve this:
# Create the instructions field
instructions_field = ttk.Label(text_frame, width=50, wraplength=300)
instructions_field.grid(column=0, row=0)
The changes made:
Replaced the Message widget with a Label widget
Set both the width and wraplength keyword arguments in the Label
Manually adjusted the wraplength (taken as a number of pixels before wrapping) to fit all of the text inside the widget
It is arguably not the most elegant solution but a working and reliable one nonetheless
Note: The width keyword argument in the Label is taken as a number of characters wide

How to bind the pop up menu to a label in Tkinter

I want to limit the area where the popup menu can be triggered
My current code allows the popup menu be triggered anywhere in the tkinter window when the user right clicks
from tkinter import *
root = Tk()
w = Label(root, text="Right-click to display menu", width=40, height=20)
w.pack()
popup = Menu(root, tearoff=0)
popup.add_command(label="Next") # , command=next) etc...
popup.add_command(label="Previous")
popup.add_separator()
popup.add_command(label="Home")
def do_popup(event):
try:
popup.tk_popup(event.x_root, event.y_root, 0)
finally:
popup.grab_release()
w.bind("<Button-3>", do_popup)
b = Button(root, text="Quit", command=root.destroy)
b.pack()
mainloop()
I want the popup menu to be triggered when the user right clicks over the label "Right-click to display menu" only
Your code is working exactly as designed. You've created a really huge label widget (40 characters wide, 20 characters high, or roughly 350x325 pixels depending on your system font and resolution settings). So while you think you're clicking outside the label, you're not since it takes up the whole window.
To see what I mean, give your label a distinctive background color. For example:
w = Label(root, text="Right-click to display menu", width=40, height=20, background="pink")
The above results in a window that looks like the following image. Anywhere you click that is pink is part of the label, and thus will show the menu.

controlling the position of the scrollbar in Tkinter

Am trying to Learn Tkinter. The following program works fine. It creates a textbox and a scrolllbar connected to the textbox. There is also a button, which when pressed inserts a line into the text box. Now what I want to do is to move the scrollbar such that when the text goes beyond the first screen, the scrollbar automatically scrolls down so that the user sees only the last line of the input. What I want to do is for the text box to behave like an extended stats window, so that the user is able to see the progress so far. Right now the user will continuously have to scroll down by using the scrollbar to the last line. I want this process to be automatic. Is there a command which I can use to do that?
Thanks!
from Tkinter import *
root = Tk()
def checkFn():
text.insert(END, ' --> The check button has been pressed ... \n')
# ............... Frame 1 .................................
frame1 = Frame(root)
frame1.pack(side=TOP, fill=X)
b = Button(frame1, text='Check', command=checkFn)
b.pack(side=LEFT, padx=5)
# .............. Text Frame .................................
frame10 = Frame(root)
frame10.pack(side=TOP, fill=X)
scrlBar = Scrollbar(frame10)
scrlBar.pack(side=RIGHT, fill=Y)
text = Text(frame10, yscrollcommand=scrlBar.set)
text.insert(INSERT, 'abcd\n')
text.pack(side=LEFT, fill=BOTH)
scrlBar.config(command=text.yview)
root.mainloop()
The easiest method I would say is see(index)
In your checkFn() method, use text.see(END)

Categories