How to add a xscrollbar to Text widget in tkinter - python

According to this source: both x and y scrollbars can be added to the Text() widget of tkinter. The codes which work in procedural method are:
from tkinter import *
root = Tk()
frame = Frame(master, bd=2, relief=SUNKEN)
frame.grid_rowconfigure(0, weight=1)
frame.grid_columnconfigure(0, weight=1)
xscrollbar = Scrollbar(frame, orient=HORIZONTAL)
xscrollbar.grid(row=1, column=0, sticky=E+W)
yscrollbar = Scrollbar(frame)
yscrollbar.grid(row=0, column=1, sticky=N+S)
text = Text(frame, wrap=NONE, bd=0,
xscrollcommand=xscrollbar.set,
yscrollcommand=yscrollbar.set)
text.grid(row=0, column=0, sticky=N+S+E+W)
xscrollbar.config(command=text.xview)
yscrollbar.config(command=text.yview)
frame.pack()
root.mainloop()
However, i choose the class method and wrote the below codes, according to the below codes y scrollbar works but x scrollbar doesn't work. Why doesn't x scrollbar work in this example?
import tkinter as tk
class App(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
self.x_scrollbar = tk.Scrollbar(master=self, orient="horizontal")
self.x_scrollbar.grid(row=1, column=0, sticky="w, e")
self.y_scrollbar = tk.Scrollbar(master=self)
self.y_scrollbar.grid(row=0, column=1, sticky="n, s")
self.text = tk.Text(master=self, width=100, height=25, bg="black", fg="white", wrap=None)
self.text.grid(row=0, column=0, sticky="n, s, e, w")
self.configure_widgets()
self.pack()
def configure_widgets(self):
self.text.configure(xscrollcommand=self.x_scrollbar.set, yscrollcommand=self.y_scrollbar.set)
self.x_scrollbar.config(command=self.text.xview)
self.y_scrollbar.config(command=self.text.yview)
if __name__ == "__main__":
root = tk.Tk()
app = App(master=root)
app.mainloop()

The problem here is not the scrollbar code but the assignment of None in wrap config of your textbox.
Change
wrap=None
To
wrap='none'
on an unrelated note
change sticky="n, s, e, w" to sticky="nsew" the commas mean nothing in a quote here. And your other stickys should be "we" and "ns"
You might have been trying to do the tkinter CONSTANTS version of the stick. That would look like this: sticky=(N, S, E, W). However because you did not import * this will not work. You could import each constant from tkinter individually but in this case its best to use sticky="nsew" instead.
Just for reference here is a list of the 'nsew' constants you get when you import * from tkinter
N='n'
S='s'
W='w'
E='e'
NW='nw'
SW='sw'
NE='ne'
SE='se'
NS='ns'
EW='ew'
NSEW='nsew'

Related

Sub Frame Is Not Expanded When Its Parent Is

import tkinter as tk
import tkinter.ttk as ttk
root = tk.Tk()
left_frame_1 = tk.Frame(root, background="#ff0000")
left_frame_1.grid(row=0, column=0)
left_frame_2 = tk.Frame(left_frame_1)
left_frame_2.grid(row=0, column=0)
left_label_1 = tk.Label(left_frame_2, text="HELLO")
left_label_2 = tk.Label(left_frame_2, text="WORLD")
left_label_3 = tk.Label(left_frame_2, text="=D")
left_label_1.grid(row=0, column=0)
left_label_2.grid(row=1, column=0)
left_label_3.grid(row=2, column=0)
right_frame1 = tk.Frame(root, background="#00ff00")
right_frame1.grid(row=0, column=1, sticky="nsew")
right_frame_2 = tk.Frame(right_frame1, background="#0000ff")
right_frame_2.grid(row=0, column=0)
right_label_1 = tk.Label(right_frame_2, text="CENTER ME!")
right_label_1.grid(row=0, column=0)
root.mainloop()
When my parent frame expands to all its free space, the child frame doesn't, instead it just stays on top.
I've been testing if .grid() has something to do with it, but haven't found anything.
Even if I add sticky="nsew" to both the frame and the label, there is still no change.
right_frame1 = tk.Frame(root, background="#00ff00")
right_frame1.grid(row=0, column=1, sticky="nsew")
right_frame_2 = tk.Frame(right_frame1, background="#0000ff")
right_frame_2.grid(row=0, column=0, sticky="nsew")
right_label_1 = tk.Label(right_frame_2, text="CENTER ME!")
right_label_1.grid(row=0, column=0, sticky="nsew")
My goal is for the parent frame (the one with the green color) to expand to all available space (which I've achieved), and for the child frame containing the label to expand.
right_frame_2 looks because it expands.
right_frame_1 is not visible because it is completely covered by right_frame_2.
I hope your help, thank you.
To get the result of the last image in the question, you need to:
change sticky options of .grid() for right_frame_2 and right_label_1
set weight options of .rowconfigure() and .columnconfigure() on root, right_frame1 and right_frame_2
import tkinter as tk
import tkinter.ttk as ttk
root = tk.Tk()
left_frame_1 = tk.Frame(root, background="#ff0000")
left_frame_1.grid(row=0, column=0)
left_frame_2 = tk.Frame(left_frame_1)
left_frame_2.grid(row=0, column=0)
left_label_1 = tk.Label(left_frame_2, text="HELLO")
left_label_2 = tk.Label(left_frame_2, text="WORLD")
left_label_3 = tk.Label(left_frame_2, text="=D")
left_label_1.grid(row=0, column=0)
left_label_2.grid(row=1, column=0)
left_label_3.grid(row=2, column=0)
right_frame1 = tk.Frame(root, background="#00ff00")
right_frame1.grid(row=0, column=1, sticky="nsew")
right_frame_2 = tk.Frame(right_frame1, background="#0000ff")
right_frame_2.grid(row=0, column=0, sticky="nsew") # expand to fill available space
right_label_1 = tk.Label(right_frame_2, text="CENTER ME!")
right_label_1.grid(row=0, column=0, sticky="ew") # expand horizontally
root.rowconfigure(0, weight=1) # make left and right frame expand vertically
root.columnconfigure(1, weight=1) # make right frame expand horizontally
# allocate all space to right_frame_2
right_frame1.rowconfigure(0, weight=1)
right_frame1.columnconfigure(0, weight=1)
# allocate all space of right_frame_2 to right_label_1
right_frame_2.rowconfigure(0, weight=1)
right_frame_2.columnconfigure(0, weight=1)
root.mainloop()
Result:
When the window is resized:

Restrict Tkinter LabelFrame from changing proportions in a grid

I have created a GUI for reading a PDF file. As soon as I load PDF, the grid expands on x axis.
I have tried setting weights of row and column as 1, but I have no clue what else I am missing.
I want to restrict these 2 LabelFrame from resizing in wrong proportion, even if I resize the window, the proportions should be same.
In short, I want the two LabelFrame to be in equal space!
Does anyone have any idea how to achieve so?
My code
import cv2
from PIL.ImageTk import PhotoImage
from pdf2image import convert_from_path
from tkinter.ttk import Separator
from tkinter import Tk, LabelFrame, Label, Frame, Button
from tkinter.filedialog import askopenfilename
class GUI(Tk):
def __init__(self):
super().__init__()
self.geometry('1000x600')
# self.resizable(False, False)
self.init_frame()
def open_file(self):
file_path = askopenfilename(
title='Select a PDF...',
filetypes=(('PDF', '*.PDF'),))
h = self.pdf_frame.winfo_height()
w = self.pdf_frame.winfo_width()
print(h, w)
img = PhotoImage(convert_from_path(file_path, size=(w, h))[0])
self.pdf_frame.configure(image=img)
self.pdf_frame.image = img
def clear_file(self):
self.init_pdf_frame()
def init_frame(self):
self.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=1)
self.frame = Frame(self)
self.frame.rowconfigure(0, weight=1)
self.frame.columnconfigure(0, weight=1)
self.frame.columnconfigure(1, weight=1)
self.frame.grid(column=0, row=0, sticky='news', padx=10, pady=10)
self.init_pdf_frame()
self.init_ocr_frame()
def init_pdf_frame(self):
image_viewer = LabelFrame(self.frame, text="PDF")
image_viewer.rowconfigure(2, weight=1)
image_viewer.columnconfigure(0, weight=1)
image_viewer.grid(column=0, row=0, sticky='news')
options = Frame(image_viewer)
options.columnconfigure(0, weight=1)
options.columnconfigure(1, weight=1)
options.grid(column=0, row=0, padx=5, pady=10, sticky='news')
open_button = Button(options, text='Open', command=self.open_file)
open_button.grid(column=0, row=0, padx=10, sticky='news')
clear_button = Button(options, text='Clear', command=self.clear_file)
clear_button.grid(column=1, row=0, padx=10, sticky='news')
line = Separator(image_viewer, orient='horizontal')
line.grid(row=1, column=0, sticky='ew')
self.pdf_frame = Label(image_viewer, text="PDF result appears here")
self.pdf_frame.grid(row=2, column=0, sticky='news')
def init_ocr_frame(self):
ocr_label = LabelFrame(self.frame, text="OCR")
ocr_label.rowconfigure(0, weight=1)
ocr_label.columnconfigure(0, weight=1)
ocr_label.grid(column=1, row=0, sticky='news')
label = Label(ocr_label, text="PDF result appears here")
label.grid(row=0, column=0, padx=10, pady=10, sticky='news')
if __name__ == '__main__':
GUI().mainloop()

Is it possible to manage geometry modularly?

I have a code that I'm trying to handle the geometry of a button in a frame and entry in another frame. But it doesn't seem to work independently of the main window they're both children of.
try: # In order to be able to import tkinter for
import tkinter as tk # either in python 2 or in python 3
except ImportError:
import Tkinter as tk
class NumPad(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.button = tk.Button(text=0)
self.button.grid(row=1, column=0, sticky='nsew')
class CalcFrame(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.entry = tk.Entry(self)
self.entry.pack()
if __name__ == '__main__':
root = tk.Tk()
frame1 = CalcFrame(master=root)
frame2 = NumPad(master=root)
frame1.grid(row=0, column=0)
frame2.grid(row=1, column=0, sticky='nsew')
root.mainloop()
In the above code if I replace:
self.button.grid(row=0, column=0, sticky='nsew')
with:
self.button.grid(row=1, column=0, sticky='nsew')
the widget in frame2 overlaps the widget on frame1. How can I have an inner grid per widget basis? Right now it seems like there's only one top-level grid.
As Bryan Oakley pointed out in the comments above, when you declare the Button widget on this line...
self.button = tk.Button(text=0)
You aren't assigning it a parent meaning that it just dumps itself into the Tk() window by default.
On a side note, you have variables which by their name suggest that they are Frame widgets (namely frame1 and frame2) but actually appear to be references to classes which don't ever use Frame widgets.
Frame widgets are very powerful and can be used to easily separate sets of widgets in the same window. An example of using Frames can be found below:
from tkinter import *
root = Tk()
frame1 = Frame(root, borderwidth=1, relief="solid")
frame2 = Frame(root, borderwidth=1, relief="solid")
frame1.pack(side="left", fill="both", expand=True, padx=10, pady=10)
frame2.pack(side="right", fill="both", expand=True, padx=10, pady=10)
label1 = Label(frame1, text="I'm inside a frame")
label2 = Label(frame2, text="I'm inside a different frame")
label1.pack()
label2.pack()
root.mainloop()
This shows that you can have widgets using a different geometry manager to their parents:
from tkinter import *
root = Tk()
frame1 = Frame(root)
frame2 = Frame(root)
frame1.pack(side="left")
frame2.pack(side="right")
label1 = Label(frame1, text="I'm grid")
label2 = Label(frame1, text="I'm grid")
label3 = Label(frame2, text="I'm pack")
label4 = Label(frame2, text="I'm pack")
label1.grid(row=0, column=0)
label2.grid(row=0, column=1)
label3.pack()
label4.pack()
root.mainloop()

Is it possible to make 'dynamically' adjustable widgets in Tkinter/ttk

I'm developing very simple GUI for my DB. It shows record's list/tree in DB on left panel and (if user clicks on some record) shows the record on the right panel.
Here some bit of code which creates GUI
from Tkinter import *
import ttk
master = Tk()
reclist = ttk.Treeview(columns=["TIME STAMP","HASH","MESSAGE"])
ysb = ttk.Scrollbar(orient=VERTICAL, command= reclist.yview)
xsb = ttk.Scrollbar(orient=HORIZONTAL, command= reclist.xview)
reclist['yscroll'] = ysb.set
reclist['xscroll'] = xsb.set
reclist.grid(in_=master, row=0, column=0, sticky=NSEW)
ysb.grid(in_=master, row=0, column=1, sticky=NS)
xsb.grid(in_=master, row=1, column=0, sticky=EW)
Comment = Text(master)
Comment.tag_configure("center", justify='center')
ysc = ttk.Scrollbar(orient=VERTICAL, command= Comment.yview)
xsc = ttk.Scrollbar(orient=HORIZONTAL, command= Comment.xview)
Comment.grid(in_=master,row=0,column=2,sticky=W+E+N+S)#, columnspan=5)
ysc.grid(in_=master, row=0, column=3, sticky=NS)
xsc.grid(in_=master, row=1, column=2, sticky=EW)
master.rowconfigure(0, weight=3)
master.columnconfigure(0, weight=3)
master.columnconfigure(2, weight=3)
master.mainloop()
Everything works pretty well, except that two panels are not adjustable. I cannot move border between them to make list of records or record panel bigger or smaller. I'm pretty sure in is possible (for example in gitk you can move the border between the list of commits and a displaied commit). I've search quite a lot with no luck.
What you are looking for is called a "PanedWindow". Both the tkinter and ttk modules have one, and they work almost identically. The general idea is that you create a PanedWindow instance, and then you add two or more widgets to it. The PanedWindow will add a movable slider between each widget. Typically you would use frames, which you can then fill up with other widgets.
Here is an example using the one in Tkinter:
import Tkinter as tk
root = tk.Tk()
pw = tk.PanedWindow()
pw.pack(fill="both", expand=True)
f1 = tk.Frame(width=200, height=200, background="bisque")
f2 = tk.Frame(width=200, height=200, background="pink")
pw.add(f1)
pw.add(f2)
# adding some widgets to the left...
text = tk.Text(f1, height=20, width=20, wrap="none")
ysb = tk.Scrollbar(f1, orient="vertical", command=text.yview)
xsb = tk.Scrollbar(f1, orient="horizontal", command=text.xview)
text.configure(yscrollcommand=ysb.set, xscrollcommand=xsb.set)
f1.grid_rowconfigure(0, weight=1)
f1.grid_columnconfigure(0, weight=1)
xsb.grid(row=1, column=0, sticky="ew")
ysb.grid(row=0, column=1, sticky="ns")
text.grid(row=0, column=0, sticky="nsew")
# and to the right...
b1 = tk.Button(f2, text="Click me!")
s1 = tk.Scale(f2, from_=1, to=20, orient="horizontal")
b1.pack(side="top", fill="x")
s1.pack(side="top", fill="x")
root.mainloop()

Python tkinter - Text not showing (GUI)

I have no bug errors and I was wondering why 'TIMER' is not showing up in the GUI when I run it. It just shows a white box. I`ve tried searching the forums for an issue similar to mine but I failed to find any.
CODE:
import tkinter
class study_timer:
def __init__(self, master):
self.master = master
self.mainframe = tkinter.Frame(self.master, bg='white')
self.mainframe.pack(fill = tkinter.BOTH, expand=True)
self.build_grid()
self.build_banner()
def build_grid(self):
self.mainframe.columnconfigure(0, weight=1)
self.mainframe.rowconfigure(0, weight=0)
self.mainframe.rowconfigure(0, weight=1)
self.mainframe.rowconfigure(0, weight=0)
def build_banner(self):
banner = tkinter.Label(
self.mainframe,
bg='black',
text='TIMER',
fg='white',
font=('Ravie Regular', 30)
)
banner.grid(
row=0, column=0,
stick='ew',
padx=10, pady=10
)
if __name__ == "__main__":
root = tkinter.Tk()
root.mainloop()
You should instantiate an object of the class if you want to run the functions that you defined. The functions are called from constructor(init) in your class structure.
Second, if statement's indentation is wrong.
Third, you should send the root object to init function as parameter.
This will work
import tkinter
class study_timer:
def __init__(self, master):
self.master = master
self.mainframe = tkinter.Frame(self.master, bg='white')
self.mainframe.pack(fill = tkinter.BOTH, expand=True)
self.build_grid()
self.build_banner()
def build_grid(self):
self.mainframe.columnconfigure(0, weight=1)
self.mainframe.rowconfigure(0, weight=0)
self.mainframe.rowconfigure(0, weight=1)
self.mainframe.rowconfigure(0, weight=0)
def build_banner(self):
banner = tkinter.Label(
self.mainframe,
bg='black',
text='TIMER',
fg='white',
font=('Ravie Regular', 30)
)
banner.grid(
row=0, column=0,
stick='ew',
padx=10, pady=10
)
if __name__ == "__main__":
root = tkinter.Tk()
ss = study_timer(root)
root.mainloop()

Categories