ttk: how to make a frame look like a labelframe? - python

Python ttk gui creating is easy and has mostly native look and feel (win7). I am having a slight problem, though:
I would like to have a frame that looks like ttk.LabelFrame but without label. Just omitting the text option will leave an ugly gap.
Also I can not get the ttk.Frame border to look like LabelFrame. Is there an elegant way of doing this? Bonus karma if this works on all/most windows versions above xp.
Maybe it works with styles but the style LabelFrame properties seem mostly empty (style.element_options("border.relief")). Maybe I am looking in the wrong place.
edit:
try: # python 3
from tkinter import * # from ... import * is bad
from tkinter.ttk import *
except:
from Tkinter import *
from ttk import *
t = Tcl().eval
print("tcl version: " + str(t("info patchlevel")))
print("TkVersion: " + str(TkVersion))
root = Tk()
lF = LabelFrame(root, text=None)
lF.grid()
b = Button(lF, text='gonzo')
b.grid()
f = Frame(root, relief="groove") #GROOVE)
f.grid()
b2 = Button(f, text='gonzo')
b2.grid()
f2 = Frame(root, relief=GROOVE, borderwidth=2)
f2.grid()
b3 = Button(f2, text='gonzo')
b3.grid()
mainloop()
output on win7 with freshly downloaded python 3.2.3:
tcl version: 8.5.9
TkVersion: 8.5
There is python 2.6.6 installed on this machine, too (same problem). Each installation seems to be using the correct tk/tcl, though.

OK, seems like one solution to getting the LabelFrame without the gap is to use an empty widget in place of the text. So, modifying the first part of your example slightly:
# don't do the 'from tkinter import *' thing to avoid namespace clashes
import tkinter # assuming Python 3 for simplicity's sake
import tkinter.ttk as ttk
root = tkinter.Tk()
f = tkinter.Frame(relief='flat')
lF = ttk.LabelFrame(root, labelwidget=f, borderwidth=4)
lF.grid()
b = ttk.Button(lF, text='gonzo')
b.grid()
root.mainloop()
Seems to work for me with the regular Win7 theme.

Related

root = tkinter.Tk() or root = Tk()?

I have two scripts that both work:
import tkinter
root = tkinter.Tk()
root.configure(bg='blue')
root.mainloop()
and
from tkinter import *
root = Tk()
text = Text(root)
text.insert(INSERT, "Hello world!")
text.pack()
root.mainloop()
I want to combine the two scripts to print the text on a blue background, but moving anything from one script to another seems to break it.
I can't figure out if it's about root = tkinter.Tk() vs root = Tk(), or import tkinter vs from tkinter import *, or something entirely different. I can't find a successful combination.
I'm using Ubuntu and Python 3.6.9.
Because you use two different styles when importing tkinter, you will need to modify the code from one file when moving to the other. The code in your first example is the preferred way to do it because PEP8 discourages wildcard imports.
When when you copy the code from the second example, you'll need to add tkinter. to every tkinter command (tkinter.Tk(), tkinter.Text(root), tk.INSERT, etc.
Personally I find import tkinter as tk to be a slight improvement. I find tk.Tk() to be a little easier to type and read than tkinter.Tk().
You should know that:
from tkinter import *
will import all the attribute in the tkinter.But if you also define some variable in your script.It will be covered by your new variable.So we don't recommend you to use that.(If you used both from tkinter.ttk import * and from tkinter import *.Some default widgets of tkinter will be covered by ttk widgets.)
Just like Mr.Bryan said,I'd like to use import tkinter as tk,too.

How to position a Textbox with Tkinter in Python?

I'm learning Tkinter for GUI. I write the following simple code, I can't change the location of the Textbox even if I try different values in the grid. Whatever value I write, the Textbox always comes at the left up corner.
from tkinter import *
startingWindow = Tk()
startingWindow.title("Hello")
startingWindow.geometry("400x300")
myEntry = Entry(startingWindow, width=10)
myEntry.grid(column=15, row=15)
startingWindow.mainloop()
Instead, I get the following output.
Note: I'm using Python 3.6 under Windows 7
use place instead of grid like this:
from tkinter import *
startingWindow = Tk()
startingWindow.title("Hello")
startingWindow.geometry("400x300")
myEntry = Entry(startingWindow, width=10)
myEntry.place(x=15,y=15) # here
startingWindow.mainloop()

Select several directories with tkinter

I have to perform an operation on several directories.
TKinter offers a dialog for opening one file (askopenfilename), and several files (askopenfilenames), but is lacking a dialog for several directories.
What is the quickest way to get to a feasible solution for "askdirectories"?
Unfortunately, tkinter doesn't natively support this. A nice looking alternative is tkfilebrowser. Code based on Luke's answer using tkfilebrowser is below:
import tkfilebrowser
from tkinter import *
root = Tk()
root.geometry('200x200')
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
dirs = []
def get_directories():
dirs.append(tkfilebrowser.askopendirnames())
return dirs
b1 = Button(root, text='select directories...', command=get_directories)
b1.pack()
root.mainloop()
The only way for doing this in pure tkinter (except building the directory selector widget by hand) is requesting user for each dir in separate dialog. You could save previously used location, so user won't need to navigate there each time, by using code of below:
from tkinter import filedialog
dirselect = filedialog.Directory()
dirs = []
while True:
d = dirselect.show()
if not d: break
dirs.append(d)
Another solution is to use tkinter.tix extension (now part of standard lib, but may require to install Tk's Tix on some platforms). Primarly, you'll need the tkinter.tix.DirList widget. It look as follows (a bit old img):
For more, see tkinter.tix and Tk Tix docs
You should be able to use tkFileDialog.askdirectory. Take a look at the docs here :)
EDIT
Perhaps something like this?
from Tkinter import *
import tkFileDialog
root = Tk()
root.geometry('200x200')
root.grid_rowconfigure(0, weight = 1)
root.grid_columnconfigure(0, weight = 1)
dirs = []
def get_directories():
dirs.append(tkFileDialog.askdirectory())
return dirs
b1 = Button(root, text='select directories...', command = get_directories)
b1.pack()
root.mainloop()
Any thoughts?

Why can't I define (and save) Tkinter fonts inside a function?

Defining a font inside a function and in the main body of the script seems to behave differently, and I can't seem to figure out how it's supposed to work.
For example, the Label in this example ends up being in a larger font, as expected:
from Tkinter import *
from ttk import *
import tkFont
root = Tk()
default = tkFont.Font(root=root, name="TkTextFont", exists=True)
large = default.copy()
large.config(size=36)
style = Style(root)
style.configure("Large.TLabel", font=large)
root.title("Font Test")
main_frame = Frame(root)
Label(main_frame, text="Large Font", style="Large.TLabel").pack()
main_frame.pack()
root.mainloop()
However, if I try to define styles inside a function, it seems like the font gets deleted or garbage collected and is not available by the time the widget needs to use it:
from Tkinter import *
from ttk import *
import tkFont
def define_styles(root):
default = tkFont.Font(root=root, name="TkTextFont", exists=True)
large = default.copy()
large.config(size=36)
style = Style(root)
style.configure("Large.TLabel", font=large)
root = Tk()
root.title("Font Test")
define_styles(root)
main_frame = Frame(root)
Label(main_frame, text="Large Font", style="Large.TLabel").grid(row=0, column=0)
main_frame.pack()
root.mainloop()
Printing out tkFont.names() in the first version just before the main_frame.pack() lists the custom font as font<id>, but printing the same in the second version does not list the custom font outside the define_styles function. Do I have to do something special to save them?
Why can't I put that code in a function? Am I fundamentally misunderstanding something about how Fonts are supposed to be used? tkFont seems to have some kind of font registry, why aren't mine sticking around?
I have no evidence to back this up, but I believe that your large Font object is being garbage collected by Python once define_styles ends. This is because no pure Python objects have any references to it, even though the underlying Tcl implementation is still using it. This is a problem that afflicts Tkinter's PhotoImage class, as well.
The workaround is to keep the object alive by making a long-lived reference to it. Just assign it to any old attribute on the root object, for example.
def define_styles(root):
default = tkFont.Font(root=root, name="TkTextFont", exists=True)
large = default.copy()
large.config(size=36)
style = Style(root)
style.configure("Large.TLabel", font=large)
root.myfont = large
Result:

ttk OptionMenu does not fit into its bounding box

I am facing a problem creating a Tkinter-application under Windows, using python 2.7. Basically, when I create an OptionMenu, its right corner (where a down button indicates that something happens when you click there) is truncated in the middle.
The following code reproduces the issue:
from Tkinter import Tk, StringVar
from ttk import OptionMenu
root = Tk()
options = list('ABC')
var = StringVar(value='A')
om = OptionMenu(root, var, var.get(), *options)
om.config(width=25)
om.pack()
root.mainloop()
The result looks on my computer like this:
I have played around with the padx and ipadx keywords of the packing layout manager and also tried a grid layout instead. None of them lets me see the down-arrow completely.
I appreciate your helpful comments on this issue.
The same happens to me on Windows 7 but not on XP, both using Python 2.7. I have found a bug report which states is should be fixed in Tk 8.5.8. Updating Tcl/Tk in Python seems to be very complicated though
The fix in question is for one of the script files shipped in the tk library. You could modify your local copy of vistaTheme.tcl to match this. In later versions I think it does actually request the size from the system properly but this should work if you are forced to use an older version of Tk.
You can find the path using:
from Tkinter import Tk
tk = Tk()
tk.eval("set tk_library")
and then edit the /ttk/vistaTheme.tcl file. I've got python3 here and it seems to have come with Tk 8.6.1 so has this fixed already.

Categories