I'm trying to build a dashboard.
I want to keep the scrollbar just for the canvas area, but, on the contrary, it just stick to other frame.
The code I have written is this
# python dashboard
import tkinter as tk
from tkinter import *
class AutoScrollbar(Scrollbar):
def set(self, lo, hi):
if float(lo) <= 0.0 and float(hi) >= 1.0:
self.tk.call("grid", "remove", self)
else:
self.grid()
Scrollbar.set(self, lo, hi)
def pack(self, **kw):
raise TclError("cannot use pack with this widget")
def place(self, **kw):
raise TclError("cannot use place with this widget")
class Dashboard():
def __init__(self, root):
self.root=root
root.title("Dashboard View")
vsb = AutoScrollbar(root)
vsb.grid(row=0, column=1, sticky=N+S)
hsb = AutoScrollbar(root, orient=HORIZONTAL)
hsb.grid(row=1, column=0, sticky=E+W)
self.canvas=tk.Canvas(root,yscrollcommand=vsb.set,
xscrollcommand=hsb.set,background='blue')
vsb.config(command=self.canvas.yview)
hsb.config(command=self.canvas.xview)
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
self.canvas.grid(row=0,column=3)
frame = Frame(self.root, bd=2, relief=SUNKEN)
frame.grid(row=0,column=0, sticky="nw")
Button1=Button(frame,text="Status").grid(row = 0,column = 0, sticky = "we")
Button2=Button(frame,text="Processes").grid(row = 0,column = 1, sticky = "we")
Button3=Button(frame,text="Links").grid(row = 1,column = 0, sticky = "we")
Button4=Button(frame,text="Traffic").grid(row = 1,column = 1, sticky = "we")
Button5=Button(frame,text="App Version").grid(row = 2,column = 0, sticky = "we")
Button5=Button(frame,text="Archive/Purge").grid(row = 2,column = 1, sticky = "we")
fhandle = open("dashboard_content.txt")
lines = fhandle.read()
fhandle.close()
text1=self.canvas.create_text(400, 400, fill="white")
self.canvas.itemconfig(text1, text=lines)
frame.update_idletasks()
self.canvas.config(scrollregion=self.canvas.bbox("all"))
if __name__== '__main__':
root=tk.Tk()
board=Dashboard(root)
root.mainloop()
How can I get the scrollbar just for the Canvas window, "to the blue background"?
I'm guessing you mean you want to leave all of the blank space surrounding the canvas, but you want the scrollbar to "stick" to the side of the canvas.
The easiest way to accomplish that is to put both the canvas and the scrollbars in a frame so that they act as a single unit.
For example:
class Dashboard():
def __init__(self, root):
....
canvasContainer = tk.Frame(root)
self.canvas = tk.Canvas(canvasContainer, ...)
vsb = AutoScrollbar(canvasContainer, ...)
hsb = AutoScrollbar(canvasContainer, ...)
vsb.grid(row=0, column=0, sticky="ns")
hsb.grid(row=1, column=1, sticky="ew")
self.canvas.grid(row=0, column=1, sticky="nsew")
canvasContainer.grid_rowconfigure(0, weight=1)
canvasContainer.grid_columnconfigure(1, weight=0)
...
canvasContainer.grid(row=0, column=3)
...
#Bryan , Well all I wanted was to stick the scrollbar to the canvas area only and a sidebar on the left with buttons. With a bit modification to your suggestion I could manage to achieve. Here is what I did.
# python dashboard
import tkinter as tk
from tkinter import *
class AutoScrollbar(Scrollbar):
def set(self, lo, hi):
if float(lo) <= 0.0 and float(hi) >= 1.0:
self.tk.call("grid", "remove", self)
else:
self.grid()
Scrollbar.set(self, lo, hi)
def pack(self, **kw):
raise TclError("cannot use pack with this widget")
def place(self, **kw):
raise TclError("cannot use place with this widget")
class Dashboard():
def __init__(self, root):
self.root=root
root.title("Dashboard View")
canvasContainer = tk.Frame(root)
self.canvas=tk.Canvas(canvasContainer,background='blue')
fhandle = open("dashboard_content.txt")
lines = fhandle.read()
fhandle.close()
text1=self.canvas.create_text(400, 400, fill="white")
self.canvas.itemconfig(text1, text=lines)
vsb = AutoScrollbar(canvasContainer,orient=VERTICAL)
hsb = AutoScrollbar(canvasContainer, orient=HORIZONTAL)
vsb.grid(row=0, column=2, sticky="ns")
hsb.grid(row=1, column=1, sticky="ew")
self.canvas.grid(row=0,column=1,sticky="news")
self.canvas.config(yscrollcommand=vsb.set, xscrollcommand=hsb.set, scrollregion=self.canvas.bbox("all"))
vsb.config(command=self.canvas.yview)
hsb.config(command=self.canvas.xview)
canvasContainer.grid_rowconfigure(0, weight=1)
canvasContainer.grid_columnconfigure(0, weight=1)
canvasContainer.grid(row=0,column=3)
frame = Frame(self.root, bd=2, relief=SUNKEN)
frame.grid(row=0,column=0, sticky="nw")
Button1=Button(frame,text="Status").grid(row = 0,column = 0, sticky = "we")
Button2=Button(frame,text="Processes").grid(row = 0,column = 1, sticky = "we")
Button3=Button(frame,text="Links").grid(row = 1,column = 0, sticky = "we")
Button4=Button(frame,text="Traffic").grid(row = 1,column = 1, sticky = "we")
Button5=Button(frame,text="App Version").grid(row = 2,column = 0, sticky = "we")
Button5=Button(frame,text="Archive/Purge").grid(row = 2,column = 1, sticky = "we")
self.canvas.update_idletasks()
# Grid.columnconfigure(self.root,1,weight=1, minsize=100)
if __name__== '__main__':
root=tk.Tk()
board=Dashboard(root)
root.mainloop()
Any idea, How can I make the complete window expandable.
Related
I've created a simple GUI application with Tkinter.
I have two questions about this code:
import tkinter as tk
from tkinter import ttk, Grid, Frame, N, S, W, E, StringVar, Label, Entry, RAISED, Button, Checkbutton, Scrollbar
class mainApp(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.scan_button = Button(self.parent, text="Scan", command=self.scan_wifi)
self.forget_button = Button(self.parent, text="Forget", command=self.forget_wifi)
self.reboot_button = Button(self.parent, text="Reboot", command=self.reboot)
frame=Frame(self.parent)
Grid.rowconfigure(self.parent, 0, weight=1)
Grid.columnconfigure(self.parent, 0, weight=1)
frame.grid(row=0, column=0, sticky=N+S+E+W)
grid=Frame(self.parent)
grid.grid(sticky=N+S+E+W, column=0, row=7, columnspan=2)
Grid.rowconfigure(self.parent, 7, weight=1)
Grid.columnconfigure(self.parent, 0, weight=1)
headings=('Name', 'Address', 'Quality', 'Channel', 'Signal Level', 'Encryption')
row=[]
rows=[]
self.table = ttk.Treeview(show="headings", selectmode="browse")
self.table["columns"]=headings
self.table["displaycolumns"]=headings
for head in headings:
self.table.heading(head, text=head, anchor=tk.CENTER)
self.table.column(head, width=30, anchor=tk.CENTER)
self.scrolltable = tk.Scrollbar(command=self.table.yview)
self.table.configure(yscrollcommand=self.scrolltable.set)
self.scrolltable.grid(row=1, column=100, sticky=N+S)
for row in rows:
self.table.insert('', tk.END, values=tuple(row))
self.table.bind('<ButtonRelease-1>', self.OnRelease)
self.table.grid(row=0, rowspan=14, columnspan = 21, sticky=N+S+E+W)
self.scan_button.grid(row=15, column = 1, columnspan = 1, sticky=N+S+E+W)
self.forget_button.grid(row=15, column = 0, columnspan = 1 , sticky=N+S+E+W)
self.reboot_button.grid(row=15, column = 3, columnspan = 1 , sticky=N+S+E+W)
def OnRelease(self, event):
pass
def scan_wifi(self):
pass
def forget_wifi(self):
pass
def reboot(self):
pass
root=tk.Tk()
app = mainApp(root)
root.mainloop()
1) How I can move the button on the top of the window?
2) Why, if I resize the window, the button "Forget" becomes bigger than other buttons? How I can make all buttons identical size?
Because you have called columnconfigure(0, weight=1) only, therefore only the Forget button will be resized when the window is resized.
To move the buttons to the top of the window, you need to rearrange the buttons to row=0 and the Treeview to row=1. Below is modified code based on yours:
import tkinter as tk
from tkinter import ttk
class mainApp(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
# buttons
self.forget_button = tk.Button(self.parent, text='Forget', command=self.forget_wifi)
self.scan_button = tk.Button(self.parent, text='Scan', command=self.scan_wifi)
self.reboot_button = tk.Button(self.parent, text='Reboot', command=self.reboot)
self.forget_button.grid(row=0, column=0, sticky='nsew')
self.scan_button.grid(row=0, column=1, sticky='nsew')
self.reboot_button.grid(row=0, column=2, sticky='nsew')
# make the 3 buttons same width
for i in range(3):
self.parent.columnconfigure(i, weight=1)
# make the treeview and the scrollbar to fill the remaining space
self.parent.rowconfigure(1, weight=1)
# treeview and scrollbar
frame = tk.Frame(self.parent)
frame.grid(row=1, column=0, columnspan=3, sticky='nsew')
headings=('Name', 'Address', 'Quality', 'Channel', 'Signal Level', 'Encryption')
self.table = ttk.Treeview(frame, show='headings', selectmode='browse')
self.table['columns'] = headings
self.table['displaycolumns'] = headings
for head in headings:
self.table.heading(head, text=head, anchor=tk.CENTER)
self.table.column(head, width=30, anchor=tk.CENTER)
self.scrolltable = tk.Scrollbar(frame, command=self.table.yview)
self.table.configure(yscrollcommand=self.scrolltable.set)
self.table.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
self.scrolltable.pack(side=tk.RIGHT, fill=tk.Y)
self.table.bind('<ButtonRelease-1>', self.OnRelease)
def OnRelease(self, event):
pass
def scan_wifi(self):
pass
def forget_wifi(self):
pass
def reboot(self):
pass
root=tk.Tk()
app = mainApp(root)
root.mainloop()
I have a canvas which is opening in the main window, but I want it to open in the canvas inside the Frame. I tried to add some crazy (as they're in my hands) attributes, but they didn't solve my problem, so I deleted them and have no idea what to do. Could you help me - how could I realize it?
code:
from tkinter import *
from tkinter import ttk
class Example(Frame): # Frame
def __init__(self, root):
Frame.__init__(self, root)
# ---- for grid ----
canvas_width = 1000 # MUTABLE!!!
canvas_height = 1000 # MUTABLE!!!
self.canvas_width = canvas_width
self.canvas_height = canvas_height
self.warehouse_canvas = Canvas(self, width=canvas_width, height=canvas_height, background="bisque")
self.x_scrollbar = Scrollbar(self, orient="horizontal", command=self.warehouse_canvas.xview)
self.y_scrollbar = Scrollbar(self, orient="vertical", command=self.warehouse_canvas.yview)
self.warehouse_canvas.configure(yscrollcommand=self.y_scrollbar.set, xscrollcommand=self.x_scrollbar.set)
self.warehouse_canvas.configure(scrollregion=(0, 0, canvas_width, canvas_height)) # c_w c_h изменены
self.x_scrollbar.grid(row=1, column=0, sticky="ew")
self.y_scrollbar.grid(row=0, column=1, sticky="ns")
self.warehouse_canvas.grid(row=0, column=0, sticky="nsew")
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
root.columnconfigure(1, weight=1)
root.rowconfigure(1, weight=1)
self.warehouse_canvas.bind("<ButtonPress-1>", self.scroll_start)
self.warehouse_canvas.bind("<B1-Motion>", self.scroll_move)
self.draw_grid(self.warehouse_canvas, canvas_width, canvas_height) # !!!
def draw_grid(root, warehouse_canvas, canvas_width, canvas_height):
for line in range(0, canvas_width, 10):
warehouse_canvas.create_line([(line, 0), (line, canvas_height)], fill='#d9d9d9', tags='grid_line_width')
for line in range(0, canvas_width, 10):
warehouse_canvas.create_line([(0, line), (canvas_width, line)], fill='#d9d9d9', tags='grid_line_height')
# warehouse_canvas.grid(row=0, column=0) color : #d9d9d9
def scroll_start(self, event):
self.warehouse_canvas.scan_mark(event.x, event.y)
def scroll_move(self, event):
self.warehouse_canvas.scan_dragto(event.x, event.y, gain=1)
if __name__ == "__main__":
root = Tk()
Example(root).grid(row=0, column=0, # положение
columnspan=2, rowspan=2, sticky="nsew")
root.mainloop()
What I have
Where I want it to be
Is there any way to do it without rewriting the whole code?
I'm trying to create a canvas widget with a number of widgets embedded within it. Since there will frequently be too many widgets to fit in the vertical space I have for the canvas, it'll need to be scrollable.
import tkinter as tk # for general gui
import tkinter.ttk as ttk # for notebook (tabs)
class instructionGeneratorApp():
def __init__(self, master):
# create a frame for the canvas and scrollbar
domainFrame = tk.LabelFrame(master)
domainFrame.pack(fill=tk.BOTH, expand=1)
# make the canvas expand before the scrollbar
domainFrame.rowconfigure(0,weight=1)
domainFrame.columnconfigure(0,weight=1)
vertBar = ttk.Scrollbar(domainFrame)
vertBar.grid(row=0, column=1, sticky=tk.N + tk.S)
configGridCanvas = tk.Canvas(domainFrame,
bd=0,
yscrollcommand=vertBar.set)
configGridCanvas.grid(row=0, column=0, sticky=tk.N + tk.S + tk.E + tk.W)
vertBar.config(command=configGridCanvas.yview)
# add widgets to canvas
l = tk.Label(configGridCanvas, text='Products')
l.grid(row=1, column=0)
r = 2
for product in ['Product1','Product2','Product3','Product4','Product5','Product6','Product7','Product8','Product9','Product10','Product11','Product12','Product13','Product14','Product15','Product16','Product17','Product18','Product19','Product20']:
l = tk.Label(configGridCanvas, text=product)
l.grid(row=r, column=0)
c = tk.Checkbutton(configGridCanvas)
c.grid(row=r, column=1)
r += 1
ButtonFrame = tk.Frame(domainFrame)
ButtonFrame.grid(row=r, column=0)
removeServerButton = tk.Button(ButtonFrame, text='Remove server')
removeServerButton.grid(row=0, column=0)
# set scroll region to bounding box?
configGridCanvas.config(scrollregion=configGridCanvas.bbox(tk.ALL))
root = tk.Tk()
mainApp = instructionGeneratorApp(root)
root.mainloop()
As best as I can tell, I'm following the effbot pattern for canvas scrollbars, but I end up with either a scrollbar that isn't bound to the canvas, or a canvas that is extending beyond the edges of its master frame:
I've attempted the solutions on these questions, but there's still something I'm missing:
resizeable scrollable canvas with tkinter
Tkinter, canvas unable to scroll
Any idea what I'm doing wrong?
I have added some comments to #The Pinapple 's solution for future reference.
from tkinter import *
class ProductItem(Frame):
def __init__(self, master, message, **kwds):
Frame.__init__(self, master, **kwds)
self.grid_rowconfigure(1, weight=1)
self.grid_columnconfigure(0, weight=1)
self.text = Label(self, text=message, anchor='w')
self.text.grid(row=0, column=0, sticky='nsew')
self.check = Checkbutton(self, anchor='w')
self.check.grid(row=0, column=1)
class ScrollableContainer(Frame):
def __init__(self, master, **kwargs):
#our scrollable container is a frame, this frame holds the canvas we draw our widgets on
Frame.__init__(self, master, **kwargs)
#grid and rowconfigure with weight 1 are used for the scrollablecontainer to utilize the full size it can get from its parent
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
#canvas and scrollbars are positioned inside the scrollablecontainer frame
#the scrollbars take a command parameter which is used to position our view on the canvas
self.canvas = Canvas(self, bd=0, highlightthickness=0)
self.hScroll = Scrollbar(self, orient='horizontal',
command=self.canvas.xview)
self.hScroll.grid(row=1, column=0, sticky='we')
self.vScroll = Scrollbar(self, orient='vertical',
command=self.canvas.yview)
self.vScroll.grid(row=0, column=1, sticky='ns')
self.canvas.grid(row=0, column=0, sticky='nsew')
#We do not only need a command to position but also one to scroll
self.canvas.configure(xscrollcommand=self.hScroll.set,
yscrollcommand=self.vScroll.set)
#This is the frame where the magic happens, all of our widgets that are needed to be scrollable will be positioned here
self.frame = Frame(self.canvas, bd=2)
self.frame.grid_columnconfigure(0, weight=1)
#A canvas itself is blank, we must tell the canvas to create a window with self.frame as content, anchor=nw means it will be positioned on the upper left corner
self.canvas.create_window(0, 0, window=self.frame, anchor='nw', tags='inner')
self.product_label = Label(self.frame, text='Products')
self.product_label.grid(row=0, column=0, sticky='nsew', padx=2, pady=2)
self.products = []
for i in range(1, 21):
item = ProductItem(self.frame, ('Product' + str(i)), bd=2)
item.grid(row=i, column=0, sticky='nsew', padx=2, pady=2)
self.products.append(item)
self.button_frame = Frame(self.frame)
self.button_frame.grid(row=21, column=0)
self.remove_server_button = Button(self.button_frame, text='Remove server')
self.remove_server_button.grid(row=0, column=0)
self.update_layout()
#If the widgets inside the canvas / the canvas itself change size,
#the <Configure> event is fired which passes its new width and height to the corresponding callback
self.canvas.bind('<Configure>', self.on_configure)
def update_layout(self):
#All pending events, callbacks, etc. are processed in a non-blocking manner
self.frame.update_idletasks()
#We reconfigure the canvas' scrollregion to fit all of its widgets
self.canvas.configure(scrollregion=self.canvas.bbox('all'))
#reset the scroll
self.canvas.yview('moveto', '1.0')
#fit the frame to the size of its inner widgets (grid_size)
self.size = self.frame.grid_size()
def on_configure(self, event):
w, h = event.width, event.height
natural = self.frame.winfo_reqwidth() #natural width of the inner frame
#If the canvas changes size, we fit the inner frame to its size
self.canvas.itemconfigure('inner', width=w if w > natural else natural)
#dont forget to fit the scrollregion, otherwise the scrollbar might behave strange
self.canvas.configure(scrollregion=self.canvas.bbox('all'))
if __name__ == "__main__":
root = Tk()
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
sc = ScrollableContainer(root, bd=2)
sc.grid(row=0, column=0, sticky='nsew')
root.mainloop()
From what I can tell you are looking for something like this..
from tkinter import *
class ProductItem(Frame):
def __init__(self, master, message, **kwds):
Frame.__init__(self, master, **kwds)
self.grid_rowconfigure(1, weight=1)
self.grid_columnconfigure(0, weight=1)
self.text = Label(self, text=message, anchor='w')
self.text.grid(row=0, column=0, sticky='nsew')
self.check = Checkbutton(self, anchor='w')
self.check.grid(row=0, column=1)
class ScrollableContainer(Frame):
def __init__(self, master, **kwargs):
Frame.__init__(self, master, **kwargs) # holds canvas & scrollbars
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
self.canvas = Canvas(self, bd=0, highlightthickness=0)
self.hScroll = Scrollbar(self, orient='horizontal',
command=self.canvas.xview)
self.hScroll.grid(row=1, column=0, sticky='we')
self.vScroll = Scrollbar(self, orient='vertical',
command=self.canvas.yview)
self.vScroll.grid(row=0, column=1, sticky='ns')
self.canvas.grid(row=0, column=0, sticky='nsew')
self.canvas.configure(xscrollcommand=self.hScroll.set,
yscrollcommand=self.vScroll.set)
self.frame = Frame(self.canvas, bd=2)
self.frame.grid_columnconfigure(0, weight=1)
self.canvas.create_window(0, 0, window=self.frame, anchor='nw', tags='inner')
self.product_label = Label(self.frame, text='Products')
self.product_label.grid(row=0, column=0, sticky='nsew', padx=2, pady=2)
self.products = []
for i in range(1, 21):
item = ProductItem(self.frame, ('Product' + str(i)), bd=2)
item.grid(row=i, column=0, sticky='nsew', padx=2, pady=2)
self.products.append(item)
self.button_frame = Frame(self.frame)
self.button_frame.grid(row=21, column=0)
self.remove_server_button = Button(self.button_frame, text='Remove server')
self.remove_server_button.grid(row=0, column=0)
self.update_layout()
self.canvas.bind('<Configure>', self.on_configure)
def update_layout(self):
self.frame.update_idletasks()
self.canvas.configure(scrollregion=self.canvas.bbox('all'))
self.canvas.yview('moveto', '1.0')
self.size = self.frame.grid_size()
def on_configure(self, event):
w, h = event.width, event.height
natural = self.frame.winfo_reqwidth()
self.canvas.itemconfigure('inner', width=w if w > natural else natural)
self.canvas.configure(scrollregion=self.canvas.bbox('all'))
if __name__ == "__main__":
root = Tk()
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
sc = ScrollableContainer(root, bd=2)
sc.grid(row=0, column=0, sticky='nsew')
root.mainloop()
I'm placing a scrollbar in a frame, and the frame in a widget. The frame has a label above it. The label above has three columns. The frame with the scrollbar has three columns. I can't get the three columns in the frame and above the frame to line up.
I saw and used the following two questions to get this far, but got stuck.
Adding a scrollbar to a group of widgets in Tkinter
Tkinter - Add scrollbar for each LabelFrame
Any help on lining up the columns would be greatly appreciated. Thanks.
Here's a picture of the widget without the lined up columns:
Columns don't line up
Here's a MWE:
import tkinter as tk
class Selections(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
self.FifthLabelLeft = tk.Label(self,
text="""Riding""",
justify = tk.CENTER,
padx = 10).grid(row=4, column = 0, pady=5)
self.FifthLabelCenter = tk.Label(self,
text="""Winning Candidate""",
justify = tk.CENTER,
padx = 10).grid(row=4, column = 1, pady=5)
self.FifthLabelRight = tk.Label(self,
text="""Percent of Vote""",
justify = tk.CENTER,
padx = 10).grid(row=4, column = 2, pady=5)
mybox = tk.LabelFrame(self, padx=5, pady=4)
mybox.grid(row=5, column=0, columnspan=3)
canvas = tk.Canvas(mybox, borderwidth=5, background="#70ff33")
frame = tk.Frame(canvas, background="#33f4ff")
vsb = tk.Scrollbar(mybox, orient="vertical", command=canvas.yview)
canvas.configure(yscrollcommand=vsb.set, width=450, heigh=50)
vsb.pack(side="right", fill="y")
canvas.pack(side="left", fill="both", expand=True)
canvas.create_window((4,4), window=frame, anchor="nw", tags="frame")
# be sure that we call OnFrameConfigure on the right canvas
frame.bind("<Configure>", lambda event: self.OnFrameConfigure(canvas))
self.fillWindow(frame)
self.QuitButton = tk.Button(self,
text="QUIT",
command=root.destroy,
padx=25, pady=0)
self.QuitButton.grid(column = 0, columnspan=3)
def fillWindow(self, frame):
PartyWinnersList = [['Some list of places', "Somebody's Name", 0.37448599960838064],
['A shorter list', 'Three Long Names Here', 0.52167817821240514],
['A much longer, longer entry', 'Short Name', 0.41945832387008858]]
placement = 2
for i in PartyWinnersList:
ShowYear = tk.Label(frame,
text="""%s """ % i[0]
)
ShowYear.grid(row=placement, column = 0, sticky=tk.S)
ShowSystem = tk.Label(frame,
text="""%s """ % i[1]
)
ShowSystem.grid(row=placement, column = 1, sticky=tk.N)
PercentVotes = i[2]*100
ShowVotes = tk.Label(frame,
text="""%3.1f""" % PercentVotes
)
ShowVotes.grid(row=placement, column = 2, sticky=tk.N)
placement += 1
def OnFrameConfigure(self, canvas):
canvas.configure(scrollregion=canvas.bbox("all"))
if __name__ == "__main__":
root = tk.Tk()
main = Selections(root)
main.pack(side="top", fill="both", expand=True)
root.mainloop()
The two defined functions are in the class, but I couldn't get them to line up.
I usually don't solve the problem this way, but you can force alignment by specifying label widths. The problem will come when you dynamically resize the window -- everything is static. But this will solve the specified problem -- alignment.
import tkinter as tk
class Selections(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
self.FifthLabelLeft = tk.Label(self,
text="""Riding""",
justify = tk.CENTER,
width=25,padx = 10)
self.FifthLabelLeft.grid(row=4, column = 0, pady=5)
self.FifthLabelCenter = tk.Label(self,
text="""Winning Candidate""",
justify = tk.CENTER,
width=25,
padx = 10).grid(row=4, column = 1, pady=5)
self.FifthLabelRight = tk.Label(self,
text="""Percent of Vote""",
justify = tk.CENTER,
padx = 10).grid(row=4, column = 2, pady=5)
mybox = tk.LabelFrame(self, padx=5, pady=4)
mybox.grid(row=5, column=0, columnspan=3)
canvas = tk.Canvas(mybox, borderwidth=5, background="#70ff33")
frame = tk.Frame(canvas, background="#33f4ff")
# frame.grid_columnconfigure((0,1,2), weight=3)
vsb = tk.Scrollbar(mybox, orient="vertical", command=canvas.yview)
canvas.configure(yscrollcommand=vsb.set, width=450, heigh=50)
vsb.pack(side="right", fill="y")
canvas.pack(side="left", fill="both", expand=True)
canvas.create_window((4,4), window=frame, anchor="nw", tags="frame")
# be sure that we call OnFrameConfigure on the right canvas
frame.bind("<Configure>", lambda event: self.OnFrameConfigure(canvas))
self.fillWindow(frame)
self.QuitButton = tk.Button(self,
text="QUIT",
command=root.destroy,
padx=25, pady=0)
self.QuitButton.grid(column = 0, columnspan=3)
def fillWindow(self, frame):
PartyWinnersList = [['Some list of places', "Somebody's Name", 0.37448599960838064],
['A shorter list', 'Three Long Names Here', 0.52167817821240514],
['A much longer, longer entry', 'Short Name', 0.41945832387008858]]
placement = 2
for i in PartyWinnersList:
ShowYear = tk.Label(frame, text="""%s """ % i[0], width=25)
ShowYear.grid(row=placement, column = 0, sticky=tk.S)
ShowSystem = tk.Label(frame,
text="""%s """ % i[1],
width=25
)
ShowSystem.grid(row=placement, column = 1, sticky=tk.N)
PercentVotes = i[2]*100
ShowVotes = tk.Label(frame,
text="""%3.1f""" % PercentVotes,
width=15
)
ShowVotes.grid(row=placement, column = 2, sticky=tk.N)
placement += 1
def OnFrameConfigure(self, canvas):
canvas.configure(scrollregion=canvas.bbox("all"))
if __name__ == "__main__":
root = tk.Tk()
main = Selections(root)
main.pack(side="top", fill="both", expand=True)
root.mainloop()
How can I get the scrollbar to show up on canvas.
# python dashboard
import tkinter as tk
from tkinter import *
#======================{{{
class AutoScrollbar(Scrollbar):
def set(self, lo, hi):
if float(lo) <= 0.0 and float(hi) >= 1.0:
self.tk.call("grid", "remove", self)
else:
self.grid()
Scrollbar.set(self, lo, hi)
def pack(self, **kw):
raise TclError("cannot use pack with this widget")
def place(self, **kw):
raise TclError("cannot use place with this widget")
#====}}}
#===================={{{
class Dashboard():
def __init__(self, root):
self.root=root
root.title("Dashboard View")
canvasContainer = tk.Frame(root)
self.canvas=tk.Canvas(canvasContainer, width=1000, height=700,background='#002B36')
canvasContainer.grid(row=0,column=3)
frame = Frame(self.root, bd=2, relief=SUNKEN)
frame.grid(row=0,column=0, sticky="nw")
b1=Button(frame,text="Status", command=lambda color="#DC322F", filename="dashboard_content.txt" : self.contents(color,filename)).grid(row = 0,column = 0, sticky = "we")
b2=Button(frame,text="Processes", command=lambda color="#859900", filename="process.log" : self.contents(color,filename)).grid(row = 0,column = 1, sticky = "we")
b3=Button(frame,text="Links", command=lambda color="#B58900", filename="links.log" : self.contents(color,filename)).grid(row = 1,column = 0, sticky = "we")
b4=Button(frame,text="Traffic", command=lambda color="#268BD2", filename="version.log" : self.contents(color,filename)).grid(row = 1,column = 1, sticky = "we")
b5=Button(frame,text="App Version", command=lambda color="#D33682", filename="version.log" : self.contents(color,filename)).grid(row = 2,column = 0, sticky = "we")
b6=Button(frame,text="Archive/Purge", command=lambda color="#93A1A1", filename="cleanup.log" : self.contents(color,filename)).grid(row = 2,column = 1, sticky = "we")
# self.contents("blue","dashboard_content.txt")
# b1.bind("<ButtonPress-1>", lambda events, color="blue", filename="dashboard_content.txt" : self.contents(color,filename))
vsb = AutoScrollbar(canvasContainer,orient=VERTICAL)
hsb = AutoScrollbar(canvasContainer, orient=HORIZONTAL)
vsb.grid(row=0, column=2, sticky="ns")
hsb.grid(row=1, column=1, sticky="ew")
self.canvas.grid(row=0,column=1,sticky="news")
self.canvas.config(yscrollcommand=vsb.set, xscrollcommand=hsb.set, scrollregion=self.canvas.bbox("all"))
vsb.config(command=self.canvas.yview)
hsb.config(command=self.canvas.xview)
canvasContainer.grid_rowconfigure(0, weight=1)
canvasContainer.grid_columnconfigure(0, weight=1)
self.canvas.update_idletasks()
# Grid.columnconfigure(self.root,1,weight=1, minsize=100)
def contents(self, color="blue", filename="dashboard_content.txt"):
fhandle = open(filename)
lines = fhandle.read()
fhandle.close()
self.canvas.delete("all")
text1=self.canvas.create_text(0, 0, fill=color, anchor=NW)
self.canvas.itemconfig(text1, text=lines)
#===========}}}
if __name__== '__main__':
root=tk.Tk()
board=Dashboard(root)
root.mainloop()
If I don't have the contents function, the scrollbar appears. How Can I have a scrollbar around canvas in this case.
You have several problems in your code. The biggest problem is that you are trying to solve too many problems at once.
If you are just starting out, you need to solve layout problems one at a time. It appears you want two major areas in your GUI -- a panel of buttons, and a canvas with scrollbars. So, start by creating two frames, one for each. Give them distinctive colors so you can see what is what. Make sure this two areas are fully functional before trying anything else By that I mean, make sure they resize appropriately when you resize the window.
So, start with the following simple program. Notice that there are no buttons and no canvas. We're just creating the basic scaffolding of the GUI.
import Tkinter as tk
class Dashboard():
def __init__(self, root):
self.root=root
root.title("Dashboard View")
# the width, height and colors are temporary,
# until we have more of the GUI working.
buttonPanel = tk.Frame(root, background="green", width=200, height=200)
canvasPanel = tk.Frame(root, background="pink", width=500, height=500)
# because these two panels are side-by-side, pack is the
# best choice:
buttonPanel.pack(side="left", fill="y")
canvasPanel.pack(side="right", fill="both", expand=True)
# fill in these two areas:
self._create_buttons(buttonPanel)
self._create_canvas(canvasPanel)
def _create_buttons(self, parent):
pass
def _create_canvas(self, parent):
pass
if __name__== '__main__':
root=tk.Tk()
board=Dashboard(root)
root.mainloop()
Does that behave the way you expect? I'll assume so. Now, as long as the buttons go in the left, and the canvas and scrollbars go on the right, we no longer have to worry about the two areas interacting with each other.
Now, create the canvas:
def _create_canvas(self, parent):
self.canvas=tk.Canvas(parent, width=1000, height=700,background='#002B36')
vsb = tk.Scrollbar(parent, command=self.canvas.yview, orient="vertical")
hsb = tk.Scrollbar(parent, command=self.canvas.xview, orient="horizontal")
self.canvas.configure(yscrollcommand=vsb.set, xscrollcommand=hsb.set)
vsb.grid(row=0, column=0, sticky="ns")
hsb.grid(row=1, column=0, sticky="ew")
self.canvas.grid(row=0, column=1, sticky="nsew")
parent.grid_rowconfigure(0, weight=1)
parent.grid_columnconfigure(1, weight=1)
Run the program again. Do you see the scrollbars? Is everything still working properly when you resize the window?
The next step would be to add the buttons:
def _create_buttons(self, parent):
b1=tk.Button(parent,text="Status", command=lambda: self._contents("#DC322F", "dashboard_content.txt"))
b2=tk.Button(parent,text="Processes", command=lambda: self._contents("#859900", "process.log"))
b3=tk.Button(parent,text="Links", command=lambda: self._contents("#B58900", "links.log"))
b4=tk.Button(parent,text="Traffic", command=lambda: self._contents("#268BD2", "version.log"))
b5=tk.Button(parent,text="App Version", command=lambda: self._contents("#D33682", "version.log"))
b6=tk.Button(parent,text="Archive/Purge", command=lambda: self._contents("#93A1A1", "cleanup.log"))
b1.grid(row = 0,column = 0, sticky = "we")
b2.grid(row = 0,column = 1, sticky = "we")
b3.grid(row = 1,column = 0, sticky = "we")
b4.grid(row = 1,column = 1, sticky = "we")
b5.grid(row = 2,column = 0, sticky = "we")
b6.grid(row = 2,column = 1, sticky = "we")
Run the program again, and make sure there are still no layout problems.
See where this is going? Solve just one problem at a time. Organize your code in sections. Collect all of your layout commands in one place so you can better visualize what widgets go together.