Python / tkinter: horizontal scrollbar not working properly - python-3.x

I have a problem using a horizontal tk.Scrollbar on a tk.Text widget. It is displayed too small for the region it is supposed to scroll - if I move it to the left, it reaches the end of the scrollable region before the small bar touches the right side, as you'd expect. Also, if I let it go it visually jumps back to its original position at the start, the scroll state is not altered though.
What's weird is that I'm using the exact same syntax for the vertical scrollbar and it works flawlessly.
I included how I'm (ab)using the tk.Text-widget, which is tk.Label-widgets embedded into the tk.Text-widget via the window_create method.
Screenshots:
Code:
import tkinter as tk
class app(tk.Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.frame = tk.Frame(self)
self.frame.grid(row=0, column=0, sticky='NW')
# widget definitions
cellYScrollbar = tk.Scrollbar(self, orient="vertical")
cellXScrollbar = tk.Scrollbar(self, orient="horizontal")
full_width = 30
self.cell = tk.Text(
self, yscrollcommand=cellYScrollbar.set, xscrollcommand=cellXScrollbar.set,
width=full_width, height=full_width / 2, wrap="none",
cursor="arrow"
)
cellYScrollbar.config(command=self.cell.yview)
cellXScrollbar.config(command=self.cell.xview)
# widget gridding
self.cell.grid(row=0, column=0, sticky='NW', padx=[5, 0], pady=[5, 0])
cellXScrollbar.grid(row=1, column=0, columnspan=2, sticky='NEW', padx=[5, 0], pady=[0, 5])
cellYScrollbar.grid(row=0, column=1, sticky='NSW', padx=[0, 5], pady=[5, 0])
tiles = []
for i in range(0, 9):
for j in range(0, 9):
test = tk.Label(self.cell, text='TEST'+str(i)+str(j), width=7, height=3)
tiles.append(test)
self.cell.window_create("end", window=test, padx=1, pady=1)
self.cell.insert("end", "\n")
for tile in tiles:
tile.bind("<MouseWheel>", lambda event: self.cell.yview_scroll(int(-1 * (event.delta / 120)), "units"))
self.cell.config(state='disabled')
root = app()
root.mainloop()
EDIT: fixed the code, .set was missing in xscrollcommand=cellXScrollbar.set, thank you to acw1668!

Related

TKInter - Confused about frames and scrolling

I am trying to code a tkinter application that has three frames - a top frame, where the user inputs some text, a dynamically constructed middle section where some pre-analysis is conducted on the text, and a bottom frame where, once the user has selected which option they want in the middle section, the output will be produced.
The problem is that, depending upon the input, there could be around 10-20 (and in the worst case 30) lines displayed and on a small monitor the output will disappear off the screen.
What I would like is for the top (input) and bottom (output) frames to be visible no matter how the screen is re-sized, and for the middle section to scroll (if required) and still allow the user to select their choice.
I am confused as to how to get the middle section to resize when the screen is resized, show a scrollbar if required, and still allow all of the content to be accessed.
I have created a cut-down version here (for simplicity, I have removed the processing methods and have instead created some fake output in a loop that resembles what the actual middle section would look like).
Please ignore the hideous colour-scheme - I was just trying to understand which frame went where (I will remove the colours as soon as I can!)
Thank you for any suggestions...
import tkinter as tk
from tkinter import scrolledtext
class MyApp(tk.Tk):
def __init__(self, title="Sample App", *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title(title)
self.configure(background="Gray")
self.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=1)
# Create the overall frame:
master_frame = tk.Frame(self, bg="Light Blue", bd=3, relief=tk.RIDGE)
master_frame.grid(sticky=tk.NSEW)
master_frame.rowconfigure([0, 2], minsize=90) # Set min size for top and bottom
master_frame.rowconfigure(1, weight=1) # Row 1 should adjust to window size
master_frame.columnconfigure(0, weight=1) # Column 0 should adjust to window size
# Create the frame to hold the input field and action button:
input_frame = tk.LabelFrame(master_frame, text="Input Section", bg="Green", bd=2, relief=tk.GROOVE)
input_frame.grid(row=0, column=0, padx = 5, pady = 5, sticky=tk.NSEW)
input_frame.columnconfigure(0, weight=1)
input_frame.rowconfigure(0, weight=1)
# Create a frame for the middle (processing) section.
middle_frame = tk.LabelFrame(master_frame, text = "Processing Section")
middle_frame.grid(row=1, column=0, padx=5, pady=5, sticky=tk.NSEW)
# Create the frame to hold the output:
output_frame = tk.LabelFrame(master_frame, text="Output Section", bg="Blue", bd=2, relief=tk.GROOVE)
output_frame.grid(row=2, column=0, columnspan=3, padx=5, pady=5, sticky=tk.NSEW)
output_frame.columnconfigure(0, weight=1)
output_frame.rowconfigure(0, weight=1)
# Add a canvas in the middle frame.
self.canvas = tk.Canvas(middle_frame, bg="Yellow")
self.canvas.grid(row=0, column=0)
# Create a vertical scrollbar linked to the canvas.
vsbar = tk.Scrollbar(middle_frame, orient=tk.VERTICAL, command=self.canvas.yview)
vsbar.grid(row=0, column=1, sticky=tk.NS)
self.canvas.configure(yscrollcommand=vsbar.set)
# Content for the input frame, (one label, one input box and one button).
tk.Label(input_frame,
text="Please type, or paste, the text to be analysed into this box:").grid(row=0, columnspan = 3, sticky=tk.NSEW)
self.input_box = scrolledtext.ScrolledText(input_frame, height=5, wrap=tk.WORD)
self.input_box.columnconfigure(0, weight=1)
self.input_box.grid(row=1, column=0, columnspan = 3, sticky=tk.NSEW)
tk.Button(input_frame,
text="Do it!",
command=self.draw_choices).grid(row=2, column=2, sticky=tk.E)
# Content for the output frame, (one text box only).
self.output_box = scrolledtext.ScrolledText(output_frame, width=40, height=5, wrap=tk.WORD)
self.output_box.grid(row=0, column=0, columnspan=3, sticky=tk.NSEW)
def draw_choices(self):
""" This method will dynamically create the content for the middle frame"""
self.option = tk.IntVar() # Variable used to hold user's choice
self.get_input_text()
for i in range(30):
tk.Radiobutton(self.canvas,
text=f"Option {i + 1}: ", variable=self.option,
value=i,
command=self.do_analysis
).grid(row=i, column=0, sticky=tk.W)
tk.Label(self.canvas,
text=f"If you pick Option {i + 1}, the output will look like this: {self.shortText}.",
anchor=tk.W
).grid(row=i, column=1, sticky=tk.W)
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
def get_input_text(self):
""" Will get the text from the input box and also create a shortened version to display on one line"""
screenWidth = 78
self.input_text = self.input_box.get(0.0, tk.END)
if len(self.input_text) > screenWidth:
self.shortText = self.input_text[:screenWidth]
else:
self.shortText = self.input_text[:]
self.shortText = self.shortText.replace('\n', ' ') # strip out carriage returns just in case
def do_analysis(self):
"""This will ultimately process and display the results"""
option = self.option.get() # Get option from radio button press
output_txt = f"You picked option {option + 1} and here is the output: \n{self.input_text}"
self.output_box.delete(0.0, tk.END)
self.output_box.insert(0.0, output_txt)
if __name__ == "__main__":
app = MyApp("My Simple Text Analysis Program")
app.mainloop()
I understand that you can't mix grid and pack geometries in the same container, and that a scrollbar must be attached to a canvas, and objects to be placed on that canvas must therefore be in yet another container so, attempting to follow Bryan's example, I created a minimal version of what I want - window with three sections - top, middle and bottom. The Top and bottom sections will contain a simple text field, the middle section will contain dynamic content and must be able to scroll as required.
Imports:
ScrollbarFrame
Extends class tk.Frame to support a scrollable Frame]
import tkinter as tk
class App(tk.Tk):
def __init__(self):
super().__init__()
self.title("A simple GUI")
# Top frame
self.top_frame = tk.Frame(self, bg="LIGHT GREEN")
self.top_frame.pack(fill=tk.X)
tk.Label(self.top_frame, bg=self.top_frame.cget('bg'),
text="This is a label on the top frame")\
.grid(row=0, columnspan=3, sticky=tk.NSEW)
# Middle Frame
# Import from https://stackoverflow.com/a/62446457/7414759
# and don't change anything
sbf = ScrollbarFrame(self, bg="LIGHT BLUE")
sbf.pack(fill=tk.X, expand=True)
# self.middle_frame = tk.Frame(self, bg="LIGHT BLUE")
self.middle_frame = sbf.scrolled_frame
# Force scrolling by adding multiple Label
for _ in range(25):
tk.Label(self.middle_frame, bg=self.middle_frame.cget('bg'),
text="This is a label on the dynamic (middle) section")\
.grid()
# Bottom Frame
self.bottom_frame = tk.Frame(self, bg="WHITE")
self.bottom_frame.pack(fill=tk.X)
tk.Label(self.bottom_frame, bg=self.bottom_frame.cget('bg'),
text="This is a label on the bottom section")\
.grid(row=0, columnspan=3, sticky=tk.NSEW)
if __name__ == '__main__':
App().mainloop()

A Single Tkinter Window Which Moves Through Frames, Bringing Each Frame to the Foreground. One Frame needs to be split into two Sub-frames

The need for two different geometric plotters arises because i want to display a sophisticated GUI with text, entry fields, pictures, buttons, alongside an animated matplotlib graph on a FigureCanvasTkAgg with a NavigationToolbar2Tk. The NavigationToolbar2Tk fails to work when any geometric plotter other than pack() is used. I have tried over the course of the week several different methods of putting the NavigationToolbar2Tk into its own frame, but in the examples, only a single frame exists. I came to the conclusion that ideally, splitting my frame ~ PageOne ~ into two subframes or instantiating two frames which make up page one.
I have several frames as shown in my code below and my knowledge of python is rudimentary so i don't have the imagination or know how to circumvent this problem. I have gutted my code to show the problem concisely and Included my Imports.
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
from matplotlib.pylab import *
from matplotlib import style
import matplotlib.animation as animation
import matplotlib.pyplot as plt
import matplotlib.dates as dates
import matplotlib.ticker as mticker
import tkinter as tk
from tkinter import ttk
from tkinter import *
from PIL import Image, ImageTk
from mpl_toolkits.axes_grid1 import host_subplot
from gpiozero import CPUTemperature
import time
import datetime as dt
import pandas as pd
import numpy as np
#************************************************************************#
# Format Graph_1 [ax1] onto fig1 at subplot [ row = 1, col = 1, index = 1 ]
# Set Figure Text
font = {'size' : 9}
matplotlib.rc('font', **font)
# Setup Figure
fig1 = Figure()
# Define Axis 1 for Vin and Vout
ax1 = fig1.add_subplot(1, 1, 1)
#subplots_adjust(left=0.05, bottom=0.10, right=0.55, top=0.8, wspace=0.2)
ax1.minorticks_on()
ax1.grid(b=True, which='major', color='k', linestyle='-')
ax1.grid(b=True, which='minor', color='k', linestyle=':')
ax1.set_title("PI3740 Paramaters", fontsize = 12)
ax1.set_xlabel("Relative Time (s)", fontsize = 10)
ax1.set_ylabel("Voltage (V)", fontsize =10)
# Define Axis 2 for Iout Which is tied to Axis 1's X-Axis
ax2 = ax1.twinx()
ax2.set_ylabel("Output Current (A)")
# Parameters
x_len = 500 # Resolution [Number of Points in Window]
x_max = 2 # X-Axis Range [ (Step)ms Samp^-1 * (x_len)Samp = X_Range]
y_range = [0, 50] # Range of possible Y values to display
# Create figure for plotting
steps = (x_max/x_len)
stepms = steps * 1000
xs = np.arange(0, x_max, steps) # xs is a list from 0 to 10 in steps of 0.01 [A list refers to a 1D Array]
ys1 = [0] * x_len # ys is a list indexed from ys[0] to ys[999] all containing 0 # Vin
ys2 = [0] * x_len # ys is a list indexed from ys[0] to ys[999] all containing 0 # Vout
ys3 = [0] * x_len # ys is a list indexed from ys[0] to ys[999] all containing 0 # Iout
ax1.set_ylim(y_range) # Y-Axis Voltage Range Set
ax2.set_ylim(0, 10) # Y-Axis Current Range Set
ax1.set_xlim(0, x_max) # X-Axis Shared Relative Time Range Set
# Create a blank line. We will update the line in animate
line1, = ax1.plot(xs, ys1, 'b-', label = "Vin")
line2, = ax1.plot(xs, ys2, 'g-', label = "Vout")
line3, = ax2.plot(xs, ys3, 'r-', label = "Iout")
# Create a Legend
ax1.legend([line1, line2],[line1.get_label(), line2.get_label()])
ax1.legend(bbox_to_anchor = (0.,0.99,1.,.102), loc = 3, ncol = 2, borderaxespad = 0., frameon = False)
ax2.legend([line3],[line3.get_label()])
ax2.legend(bbox_to_anchor = (1.00,0.99), loc = 'lower right', borderaxespad = 0., frameon = False)
#************************************************************************#
#**********************Animation Function********************************#
# This function is called periodically from FuncAnimation
def updateData(self):
# Drop down menu event flags
global ChartLoad
# Graph variables
global xs
global ys1
global ys2
global ys3
if ChartLoad == True:
# Read temperature (Celsius) from TMP102
temp_c = cpu.temperature
temp_c1 = temp_c + 5.0
temp_c2 = temp_c - 35.0
# Add y to list
ys1.append(temp_c)
ys2.append(temp_c1)
ys3.append(temp_c2)
# Limit y list to set number of items
ys1 = ys1[-x_len:]
ys2 = ys2[-x_len:]
ys3 = ys3[-x_len:]
# Update line with new Y values
line1.set_ydata(ys1)
line2.set_ydata(ys2)
line3.set_ydata(ys3)
return line1, line2, line3,
#************************************************************************#
#*******************Tkinter Window Initalization*************************#
class MyApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self,"EMEA Vicor Charging Application ")
img=tk.PhotoImage(file='/home/pi/Pictures/Vicor_Icon1.png')
self.tk.call('wm','iconphoto',self._w,img)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
#*********************************************************
#******Function Required to Display Seperate Pages********
self.frames = {}
for F in (StartPage, HomePage, PageOne):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
#*********************************************************
#*********************************************************
#Start Page - Agreement
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
#Page One - Primary Terminal
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
tk.Frame.__init__(self, parent, bg = "white")
# Two Frames Need to be Established on Page One (Which is a “Higher Order Frame”)
my_window = Tk()
frame_name = Frame(my_window)
frame_addr = Frame(my_window)
label_f = Label(frame_name, text = "Check")
label_f.grid(row = 0, column = 0)
label_g = Label(frame_addr, text = "Correct")
label_g.grid(row = 0, column = 0)
frame_name.grid(row = 0, column = 0)
frame_addr.grid(row = 0, column = 1)
#my_window.mainloop()
app = MyApp()
app.geometry("1010x700")
ani = animation.FuncAnimation(fig1, updateData, blit = True, interval = stepms)
app.mainloop()
Page One will contain my Canvas for the Graph and Tool Bar. however to simplify the problem into its fundamental, getting two frames to make up "Higher Order" frame PageOne, with each subframe containing a label. When I run the code, another window opens with the two labels displayed. This is more progress than from other solutions that i can't implement/don't understand, producing error messages i can't respond to. I am self taught at python, and have been following tutorials from the community. I just need some help implementing the solution. If I've gutted my code to much i can provide a more comprehensive code snippet. But the essence of the problem is, while displaying one of several frames on a Tkinter Window which move to the foreground when requested, how do you split one of these "High Order" frames into two frames as to allow two different geometric plotters to be used to structure each one.
Resources Used So Far:
https://www.youtube.com/watch?v=Mxk4cMBaH3g&list=PL6lxxT7IdTxGoHfouzEK-dFcwr_QClME_&index=37&t=0s
[Most Recent Attempt - Trying to Simplify the problem]
how to make two split up screen ( canvas) inside the python tkinter window
http://www.openbookproject.net/courses/python4fun/tkphone1.html
matplotlib Navigation Bar error 'FigureCanvasTkAgg' object has no attribute 'manager' in tkinter
Displaying Matplotlib Navigation Toolbar in Tkinter via grid
[Hit the Same Problem - One Master Frame Split Into Two Sub Frames]
[This Link is probably has the answer in it and if that is the case, apologies for repeating a question already asked on stack overflow)
EDIT. To Expand Further for Clarity take the code below:
import tkinter as tk # python 3
from tkinter import font as tkfont # python 3
#import Tkinter as tk # python 2
#import tkFont as tkfont # python 2
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title_font = tkfont.Font(family='Helvetica', size=18, weight="bold", slant="italic")
# the container is where we'll stack a bunch of frames
# on top of each other, then the one we want visible
# will be raised above the others
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, PageOne, PageTwo):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
# put all of the pages in the same location;
# the one on the top of the stacking order
# will be the one that is visible.
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("StartPage")
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is the start page", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button1 = tk.Button(self, text="Go to Page One",
command=lambda: controller.show_frame("PageOne"))
button2 = tk.Button(self, text="Go to Page Two",
command=lambda: controller.show_frame("PageTwo"))
button1.pack()
button2.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 1", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 2", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
how would i get PageTwo to compose of two different frames so i can use two different geometric plotters (pack() and grid()).

Tkinter - Expanding frame after (interactively) adding elements to it

I have the following code:
from tkinter import *
DEF_CHANNELS = {'iris': (255, 0, 0), 'sclera': (0, 255, 0), 'pupil': (0, 0, 255)}
class GUI(Tk):
def __init__(self, init_source, init_target, *args, **kw):
super().__init__(*args, **kw)
self.frame = Frame(self, height=400, width=500)
self.frame.pack(fill=BOTH, expand=YES)
self.channel_frame = Frame(self.frame, height=200, width=500, pady=16)
self.channel_frame.grid(column=0, row=0, columnspan=2)
self.channel_label = Label(self.channel_frame, text="Channel")
self.channel_label.grid(column=0, row=0)
self.colour_label = Label(self.channel_frame, text="Colour")
self.colour_label.grid(column=1, row=0)
self.channel_frames = []
for channel, colour in DEF_CHANNELS.items():
self.add_channel_frame(channel, colour)
self.channel_button = Button(self.channel_frame, text="+", command=self.add_channel_frame)
self.channel_button.grid(column=0, row=len(self.channel_frames) + 1)
def add_channel_frame(self, def_channel="", def_colour=""):
pair_frame = ChannelColourFrame(self.channel_frame, def_channel=def_channel, def_colour=def_colour, height=100, width=500, pady=2)
pair_frame.grid(column=0, row=len(self.channel_frames) + 1, columnspan=2)
self.channel_frames.append(pair_frame)
class ChannelColourFrame(Frame):
def __init__(self, *args, def_channel="", def_colour="", **kw):
super().__init__(*args, **kw)
self.channel_txt = Entry(self, width=30)
self.channel_txt.insert(END, def_channel)
self.channel_txt.grid(column=0, row=0)
self.colour_txt = Entry(self, width=30)
self.colour_txt.insert(END, def_colour)
self.colour_txt.grid(column=1, row=0)
self.color_picker_button = Button(self, text="\u2712")
self.color_picker_button.grid(column=2, row=0)
self.remove_button = Button(self, text="-", command=self.remove)
self.remove_button.grid(column=3, row=0)
def remove(self):
self.master.master.master.channel_frames.remove(self)
self.destroy()
gui = GUI('', '')
gui.mainloop()
The idea is to have a Frame that starts with 3 default text Entry pairs, which a user can arbitrarily remove/add. For the most part it works fine, but with one big problem. The Frame (self.channel_frame) never expands past its initial height, which causes problems when more than the initial 3 Entry pairs appear on it.
How do I make the entire Frame fit to the Entry pairs every time one is removed/added?
As an additional question, \u2712 appears as a box on my button, but it's supposed to be the black nib symbol (✒). Why isn't the symbol showing up despite being part of unicode?
You aren't creating any new rows, so it's not going to grow. At the start, you create three channel frames, and they are put in rows 0, 1, and 2. You then add a "+" button in row 4.
When you click the "+" button, it adds a new row at len(self.channel_frames) + 1. Since len(self.channel_frames) is 3, it adds the new frame at row 4, which is on top of the "+" button. Thus, you aren't adding a new row.
If you move the "+" button out of the frame, or move it down each time you add a new row, your code works fine.
For example:
def add_channel_frame(self, def_channel="", def_colour=""):
pair_frame = ChannelColourFrame(self.channel_frame, def_channel=def_channel, def_colour=def_colour, height=100, width=500, pady=2)
pair_frame.grid(column=0, row=len(self.channel_frames) + 1, columnspan=2)
self.channel_frames.append(pair_frame)
self.channel_button.grid(column=0, row=len(self.channel_frames)+1)
As an additional question, \u2712 appears as a box on my button, but it's supposed to be the black nib symbol (✒). Why isn't the symbol showing up despite being part of unicode?
Probably because the font you're using doesn't have that symbol. Try using a different font.

Tkinter Python Frame with Scroll bar

I have below code which will create Label and Entry widgets. Also I created Scroll bar for this window but both scroll bars are not working.
Please let me know why the scroll bar is disabled. Did the code is doing wrong ?
Below is my code which will create 50 labels and entry widgets but still scroll bars are not enabled.
import tkinter as tk
from tkinter import ttk
class DoubleScrollbarFrame(ttk.Frame):
def __init__(self, master, **kwargs):
'''
Initialisation. The DoubleScrollbarFrame consist of :
- an horizontal scrollbar
- a vertical scrollbar
- a canvas in which the user can place sub-elements
'''
ttk.Frame.__init__(self, master, **kwargs)
# Canvas creation with double scrollbar
self.hscrollbar = ttk.Scrollbar(self, orient=tk.HORIZONTAL)
self.vscrollbar = ttk.Scrollbar(self, orient=tk.VERTICAL)
self.sizegrip = ttk.Sizegrip(self)
self.canvas = tk.Canvas(self, bd=0, highlightthickness=0,
yscrollcommand=self.vscrollbar.set,
xscrollcommand=self.hscrollbar.set)
self.vscrollbar.config(command=self.canvas.yview)
self.hscrollbar.config(command=self.canvas.xview)
def pack(self, **kwargs):
'''
Pack the scrollbar and canvas correctly in order to recreate the
same look as MFC's windows.
'''
self.hscrollbar.pack(side=tk.BOTTOM, fill=tk.X, expand=tk.FALSE)
self.vscrollbar.pack(side=tk.RIGHT, fill=tk.Y, expand=tk.FALSE)
self.sizegrip.pack(in_=self.hscrollbar, side=tk.BOTTOM, anchor="se")
self.canvas.pack(side=tk.LEFT, padx=5, pady=5,
fill=tk.BOTH, expand=tk.TRUE)
ttk.Frame.pack(self, **kwargs)
def get_frame(self):
'''
Return the "frame" useful to place inner controls.
'''
return self.canvas
if __name__ == '__main__':
# Top-level frame
root = tk.Tk()
root.title("Double scrollbar with tkinter")
root.minsize(width=600, height=600)
frame = DoubleScrollbarFrame(root, relief="sunken")
# Add controls here
subframe = ttk.Frame(frame.get_frame())
txt = ttk.Label(subframe, text="Add things here !")
for i in range(50):
ttk.Label(subframe, text="Field %d: " % i).grid(row=i, column=0,
sticky="w")
ttk.Entry(subframe, width=25).grid(row=i, column=1, sticky="ew")
subframe.pack(padx=15, pady=15, fill=tk.BOTH, expand=tk.TRUE)
frame.pack(padx=5, pady=5, expand=True, fill=tk.BOTH)
# launch the GUI
root.mainloop()

Tkinter How To Make Button In Middle of Window

I want to make two buttons that are in the middle of the page - meaning one on top of the other with the same x-coordinates but the y-coordinates would have to be just outside the centre so that it would look proportionally correct
Code: [ This is what I have got so far ]
from tkinter import *
class Window:
def __init__(self, master):
self.signup_button = Button(master, text="Sign Up", width=6,height=2)
self.login_button = Button(master, text="Login", width=6,height=2)
self.signup_button.grid()
self.login_button.grid()
root=Tk()
run=Window(root)
root.mainloop()
Also, How do they position everything in such an organised manner:
not sure what exactly do you expect as result, but you can try to adjust padx and pady values:
self.signup_button.grid(row = 0, column = 0, padx=5, pady=5)
self.login_button.grid(row = 0, column = 1, padx=5, pady=5)

Resources