How to properly nest many ttk.PanedWindow one in other? - python

I want to make UI with 2 bars which may be grabbed with mouse and dragged to adjust widgets sizes.
Why nested ttk.PanedWindow are not displayed? What needs to be done to show labels 1 and 2 on screen in this example?
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
root.bind('<Escape>', lambda e:root.quit())
paned_v = ttk.PanedWindow(root, orient=tk.VERTICAL)
paned_v.add(tk.Label(root, text='1'))
paned_v.add(tk.Label(root, text='2'))
paned_v.pack(fill=tk.BOTH, expand=True)
paned_h = ttk.PanedWindow(root, orient=tk.HORIZONTAL)
paned_h.add(tk.Label(root, text='3'))
paned_h.add(paned_v)
paned_h.pack(fill=tk.BOTH, expand=True)
root.mainloop()

Panes need to be children of the paned widgets. If you want pane_v to be managed by paned_h then it needs to be a child of paned_h, and you shouldn't call paned_v.pack() since it is being managed by pane_h.
root = tk.Tk()
root.bind('<Escape>', lambda e:root.quit())
paned_h = ttk.PanedWindow(root, orient=tk.HORIZONTAL)
paned_h.pack(fill=tk.BOTH, expand=True)
paned_v = ttk.PanedWindow(paned_h, orient=tk.VERTICAL)
paned_v.add(tk.Label(paned_v, text='1'))
paned_v.add(tk.Label(paned_v, text='2'))
paned_h.add(tk.Label(paned_h, text='3'))
paned_h.add(paned_v)
Strictly speaking, this isn't required. However, this is the simplest way to make sure the stacking order is correct.

Related

Tkinter is there a way to resize the scroll bar in a canvas window

I have made most of this window already, and would prefer to not have to restart because of a hitch with a scrollbar not resizing properly. Problem being that the scrollbars appear way too small for the listboxes and I want them to span the whole height of each box respecitvely, but as of now they can only function if you spam the arrows as the actual scrolling bit can't move for lack of space. Any help would be appreciated, stuck on this for a while now. (Using python 3.8).
import tkinter as tk
from tkinter import *
setup = tk.Tk()
setup.title("Set Up Game")
setup.geometry("450x650")
setup.resizable(width=False, height=False)
select_Box = tk.Canvas(setup, width=450, height=496, bg="#cd3636")
select_Box.pack(padx=10)
listbox1 = Listbox(setup, width=33, height=30)
listbox1_win = select_Box.create_window(110,250, window=listbox1)
listbox2 = Listbox(setup, width=33, height=30)
listbox2_win = select_Box.create_window(320,250, window=listbox2)
scroll1 = Scrollbar(setup)
scroll1_win = select_Box.create_window(200,250, window=scroll1)
scroll2 = Scrollbar(setup)
scroll2_win = select_Box.create_window(410,250, window=scroll2)
listbox1.config(yscrollcommand = scroll1.set, selectmode=SINGLE)
scroll1.config(command = listbox1.yview)
listbox2.config(yscrollcommand = scroll2.set, selectmode=SINGLE)
scroll2.config(command = listbox2.yview)
nameArray = ["Bulbasaur", "Ivysaur", "Venasaur", "Charmander", "Charmelion", "Charazard", "Squirtle", "Wartortle", "Blastoise", "Lucario", "Garchomp", "Gengar", "Snorlax", "Reuniclus", "Joel","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder","placeholder"]
for item in nameArray:
listbox1.insert(END, item)
setup.mainloop()
If you want to use Canvas.create_window to place all of your widgets, all you have to do is define the height of your scrollbar (you may need to play around with the numbers a little to get it to the right size).
So the edited snippet from your code will be:
scroll1 = Scrollbar(setup)
scroll1_win = select_Box.create_window(200,
250,
height=480, # this is all you're missing!
window=scroll1)

Is there a way to call the amount of labels or widgets there are currently open in a TKinter window

so for example
window = Tk()
lbl = Label(text='abc')
lbl.place(x=1, y=1)
lbl2 = Label(text='ABC')
lbl2.place(x=2, y=2)
numOfWidgets = window.amount
print(str(numOfWidgets))
>>> 2
Does anyone know if there is a way to do this?
The method winfo_children will return the children of a given window. You can use that information to iterate through all of the widgets if you need a count of all widgets in an application.
In your case, len(window.winfo_children()) will return 2.

How can I place a widget at the very bottom of a tkinter window when positioning with .grid()?

I am aware that you cannot use different types of geometry managers within the same Tkinter window, such as .grid() and .pack(). I have a window that has been laid out using .grid() and I am now trying to add a status bar that would be snapped to the bottom of the window. The only method I have found online for this is to use .pack(side = BOTTOM), which will not work since the rest of the window uses .grid().
Is there a way that I can select the bottom of the window to place widgets from when using .grid()?
from tkinter import *
from tkinter.ttk import *
import tkinter as tk
class sample(Frame):
def __init__(self,master=None):
Frame.__init__(self, master)
self.status = StringVar()
self.status.set("Initializing")
statusbar = Label(root,textvariable = self.status,relief = SUNKEN, anchor = W)
statusbar.pack(side = BOTTOM, fill = X)
self.parent1 = Frame()
self.parent1.pack(side = TOP)
self.createwidgets()
def createwidgets(self):
Label(self.parent1,text = "Grid 1,1").grid(row = 1, column = 1)
Label(self.parent1,text = "Grid 1,2").grid(row = 1, column = 2)
Label(self.parent1,text = "Grid 2,1").grid(row = 2, column = 1)
Label(self.parent1,text = "Grid 2,2").grid(row = 2, column = 2)
if __name__ == '__main__':
root = Tk()
app = sample(master=root)
app.mainloop()
So using labels since I was kinda lazy to do other stuff, you can do frames to ensure that each section of your window can be packed/grid as required. Frames will be a useful tool for you to use when trying to arrange your widgets. Note that using a class can make things a little easier when deciding your parents. So imagine each frame is a parent and their children can be packed as required. So I would recommend drawing out your desired GUI and see how you will arrange them. Also if you want to add another frame within a frame simply do:
self.level2 = Frame(self.parent1)
You can check out additional settings in the docs
http://effbot.org/tkinterbook/frame.htm
PS: I am using a class hence the self, if you don't want to use classes then its okay to just change it to be without a class. Classes make it nicer to read though
Just give it a row argument that is larger than any other row. Then, give a weight to at least one of the rows before it.
Even better is to use frames to organize your code. Pack the scrollbar on the bottom and a frame above it. Then, use grid for everything inside the frame.
Example:
# layout of the root window
main = tk.Frame(root)
statusbar = tk.Label(root, text="this is the statusbar", anchor="w")
statusbar.pack(side="bottom", fill="x")
main.pack(side="top", fill="both", expand=True)
# layout of the main window
for row in range(1, 10):
label = tk.Label(main, text=f"R{row}")
label.grid(row=row, sticky="nsew")
main.grid_rowconfigure(row, weight=1)
...

Text widget to fill half of screen

I have a python tkinter program and i have two text widgets that need to be side by side. How can i get the two widgets to take up exactly half of the window which is 400 x 400 px ?
My Code is here
import tkinter
class MyApp:
def __init__(self):
self.root = tkinter.Tk()
self.root.title("App")
self.root.geometry("400x400")
self.root.update()
self.box1Text = tkinter.Text(self.root)
self.box1Text.pack(fill=tkinter.BOTH,side=tkinter.LEFT)
self.box2Text = tkinter.Text(self.root)
self.box2Text.pack(fill=tkinter.BOTH,side=tkinter.RIGHT)
App = MyApp()
By default the text widget wants to be 80 characters wide by 24 characters tall. If you don't specify a size, tkinter will do its best to make sure the widget is that size. If it can't fit all the widgets at their requested size it will start shrinking widgets, starting with the last widget created. That is why the first widget takes up all (or most) of the window -- tkinter tries to display it, and then will give any left-over room to the next widget. In this case there is zero left over room.
Since you are giving the window an explicit size, you can give the text widget a width and height of 1 (one), and then let the geometry manager expand the widget to fill the extra space. You also need to tell pack to let the widget expand. So, add width=1, height=1 to the definition of the widgets, and then add expand=True when packing the widgets in the frame.
Here is a working example:
import tkinter
class MyApp:
def __init__(self):
self.root = tkinter.Tk()
self.root.title("App")
self.root.geometry("400x400")
self.root.update()
self.box1Text = tkinter.Text(self.root, width=1, height=1)
self.box1Text.pack(fill=tkinter.BOTH,side=tkinter.LEFT, expand=True)
self.box2Text = tkinter.Text(self.root, width=1, height=1)
self.box2Text.pack(fill=tkinter.BOTH,side=tkinter.RIGHT, expand=True)
App = MyApp()
App.root.mainloop()

tkinter.ttk.Progressbar: How to change thickness of a horizontal bar

I am using the ttk.Progressbar in my app. I have scoured the net for an answer but no avail.
I have the following code which is working well. But I want to change the thickness of the bar.
progressbar = ttk.Progressbar(myGui, orient=HORIZONTAL,
length=400, mode="determinate",
variable=value_progress,
)
progressbar.pack()
I want the length to still be 400, but from the top of the bar to the bottom, I wish to decrease that so its half or less then half. (I want my bar on a diet, so to say)
But I am beating my head against the wall to figure out a solution.
Andy ideas? Thanks in advance.
The ttk progress bar appears to lack the width option in Python.
Using a work around (here) for an issue with a Tkinter Button. From this I have been able to create a working solution.
The key to solving the issue was to add the progress bar to a window inside the canvas. Using a window inside the canvas doesn't cause the canvas to resize when the widget is added which means we can control the width of the progress bar.
I have created some working example code:
from ttk import Progressbar
import Tkinter
class Example(Tkinter.Frame):
def __init__(self, parent):
Tkinter.Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
value_progress =50
self.parent.title("Progressbar Thingymawhatsit")
self.config(bg = '#F0F0F0')
self.pack(fill = Tkinter.BOTH, expand = 1)
#create canvas
canvas = Tkinter.Canvas(self, relief = Tkinter.FLAT, background = "#D2D2D2",
width = 400, height = 5)
progressbar = Progressbar(canvas, orient=Tkinter.HORIZONTAL,
length=400, mode="indeterminate",
variable=value_progress,
)
# The first 2 create window argvs control where the progress bar is placed
canvas.create_window(1, 1, anchor=Tkinter.NW, window=progressbar)
canvas.grid()
def main():
root = Tkinter.Tk()
root.geometry('500x50+10+50')
app = Example(root)
app.mainloop()
if __name__ == '__main__':
main()
So to sum up the progress bar is the same size but you just cant see half of it!
If you must use the xpnative theme or themes like it, then you will likely not have the option to change the thickness the conventional way. However if you use the default theme, you can configure the thickness with a style. There are likely other themes that let you do this as well, and if you're going to be playing around a lot with the look and feel of your program, you may wish to use these instead.
from Tkinter import *
from ttk import *
def main():
root = Tk()
s = Style()
s.theme_use("default")
s.configure("TProgressbar", thickness=50)
pb = Progressbar(root, style="TProgressbar")
pb.pack()
root.mainloop()
main()
You can just use the ipady option of pack manager.
progressbar = ttk.Progressbar(myGui, orient=HORIZONTAL,
length=400, mode="determinate",
variable=value_progress,
)
progressbar.pack(ipady=10)

Categories

Resources