I guess this is a very beginners question. I am using Tkinter to get a GUI in Python. But with the code below the frames a displayed in the wrong order.
# ********************************************
# * Frame for toolbar *
# ********************************************
# Main frame
self.fr_toolbar = Frame(self)
self.fr_toolbar.pack(fill=X)
self.fr_toolbar.grid_columnconfigure(0, weight=1)
# Align toolbars left
self.fr_left = Frame(self.fr_toolbar, bd=1, relief = RAISED)
self.fr_left.grid(row=0, column=0, sticky=W+E)
# Align toolbars right
self.fr_right = Frame(self.fr_toolbar, bd = 1, relief = RAISED)
self.fr_right.grid(row=0, column=1, sticky=E)
# *********************************************
# * Communication toolbar *
# *********************************************
# Create Frame for comm toolbar
self.tb_comm = Frame(self.fr_left)
self.tb_comm.grid(row=0, column=0)
# Several buttons in here...
# ********************************************
# * Scope toolbar *
# ********************************************
# Seperator line
self.fr_split = Frame(self.fr_left, bd=1, relief=SUNKEN, width=2, height=28)
self.fr_split.grid(row=0, column=1)
# Create frame for scope toolbar
self.tb_scope = Frame(self.fr_left)
self.tb_scope.grid(row=0, column=2)
# Several buttons in here
# *********************************************
# * Exit tool bar *
# *********************************************
# Create frame for exit tool bar
self.tb_exit = Frame(self.fr_right)
self.tb_exit.pack()
# Exit button here
The idea was to have the exit toolbar on the right and all other toolbar on the left. So far it works. All toolbar frames inside fr_left are on the lefthand side. tb_exit in fr_right is on the right.
But inside fr_left I get the wrong order to show my toolbars. First I get tb_comm, then tb_scope and as third fr_split even if it is set between the other two toolbars. I tried it with pack() and with grid().
Can anybody explain what I am doing wrong? I thought at least with pack() I will get the widgets in the order I define them. And the same with grid(). I ordered them with column, but Python is ignoring it.
Florian
Ok, you are allowed to hit me! I set the buttons for tb_scope in tb_comm mistakenly. So all toolbars were on the right place but only the buttons were not. As I wrote, beginners fault...
Sorry to steal your time!
Related
I'm a tkinter newb, making slow progress. This question is about my parse_html func but I included relevant main code too. I can't figure out why the OK button isn't even showing up after the Listbox. While I do have a correct list of options showing up in my Listbox, and retval prints out the correct value, I'm stuck at this point because nothing is happening in the lbox section, and the ok_btn button is missing. I'm not even sure if one is causing the other. Any help would truly be appreciated. Thank you!
Updates: Noticed the banner Label also disappears when the Listbox is put in. Added function get_html_source_dirs to code below, just in case it's part of the problem.
Resolved: Added a max_len of choices to use as width of Listbox per Answer below. My problem was a symptom of Resizing tkinter listbox to width of largest item, using .grid
choices = StringVar(value=get_html_source_dirs())
max_len = max([len(c) for c in get_html_source_dirs()])
lbox = Listbox(master=new, listvariable=choices, width=max_len+2, selectmode=SINGLE)
## `test_write` is a decorator, just substitute `print`. Sorry...
def parse_html(win):
def get_html_source_dirs():
''' CACHED_DIR and CACHE_ARCHIVES are WindowsPath type'''
dirs = [CACHE_DIR.stem]
dirs.extend([name for name in os.listdir(CACHE_ARCHIVES) if (CACHE_ARCHIVES/name).is_dir()])
return dirs
def on_selection(event):
retval = [lbox.get(index) for index in lbox.curselection()]
# test_write(f"{retval}")
## ?? Assume I only need the one value for selectmode=SINGLE??
return retval[0]
def click_ok(event):
''' Will launch the parser with the selected directory as the argument '''
pass
new = Toplevel(win)
new.geometry("450x300")
new.title("Parse HTML")
banner = Label(master=new, text="Select source directory and click OK")
banner.grid(row=0, column=1)
choices = StringVar(value=get_html_source_dirs()) ## There are fewer than 10 options
lbox = Listbox(master=new, listvariable=choices, width=400, selectmode=SINGLE)
lbox.grid(row=2, column=1)
lbox.selection_set(0) # Default setting
## ?? Not sure if '<ButtonRelease>' or '<<ListboxSelection>>' is more correct??
lbox.bind('<ButtonRelease>', on_selection)
test_write(f"{lbox.get(lbox.curselection())}")
## ?? Not sure if I need this step??
# lbox.insert(0, *get_html_files())
## ?? This print statement only shows the very first time, not even after lbox.bind
# test_write(f"{lbox.index('active')}")
ok_btn = Button(master=new, text='OK')
ok_btn.grid(row=3, column=1)
ok_btn.configure(command=lambda: click_ok(win))
new.mainloop()
def main():
win = Tk()
frame1 = Frame(win)
frame1.pack(fill= BOTH, expand= True, padx= 10, pady=20)
... # other buttons
PARSE_HTML = Label(frame1, text='Process saved files:', padx=10, pady=10)
PARSE_HTML.grid(row=3, column=0, sticky=W)
process_button = Button(frame1, text='PROCESS HTML', padx=10, pady=5)
process_button.grid(row=3, column=1)
process_button.configure(command=lambda: parse_html(win))
... # more buttons
win.mainloop()
if __name__=="__main__":
main()
The problem is that you're setting the listbox width to 400. That's 400 characters, not pixels. On my machine that makes it about 3600 pixels wide. Since you don't use any other options when calling grid, the label and button will be centered, meaning they will be roughly at pixel position 1800.
Change the width to something more sane and the label and button should appear.
lbox = Listbox(master=new, listvariable=choices, width=40, selectmode=SINGLE)
# ^^
When I try to scroll down the Text object the scroll bar sort of bounces back instead of actually scrolling
memo_content = Text(edit_window,height = 4, width = 40)
memo_content.grid(row = 1, column = 0, sticky = E+W)
scroll_memo_content=Scrollbar(edit_window)
scroll_memo_content.grid(row = 1, column = 2, sticky = N+S)
memo_content.configure(yscrollcommand=scroll_memo_content.set)
The scroll bar has no control over the text field and when I drag it it snaps back to where it was meaning that the only way to scroll is by holding the up/down arrow and the cursor moving
from tkinter import *
root = Tk()
text = Text(root, height=25, width=50)
sb = Scrollbar(root)
text.configure(yscrollcommand=sb.set)
sb.configure(command=text.yview) # <- You missed this
sb.pack(side=RIGHT, fill=Y)
text.pack()
You missed configuring the actual scrollbar.
Configuring a scrollbar requires two steps:
You must configure the scrollbar to know which widget it is controlling. This is typically done by setting the command attribute of the scrollbar to be the yview command of a scrollable widget.
You must configure the window to be scrolled to know scrollbar needs to be updated when its position changes. This is typically done by setting the yscrollcommand or xscrollcomand of the widget to the set method of an appropriate scrollbar.
You are forgetting to do the first step.
Make the following change:
scroll_memo_content=Scrollbar(edit_window, command=memo_content.yview)
I've been creating an app where there are Clients that I can add to a table, the problem is, I need a scrollbar to scroll through all the clients since the app Height is limited and the clients aren't.
Using tkinter I found a way to create a "table" using Entry and grid, but what if I want to create 100 rows? they would be outside of the view, so that's why the need of a scrollbar.
For those of you who know Java, I wanted to create something similar to Jtable, it has a method to create row, delete row, and it generates automatically that scrollbar as soon as the JTable runs out of space.
I've tried to use TkTable from ttk and mess around with some properties, but I preferred how Entries look.
root = Tk()
root.geometry("1200x900")
for i in range(10):
e = Entry(relief=RIDGE)
e.grid(row=i, column=2, sticky=N)
root.mainloop()
I created a root = Tk() and used root to grid them.
You'll see 10 Entries on top of the other.
When a window contains many widgets, they might not all be visible. However, neither a window (Tk or Toplevel instance) nor a Entry are scrollable.
One solution to make the window content scrollable is to put all the widgets in a Frame, and then, embed this Frame in a Canvas using the create_window method.
from tkinter import *
root = Tk()
canvas = Canvas(root)
scroll_y = Scrollbar(root, orient="vertical", command=canvas.yview)
frame = Frame(canvas)
# group of widgets
for i in range(100):
e = Entry(frame, relief=RIDGE, width = 100)
e.grid(row=i, column=2, sticky=N)
# put the frame in the canvas
canvas.create_window(0, 0, anchor='nw', window=frame)
# make sure everything is displayed before configuring the scrollregion
canvas.update_idletasks()
canvas.configure(scrollregion=canvas.bbox('all'),
yscrollcommand=scroll_y.set)
canvas.pack(fill='both', expand=True, side='left')
scroll_y.pack(fill='y', side='right')
root.mainloop()
output:
Im using tkinter and trying to create a toolbar located on the left side going vertically, I already have a toolbar on the top of the frame filled in going horizontaly however can't figure out how to make a second one on the left, with all the buttons.
This is the code that I have:
infobar = Frame(master, bg="#ecf0f1", bd=1, relief=GROOVE)
infobar.pack(side=LEFT, fill=BOTH, expand=None)
infobarr = Label(toolbar, bg="#ecf0f1", text=' ')
infobarr.pack(side=LEFT, fill=Y)
poundToKgButton = Button(infobar, highlightbackground="#ecf0f1", image=eimg20, relief=FLAT, command=self.scale)
poundToKgButton.image = eimg20
createToolTip(poundToKgButton, "Conversion - Pound To KG")
poundToKgButton.pack(side=LEFT)
calculatorButton = Button(infobar, highlightbackground="#ecf0f1", image=eimg19, bd=1, relief=FLAT, command=self.calc)
calculatorButton.image = eimg19
createToolTip(calculatorButton, "Calculator")
calculatorButton.pack(side=LEFT, anchor="sw")
Use side=TOP if you want things stacked vertically. The containing widget has empty space. When you use pack, you're telling tkinter which side of that empty space to put the widget.
Here is a good example of exactly what happens when you call pack: http://tcl.tk/man/tcl8.5/TkCmd/pack.htm#M26
The problem I'm having is keeping my background colour when adding buttons to a frame, as soon as I run the module the background colour disappears, any help will be appreciated, thanks.
Heres me code:
import tkinter
import tkinter as tk
root = tk.Tk()
root.geometry('1000x600')
var=tk.StringVar()
Frame1 = tk.Frame(root)
Frame1.configure(background='light blue',height='300',width='500')
Frame1.grid(row='0',column='0')
Frame2 = tk.Frame(root)
Frame2.configure(background='grey',height='300',width='500')
Frame2.grid(row='0',column='1')
Frame3 = tk.Frame(root)
Frame3.configure(background='grey',height='300',width='500')
Frame3.grid(row='1',column='0')
Frame4 = tk.Frame(root)
Frame4.configure(background='light blue',height='300',width='500')
Frame4.grid(row='1',column='1')
def PrintOrder():
LabelOrder = tk.Label(Frame3,text="DONUT ORDER")
LabelOrder.grid(row='0',column='0')
return
Button1 = tk.Button(Frame1,text="Apple Cinnamon",height='2',width='15',command=PrintOrder).grid(row='0',column='0')
Button2 = tk.Button(Frame1,text="Strawberry",height='2',width='15',command=PrintOrder).grid(row='0',column='1')
Button3 = tk.Button(Frame1,text="Custard",height='2',width='15',command=PrintOrder).grid(row='0',column='2')
Button4 = tk.Button(Frame1,text="Sugar Ring",height='2',width='15',command=PrintOrder).grid(row='1',column='0')
Button5 = tk.Button(Frame1,text="Chocolate Caramel",height='2',width='15',command=PrintOrder).grid(row='1',column='1')
Button6 = tk.Button(Frame1,text="Lemon Circle",height='2',width='15',command=PrintOrder).grid(row='1',column='2')
Button7 = tk.Button(Frame1,text="Blueberry Blaster",height='2',width='15',command=PrintOrder).grid(row='2',column='0')
Button8 = tk.Button(Frame1,text="Strawberry Surprise",height='2',width='15',command=PrintOrder).grid(row='2',column='1')
Button9 = tk.Button(Frame1,text="Simple Sugar",height='2',width='15',command=PrintOrder).grid(row='2',column='2')
Label1 = tk.Label(Frame2,text="Donut special 6 for the price of 5").grid(row='0',column='0')
Button10 = tk.Button(Frame2,text="SPECIAL",height='5',width='20').grid(row='1',column='0')
root.mainloop()
Your frame still has its background color. You can see this pretty easily if you give it a distinct color so that it will show (eg: "red"), and add padding between the buttons (eg: tk.Button(...).grid(..., padx=10, pady=10). I think the only thing that is happening is that there is no space between the buttons for the color to show through, and the default behavior is for the frame to shrink (or grow) to fit its contents.
Other problems include the fact that you aren't giving any rows or columns a weight, so they won't grow or shrink as the main window grows an shrinks. Also, you don't have the sticky attribute set for the frames, so they won't fill the grid cell that they occupy. Add sticky="nsew" to where you grid the frames and you'll likely see more color.
A rule of thumb when using grid is to always set the sticky attribute for each item, and to give at least one row and one column a weight of 1 (one).
You can use grid_propagate(0) on your frames. With this, the frame's size is not adjusted to the widgets' size.
On your code, I used the next line to keep the size of Frame1:
Frame1.grid_propagate(0)
You can check this:
http://effbot.org/tkinterbook/grid.htm#Tkinter.Grid.grid_propagate-method
grid_propagate(flag) [#]
Enables or disables geometry propagation. When enabled, a grid manager connected to this widget attempts to change the size of the widget whenever a child widget changes size. Propagation is always enabled by default.
flag
True to enable propagation.