So, I am trying to make a database program for my needs, and to make it more visible attractive I want the plus sign to follow indentation from the files and folders it reads from a database? This is going to be a parent/child kind of view.
Here is what I want:
The open and closing +/- symbol follows the filename
The code for this I found during researching into this exact problem, but I cant differentiate the code to figure out what I am doing wrong.
Here is what I currently have:
Not following, and it seems to be appearing on its separate columns.
I've tried to add and remove columns, because to me, it seems like the problem is that they are being printed to their own column.
Here is my code (The code I found when searching is pasted at the bottom, I'm sorry to the author, I didnt make a not of where I found it.) (If you run it the scrollbar is way off btw..):
class FiletreeInfoApp(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.root = parent
width = self.root.winfo_screenwidth()
height = self.root.winfo_screenheight()
self.root.geometry("%dx%d+0+0" % (width, height))
# Window Title
app_title = "File Structure Info"
self.root.title(app_title)
# Upper and lower Frames
self.upper_frame = ttk.Frame(self.root, relief="sunken", height=120)
self.upper_frame.pack(fill="x", pady=15, padx=30)
self.lower_frame = ttk.Frame(self.root, relief="sunken")
self.lower_frame.pack(fill="both", padx=15, pady=15, expand=True)
# Lower frames' Treeview
self.db_reader = ttk.Treeview(self.lower_frame, columns=("#0"), height=8)
ysb = ttk.Scrollbar(self.db_reader, orient='vertical', command=self.db_reader.yview)
xsb = ttk.Scrollbar(self.db_reader, orient='horizontal', command=self.db_reader.xview)
self.db_reader.configure(yscroll=ysb.set, xscroll=xsb.set)
self.db_reader.heading('#0', text='Path', anchor='w')
self.db_reader.column('#0', minwidth=25, width=70)
self.db_reader.pack(padx=30, pady=30, fill="both", side="left", expand=True)
ysb.pack(fill="y", anchor="e")
xsb.pack(fill="x", anchor="s")
self.info_entry = ttk.Entry(self.lower_frame)
self.info_entry.pack(side="right", fill="both", expand=True)
If I also try to remove the "columns="#0"" from the creation of the Treeview widget, I get what I want, but then nothing show up. Every entry is blank.
Every entry is blank, but indentation seems fine, just need the text in there
Here is the code I found:
import os
try:
# Python2
import Tkinter as tk
import ttk
except ImportError:
# Python3
import tkinter as tk
import tkinter.ttk as ttk
class DirectoryBrowser(tk.Frame):
def __init__(self, master, path):
self.path = os.path.abspath(path)
self.entries = {"": self.path}
self.root = None
# setup treeview and scrollbars
tk.Frame.__init__(self, master)
self.tree = ttk.Treeview(self)
ysb = ttk.Scrollbar(self, orient='vertical', command=self.tree.yview)
xsb = ttk.Scrollbar(self, orient='horizontal', command=self.tree.xview)
self.tree.configure(yscroll=ysb.set, xscroll=xsb.set)
self.tree.heading('#0', text='Path', anchor='w')
self.tree.bind("<<TreeviewSelect>>", self.update_subtree)
# fill treeview with root dir and the subdirs
iid = self.insert("", "end", self.path)
self.tree.focus(iid)
self.process_directory(iid, self.path)
# add tree and scrollbars to frame
self.tree.grid(in_=self, row=0, column=0, sticky="nsew")
ysb.grid(in_=self, row=0, column=1, sticky="ns")
xsb.grid(in_=self, row=1, column=0, sticky="ew")
self.rowconfigure(0, weight=1)
self.columnconfigure(0, weight=1)
self.pack(side=tk.TOP, fill=tk.BOTH, expand=tk.Y)
# testing ...
# returns a list of children belonging to last iid
print(self.tree.get_children(iid))
def insert(self, parent, index, path, name="", **kwargs):
"""
add new element to TreeView
"""
if "text" in kwargs:
err = "arg 'text' not available"
raise ValueError(err)
kwargs["text"] = path
if name:
kwargs["text"] = name
iid = self.tree.insert(parent, index, **kwargs)
self.entries[iid] = path
return iid
def process_directory(self, parent, path, depth=3):
if depth == 0:
return
for p in os.listdir(path):
abspath = os.path.join(path, p)
if os.path.isdir(abspath):
iid = self.insert(parent,
'end',
path=abspath,
name=p,
open=False)
self.process_directory(iid, abspath, depth-1)
# Callbacks
def update_subtree(self, event):
iid = self.tree.focus()
path = self.entries[iid]
print("%s: %s" % (iid, path))
print(iid in self.entries.keys())
#self.process_directory(iid, path)
def test():
root = tk.Tk()
mypath = "F:\\Github_bittib010"
app = DirectoryBrowser(root, path=mypath)
# testing ...
print(app.tree.get_children()) # returns root children
app.mainloop()
if __name__ == "__main__":
test()
Edit as answer to a question.
I used a for loop in its own function to open, add, commit and close. Here is the for-loop and the sqlite statement:
'''
# Execute command
c.execute("SELECT * FROM Windows10")
records = c.fetchall()
# Add data to ttk treeview
counter = 0
for record in records:
self.db_reader.insert('', tk.END, values=record[1], iid=record[0], open=False)
if counter != 0:
self.db_reader.move(record[0], record[5], counter)'''
Related
I use Python 2.7 and I have a scrollable frame where the canvas is not shrinking to fit the frame I want to make scrollable.
I looked at this question for an answer but it does not work when I run it:
How to resize a scrollable frame to fill the canvas?
When I print the width of the frame inside the canvas, it says 0.
I also ran the code from the answer of this question on my computer :
Scrollable Frame does not resize properly using tkinter in Python
but it will still show the white canvas to the left of the labels, and it does not resize when the labels are deleted.
It looks like this:
This is my code, based on the answer in this question:
Adding a scrollbar to a group of widgets in Tkinter
from Tkinter import *
class Scrollable_frame(Frame):
def __init__(self, parent, title, values):
self.parent = parent
Frame.__init__(self, self.parent)
self.canvas = Canvas(self, borderwidth=0, background="#ffffff")
self.scrollbar = Scrollbar(self, command=self.canvas.yview)
self.innerFrame = Radiobutton_frame(self.canvas,title,values)
self.canvas.configure(yscrollcommand=self.scrollbar.set)
self.canvas.grid(row=0, column=0, sticky= N+S)
self.scrollbar.grid(row=0, column=1, sticky = N+S)
self.canvas.create_window((0,0),window = self.innerFrame,anchor="nw")
self.innerFrame.bind("<Configure>", self.set_canvas_scrollregion)
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
def set_canvas_scrollregion(self, event):
width = event.width - 4
self.canvas.itemconfigure("self.innerFrame ", width=width)
self.canvas.config(scrollregion=self.canvas.bbox("all"))
class Radiobutton_frame(LabelFrame):
def __init__(self, parent, title, values):
"""
In: parent - Canvas
title - String
values - List of Int
"""
self.radiobuttons = {}
self.parent = parent
self.selection = StringVar()
self.selection.set("init")
LabelFrame.__init__(self, self.parent, text = title)
for value in values:
self.add_radiobutton(value)
def add_radiobutton(self, value):
"""
Adds a radiobutton to the frame.
In: item - String
"""
# Associate to same variable to make them function as a group
self.radiobuttons[value] = Radiobutton(master = self,
variable = self.selection,
text = value,
value = value)
self.radiobuttons[value].pack(anchor=W)
# Usage example
root = Tk()
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
scrollableFrame = Scrollable_frame(root, "Canvas not resizing", range(30))
scrollableFrame.grid(row=0, column=0, sticky=N+S+E+W)
if __name__ == '__main__':
root.mainloop()
I don't think above question's code snippet fits to a Minimal, Complete, and Verifiable example but at the very least it's runnable.
You have three mistakes compared to that of: How to resize a scrollable frame to fill the canvas?
The most significant of which is that in the linked question, the OP uses the option tags where you don't. Replace:
self.canvas.create_window((0,0),window = self.innerFrame,anchor="nw")
with:
self.canvas.create_window((0,0),window = self.innerFrame, anchor="nw", tags="my_tag")
Another mistake is that you're binding the event of a frame's resizing as opposed to the actual Canvas' resizing, also pointed out in Bryan's comment here. Replace:
self.innerFrame.bind("<Configure>", self.set_canvas_scrollregion)
with:
self.canvas.bind("<Configure>", self.set_canvas_scrollregion)
Lastly, tkinter doesn't seem to accept space character with tags, replace:
self.canvas.itemconfigure("self.innerFrame ", width=width)
with:
self.canvas.itemconfigure("my_tag", width=width)
Finally, you should have:
from Tkinter import *
class Scrollable_frame(Frame):
def __init__(self, parent, title, values):
self.parent = parent
Frame.__init__(self, self.parent)
self.canvas = Canvas(self, borderwidth=0, background="#ffffff")
self.scrollbar = Scrollbar(self, command=self.canvas.yview)
self.innerFrame = Radiobutton_frame(self.canvas,title,values)
self.canvas.configure(yscrollcommand=self.scrollbar.set)
self.canvas.grid(row=0, column=0, sticky= N+S)
self.scrollbar.grid(row=0, column=1, sticky = N+S)
self.canvas.create_window((0,0),window = self.innerFrame,anchor="nw",
tags="my_tag")
self.canvas.bind("<Configure>", self.set_canvas_scrollregion)
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
def set_canvas_scrollregion(self, event):
width = event.width - 4
self.canvas.itemconfigure("my_tag", width=width)
self.canvas.config(scrollregion=self.canvas.bbox("all"))
class Radiobutton_frame(LabelFrame):
def __init__(self, parent, title, values):
"""
In: parent - Canvas
title - String
values - List of Int
"""
self.radiobuttons = {}
self.parent = parent
self.selection = StringVar()
self.selection.set("init")
LabelFrame.__init__(self, self.parent, text = title)
for value in values:
self.add_radiobutton(value)
def add_radiobutton(self, value):
"""
Adds a radiobutton to the frame.
In: item - String
"""
# Associate to same variable to make them function as a group
self.radiobuttons[value] = Radiobutton(master = self,
variable = self.selection,
text = value,
value = value)
self.radiobuttons[value].pack(anchor=W)
# Usage example
root = Tk()
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
scrollableFrame = Scrollable_frame(root, "Canvas not resizing", range(30))
scrollableFrame.grid(row=0, column=0, sticky=N+S+E+W)
if __name__ == '__main__':
root.mainloop()
I'm using the ttk Treeview widget to implement a folder/path selection dialog. It's all working as expected except that my horizontal scrollbar won't activate. No matter how wide the folder path goes horizontally, and no matter how narrow the window, the horizontal slider never appears. Vertical scrolling is working perfectly though.
I'm figuring it's either some kind of limitation when you only use one column in the treeview, or just a newbie mistake with configuring and connecting the widgets. I'd bet on the latter.
Example with dialog widened to show full folder depth:
Dialog narrowed to the point where horizontal scrolling should activate (but doesn't):
Here's my GUI layout code:
winDirSel = tk.Toplevel()
winDirSel.title('Select Test Directory...')
tvwDirSel = ttk.Treeview(winDirSel,
height=10,padding=3,
show='tree')
lblTestDir = tk.Label(winDirSel, relief=tk.SUNKEN,
justify=tk.LEFT, anchor=tk.W,
textvariable=ctrlTestDir,width=80)
scbHDirSel = ttk.Scrollbar(winDirSel,
orient=tk.HORIZONTAL,
command=tvwDirSel.xview)
scbVDirSel = ttk.Scrollbar(winDirSel,
orient=tk.VERTICAL,
command=tvwDirSel.yview)
tvwDirSel.configure(xscrollcommand=scbHDirSel.set,
yscrollcommand=scbVDirSel.set)
lblTestDir.grid(row=0,column=0,sticky=tk.EW)
tvwDirSel.grid(row=1,column=0,sticky=tk.NSEW)
scbVDirSel.grid(row=1,column=1,sticky=tk.NS)
scbHDirSel.grid(row=2,column=0,sticky=tk.EW)
winDirSel.rowconfigure(1,weight=1)
winDirSel.columnconfigure(0,weight=1)
OK, after some playing with minwidth and stretch, I think I have a better handle on it. The horizontal scrolling is triggered by the column-edge going out of the window's bounds, not the content of the column. So you can use these parameters to force the column to be wider and thus force the scrolling.
The problem though is that you then lose the automatic adjustment of the column width to suit the width of the tree itself. You either have to force it very wide to accommodate any (assumed) likely folder depth, or you live with folder names getting truncated at the right boundary of the column.
So bottom line: it's just a limitation of the widget itself. (At least with respect to its behavior on my platform, MS Windows.)
Here's what I finally came up with to display a TreeView of files which are lazy-loaded (thanks to this answer) which is inside a PanedWindow (SplitterWindow in wxPython terms) along with a Notebook. The scrollbars are auto-displayed/hidden as needed, thanks to this example.
import os
import Tkinter as tk
import ttk as ttk
from ScrolledText import ScrolledText
class App(object):
def __init__(self, master, path):
splitter = tk.PanedWindow(master, orient=tk.HORIZONTAL)
# left-side
frame_left = tk.Frame(splitter)
self.tree = ttk.Treeview(frame_left, show='tree')
ysb = ttk.Scrollbar(frame_left, orient='vertical', command=self.tree.yview)
xsb = ttk.Scrollbar(frame_left, orient='horizontal', command=self.tree.xview)
# right-side
frame_right = tk.Frame(splitter)
nb = ttk.Notebook(frame_right)
page1 = ttk.Frame(nb)
page2 = ttk.Frame(nb)
text = ScrolledText(page2)
# overall layout
splitter.add(frame_left)
splitter.add(frame_right)
splitter.pack(fill=tk.BOTH, expand=1)
# left-side widget layout
self.tree.grid(row=0, column=0, sticky='NSEW')
ysb.grid(row=0, column=1, sticky='ns')
xsb.grid(row=1, column=0, sticky='ew')
# left-side frame's grid config
frame_left.columnconfigure(0, weight=1)
frame_left.rowconfigure(0, weight=1)
# right-side widget layout
text.pack(expand=1, fill="both")
nb.add(page1, text='One')
nb.add(page2, text='Two')
nb.pack(expand=1, fill="both")
# setup
self.tree.configure(yscrollcommand=lambda f, l:self.autoscroll(ysb,f,l), xscrollcommand=lambda f, l:self.autoscroll(xsb,f,l))
# use this line instead of the previous, if you want the scroll bars to always be present, but grey-out when uneeded instead of disappearing
#self.tree.configure(yscrollcommand=ysb.set, xscrollcommand=xsb.set)
self.tree.heading('#0', text='Project tree', anchor='w')
self.tree.column("#0",minwidth=1080, stretch=True)
# add default tree node
abspath = os.path.abspath(path)
self.nodes = dict()
self.insert_node('', abspath, abspath)
self.tree.bind('<<TreeviewOpen>>', self.open_node)
def autoscroll(self, sbar, first, last):
"""Hide and show scrollbar as needed."""
first, last = float(first), float(last)
if first <= 0 and last >= 1:
sbar.grid_remove()
else:
sbar.grid()
sbar.set(first, last)
def insert_node(self, parent, text, abspath):
node = self.tree.insert(parent, 'end', text=text, open=False)
if os.path.isdir(abspath):
self.nodes[node] = abspath
self.tree.insert(node, 'end')
def open_node(self, event):
node = self.tree.focus()
abspath = self.nodes.pop(node, None)
if abspath:
self.tree.delete(self.tree.get_children(node))
for p in os.listdir(abspath):
self.insert_node(node, p, os.path.join(abspath, p))
if __name__ == '__main__':
root = tk.Tk()
root.geometry("800x600")
app = App(root, path='.')
root.mainloop()
import tkinter as tk
import tkinter.ttk as ttk
import tkinter.font as tk_font
class TreeListBox:
def __init__(self, master, root, dict_group):
self.master = master
self.root = root
self.dict_group = dict_group
self.level = 0
self.setup_widget_tree()
self.build_tree(self.root, '')
def setup_widget_tree(self):
container_tree = tk.Frame(self.master, width=250, height=300)
container_tree.propagate(False)
container_tree.pack(side="left", fill='y')
self.tree = ttk.Treeview(container_tree, show="tree", selectmode='browse')
fr_y = tk.Frame(container_tree)
fr_y.pack(side='right', fill='y')
tk.Label(fr_y, borderwidth=1, relief='raised', font="Arial 8").pack(side='bottom', fill='x')
sb_y = tk.Scrollbar(fr_y, orient="vertical", command=self.tree.yview)
sb_y.pack(expand='yes', fill='y')
fr_x = tk.Frame(container_tree)
fr_x.pack(side='bottom', fill='x')
sb_x = tk.Scrollbar(fr_x, orient="horizontal", command=self.tree.xview)
sb_x.pack(expand='yes', fill='x')
self.tree.configure(yscrollcommand=sb_y.set, xscrollcommand=sb_x.set)
self.tree.pack(fill='both', expand='yes')
def build_tree(self, parent, id_stroki):
self.level += 1
id = self.tree.insert(id_stroki, 'end', text=parent)
# -----------------
col_w = tk_font.Font().measure(parent)
if col_w > 1000:
col_w -= 400
elif col_w > 500:
col_w -= 200
elif col_w > 300:
col_w -= 100
col_w = col_w + 25 * self.level
if col_w > self.tree.column('#0', 'width'):
self.tree.column('#0', width=col_w)
# -----------------
for element in sorted(self.dict_group[parent]):
self.build_tree(element, id)
self.level -= 1
if __name__ == '__main__':
dict_group = {'Nomenclature': ['ABC1', 'ABC2'],
'ABC1': ['ABC3', 'ABC4'],
'ABC2': ['ABC5'],
'ABC3': ['ABC______________________________________6'],
'ABC4': ['ABC--------------------------------------8'],
'ABC5': ['ABC######################################9'],
'ABC______________________________________6': [],
'ABC--------------------------------------8': [],
'ABC######################################9': []
}
root = tk.Tk()
myTest = TreeListBox(root, 'Nomenclature', dict_group)
root.mainloop()
I've looked at other question with a similar title and have read the answers, however nothing has worked for me. I am trying to make a simple app with a listbox + scroll bar with two buttons below it all within a group box. I've used pyqt but this is my first time using tkinter:
import tkinter as tk
class InputWindow(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.initialize()
def initialize(self):
# Group box to contain the widgets
self.input = tk.LabelFrame(self, text="Input Files")
# Listbox with scrollbar to the side
self.listbox = tk.Listbox(self.input)
self.scrollbar = tk.Scrollbar(self.listbox, orient=tk.VERTICAL)
self.listbox.config(yscrollcommand=self.scrollbar.set)
self.scrollbar.config(command=self.listbox.yview)
self.listbox.grid(row=0, column=0, columnspan=2)
self.add_btn = tk.Button(self.input, text="Add...")
self.add_btn.grid(row=1, column=0)
self.remove_btn = tk.Button(self.input, text="Remove")
self.remove_btn.grid(row=1, column=1)
if __name__ == "__main__":
root = tk.Tk()
app = InputWindow(root)
root.mainloop()
This is more or less what I want but in tkinter:
What am I doing wrong/how can this be done?
You're forgetting two things:
To pack (or grid or place) app
To pack (or grid or place) input
You're program with the required statements:
import tkinter as tk
class InputWindow(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.initialize()
def initialize(self):
# Group box to contain the widgets
self.input = tk.LabelFrame(self, text="Input Files")
# Listbox with scrollbar to the side
self.listbox = tk.Listbox(self.input)
self.scrollbar = tk.Scrollbar(self.listbox, orient=tk.VERTICAL)
self.listbox.config(yscrollcommand=self.scrollbar.set)
self.scrollbar.config(command=self.listbox.yview)
self.listbox.grid(row=0, column=0, columnspan=2)
self.add_btn = tk.Button(self.input, text="Add...")
self.add_btn.grid(row=1, column=0)
self.remove_btn = tk.Button(self.input, text="Remove")
self.remove_btn.grid(row=1, column=1)
self.input.pack(expand=1, fill="both") # Do not forget to pack!
if __name__ == "__main__":
root = tk.Tk()
app = InputWindow(root)
app.pack(expand=1, fill="both") # packing!
root.mainloop()
I want to create a custom widget in tkinter such that when instantiated, displays a label and an entry box. Example I created a class named entry and call as.. entry ('name', master ) and this would display a label with text as main along side an entry box.
I have succeeded in doing that but my problem is with the geometry managers. they all seem to mess up everything
Your widget should subclass Frame. Within the frame you can use any geometry manager you want without affecting any other code. It's important that the widget class does not call grid, pack or place on itself -- that's the job of the function that creates the widget. Every widget, or function that creates a widget, should only ever worry about laying out its children.
Here's an example that creates a couple of different custom widgets. Each uses a different geometry manager to illustrate that they don't interfere with each other:
try:
# python 3.x
import tkinter as tk
except ImportError:
# python 2.x
import Tkinter as tk
class CustomWidget(tk.Frame):
def __init__(self, parent, label, default=""):
tk.Frame.__init__(self, parent)
self.label = tk.Label(self, text=label, anchor="w")
self.entry = tk.Entry(self)
self.entry.insert(0, default)
self.label.pack(side="top", fill="x")
self.entry.pack(side="bottom", fill="x", padx=4)
def get(self):
return self.entry.get()
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.label = tk.Label(self)
self.e1 = CustomWidget(self, "First Name:", "Inigo")
self.e2 = CustomWidget(self, "Last Name:", "Montoya")
self.submitButton = tk.Button(self, text="Submit", command=self.submit)
self.e1.grid(row=0, column=0, sticky="ew")
self.e2.grid(row=1, column=0, sticky="ew")
self.label.grid(row=2, column=0, sticky="ew")
self.submitButton.grid(row=4, column=0)
self.grid_columnconfigure(0, weight=1)
self.grid_rowconfigure(2, weight=1)
def submit(self):
first = self.e1.get()
last = self.e2.get()
self.label.configure(text="Hello, %s %s" % (first, last))
if __name__ == "__main__":
root = tk.Tk()
Example(root).place(x=0, y=0, relwidth=1, relheight=1)
root.mainloop()
I agree with Mr. Oakley. You should subclass frame to do your job.
The simplest way to do what you want is to create a module with the following code:
# AnnotatedEntry.py
def AnnotatedEntry(master, name="An annoted entry box"):
'''
As a little extra, name is a keyword-argument, which defaults to "An annotated
entry box."
'''
import tkinter as tk
overlord = tk.Frame(master, height=5, width=40)
labeller = tk.Label(overlord, text=name, font="Times 14 bold")
labeller.grid(sticky='new')
inputter = tk.Entry(overlord, font="Times 14 bold")
inputter.grid(sticky='sew', pady=(10,0))
return overlord
This would be used as follows:
# Main program
import tkinter
import AnnotatedEntry
root = tkinter.Tk()
hold = AnnotatedEntry.AnnotatedEntry(root, name="Hello, world!")
hold.grid()
I hereby affirm, on my Scout Honor, that this code has been fully tested, and is guaranteed to work in Python 3.7.4. That being said, there is currently no method for returning the data contained in the Entry; you will have to work that out for yourself.
Based on #Bryan Oakley answer, I do have some modification. I know it's out of topic somehow. This is how to return a value from the widget and it only allows integer up to some number of digits that the user must entered.
#create a global value
global tbVal
tbVal = 0
class CustomWidget(tk.Frame):
def __init__(self, parent, nDigits):
tk.Frame.__init__(self, parent)
self.entry = tk.Entry(self)
self.entry.pack(side="bottom", fill="x", padx=4)
self.entry.configure(validate='all',validatecommand=windows.register(self.sbValidate),'%P','%W',nDigits))
def get(self):
return self.entry.get()
def sbValidate(self, userInput, widget, nDigits):
global tbVal
tbVal = userInput
if userInput == '':
return True
if '.' in userInput or ' ' in userInput:
return False
n = len(userInput)
if n > int(nDigits):
return False
try:
val = int(float(userInput))
except ValueError:
return False
return val
class Example(tk.Frame):
def __init__(self, parent, nDigitsLimit):
tk.Frame.__init__(self, parent)
self.e1 = CustomWidget(self, nDigitsLimit)
self.e1.grid(row=0, column=0, sticky="ew")
self.grid_columnconfigure(0, weight=1)
self.grid_rowconfigure(2, weight=1)
def btnStartClick():
print(tbVal)
nDigitsLimit = 8
tbTest = ttk.Entry(Example(windows, nDigitsLimit).place(x=20, y=20, relwidth=0.25, relheight=0.05))
btnStart = tk.Button(frame, text='Start', command=btnStartClick)
btnStart.place(relx=0.50, rely=0.50)
I am using the grid geometry manager to create a table with the following widget structure:
Radiobuttons in columns 0 and 1
Horizontally scrollable Text (representing potentially long file names) in column 2
and progress bars in column 6 (with numerical values for columns 3 - 5 to be filled in later).
It's the horizontally scrollable Text that's giving me trouble. I tried creating a Listbox widget that lives in column 2 (as a child of the overall frame) and spans all the rows. This seemed promising, until it became apparent that the lines of text in the Listbox are not aligned with the rows of the parent grid.
I've been searching in vain for a way to pad each row of text in the Listbox so that the rows match up; but even if that were possible, I would prefer a more general, less kludgy solution.
Is there perhaps an easy way to make a single column scrollable that I'm missing? Is setGrid a possible solution?
(In order to see the problem with the code below, use the Select File or Select Folder buttons to load multiple audio files into the file list.)
#! /usr/bin/env python
#################################################
# This tool allows the user to select audio files
# (or folders containing audio files) and subject
# them to loudness analysis.
#################################################
import sys
import os
import codecs
import re
import Tkinter
from Tkinter import *
import tkFileDialog
from os import walk
from os import path
from Tkinter import Tk, Text, BOTH, W, N, E, S
from ttk import Frame, Button, Label, Style, Progressbar
from ScrolledText import *
from progressbar import ProgressBar
class Leveler_tk(Frame):
fileList = []
allowedExtensions = ['mp3','mp2','m4a','aiff','wav']
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initialize()
def initialize(self):
self.style = Style()
self.style.theme_use("default")
self.pack(fill=BOTH, expand=1)
self.columnconfigure(1, weight=1)
self.columnconfigure(2,weight=1)
self.columnconfigure(2,pad=250)
self.columnconfigure(3, weight=1)
self.columnconfigure(4, weight=1)
self.columnconfigure(5, weight=1)
lbl1 = Label(self, text="Analyze")
lbl1.grid(pady=4, padx=5,row=0,column=0)
lbl2 = Label(self, text="Adjust")
lbl2.grid(pady=4, padx=5,row=0,column=1)
lbl3 = Label(self, text="File")
lbl3.grid(pady=4, padx=5,row=0,column=2)
lbl4 = Label(self, text="Integrated\nLoudness")
lbl4.grid(pady=4, padx=5,row=0,column=3)
lbl5 = Label(self, text="LRA")
lbl5.grid(pady=4, padx=5,row=0,column=4)
lbl6 = Label(self, text="Peak")
lbl6.grid(pady=4, padx=5,row=0,column=5)
lbl7 = Label(self, text="Progress")
lbl7.grid(pady=4, padx=5,row=0,column=6)
lbl8 = Label(self, text="Meets\nSpecs?")
lbl8.grid(sticky=W, pady=4, padx=5,row=0,column=7)
file_btn = Button(self, text="Select File",command=self.selectFile)
file_btn.grid(row=1,rowspan=2, column=8,padx=5,pady=4)
folder_btn = Button(self, text="Select Folder", command=self.selectFolder)
folder_btn.grid(row=3, rowspan=2, column=8,padx=5,pady=4)
def render(self):
count = 0
filebox = Listbox(self,selectmode=EXTENDED,setgrid=1)
scrollbar = Scrollbar(filebox, orient=HORIZONTAL)
scrollbar.config(command=filebox.xview)
filebox.grid(row=1, column=2, rowspan=len(self.fileList), columnspan=1, sticky=N+S+E+W)
filebox.config(xscrollcommand=scrollbar.set)
scrollbar.pack(side=BOTTOM, fill=X)
for file in self.fileList:
analyze = IntVar()
adjust = IntVar()
Radiobutton(self, text="", variable=analyze, value=count, borderwidth=0).grid(row=count+1, column=0)
Radiobutton(self, text="", variable=adjust, value=count, borderwidth=0).grid(row=count+1, column=1)
filebox.insert(END, file + "\n")
Progressbar(self, orient=HORIZONTAL,length=100, mode='determinate').grid(row=count+1, column=6)
count += 1
def addToList(self, name):
dot = re.search("\.(?=[^.]*$)",name)
extension = name[dot.end():]
if extension in self.allowedExtensions and not name in self.fileList:
self.fileList.append(name)
def selectFile(self):
input = tkFileDialog.askopenfilename(filetypes = [('MP3', '*.mp3'), ('MP2', '*.mp2'), ('M4A', '*.m4a'), ('AIFF', '*.aiff'), ('WAV', '*.wav')], multiple = 1)
for el in input:
if os.path.isfile(el) and ".DS_Store" not in el:
try:
self.addToList(el)
except:
tkMessageBox.showerror("Some error")
self.render()
def selectFolder(self):
input = tkFileDialog.askdirectory()
for (dirpath, dirnames, filenames) in walk(input):
for name in filenames:
if name != ".DS_Store":
self.addToList(dirpath + "/" + name)
self.render()
def main():
root = Tk()
app = Leveler_tk(root)
root.mainloop()
if __name__ == "__main__":
main()