Python Megawidgets tab widths - python

How to I change the default width of the tabs on a notebook page?
import tkinter #widget library ships with Python
import Pmw #allows access to update tkinter widgets
class TextBookGUI:
# class constructor
# populates each note book page
def __init__(self, master):
#place hash tables here
"""
Create 5 pages on using Pmw notebook widget.
Documenation for notebook:
http://pmw.sourceforge.net/doc/NoteBook.html
"""
self.nb = Pmw.NoteBook(master)
Pmw.Color.changecolor(self.nb.component('hull'), background='blue')
self.HomeTab = self.nb.add("Welcome")
self.nb.tab('Welcome').focus_set()
self.StudentTab = self.nb.add("Students")
self.BookTab = self.nb.add("Books")
self.LoanTab = self.nb.add("Loans")
self.HelpTab = self.nb.add("Help")
self.nb.pack(fill = 'both', expand = 1, padx = 10, pady = 10)
self.nb.setnaturalsize()
#format the house style of tabs: yellow bg and blue text
self.nb.component('Welcome-tab').configure(font= ('Helvetica', 7 ,'bold', 'italic'), width= 30,fg= "yellow", bg="blue")
I need to set the default tab width to be wider to accomodate the tab names. I cannot access the tab width property. The last line I tried using .configure(width=30), this had no effect on the tab!

OK , I have got the solution , although ugly. It does manipulate tab widths.
class TextBookGUI:
# class constructor
# populates each note book page
def __init__(self, master):
#place hash tables here
"""
Create 5 pages on using Pmw notebook widget.
Documenation for notebook:
http://pmw.sourceforge.net/doc/NoteBook.html
"""
Pmw.initialise(master)
self.nb = Pmw.NoteBook(master,borderwidth=2,arrownavigation=True,tabpos='n')
self.HomeTab = self.nb.add("Welcome")
self.nb.tab('Welcome').focus_set()
self.StudentTab = self.nb.add("Students")
self.BookTab = self.nb.add("Books")
self.LoanTab = self.nb.add("Loans")
self.HelpTab = self.nb.add("Help")
*self.nb._pageAttrs['Welcome']['tabreqwidth'] = 200
self.nb._pageAttrs["Welcome"]['tabreqheight'] = 100
self.nb._pageAttrs["Students"]['tabreqwidth'] = 200
self.nb._pageAttrs["Students"]['tabreqheight'] = 100
self.nb._pageAttrs["Books"]['tabreqwidth'] = 200
self.nb._pageAttrs["Books"]['tabreqheight'] = 100
self.nb._pageAttrs['Loans']['tabreqwidth'] = 200
self.nb._pageAttrs["Loans"]['tabreqheight'] = 100
self.nb._pageAttrs['Help']['tabreqwidth'] = 200
self.nb._pageAttrs["Help"]['tabreqheight'] = 100*
#format the house style of tabs: yellow bg and blue text
self.nb.component('Welcome-tab').configure(font= ('Helviticva',20 ,'bold italic'),
fg= "yellow",bg="blue",wraplength=150)
self.nb.component('Students-tab').configure(font= ('Helviticva',20 ,'bold italic'),
fg= "yellow",bg="blue",wraplength=150)
self.nb.component('Books-tab').configure(font= ('Helviticva',20 ,'bold italic'),
fg= "yellow",bg="blue",wraplength=150)
self.nb.component('Loans-tab').configure(font= ('Helviticva',20 ,'bold italic'),
fg= "yellow",bg="blue",wraplength=150)
self.nb.component('Help-tab').configure(font= ('Helviticva',20,'bold italic'),
fg= "yellow",bg="blue",wraplength=150)
self.nb.pack(fill = 'both', expand = 1, padx = 10, pady = 10)
self.nb.setnaturalsize()
Having searched the net ,someone suggested iterating the page attribute dictionary with iterrkeys(). This did not work so I took the code out of the loop and used the attributes from the pageAttrs dictionary, which were were [reqtabwidth], [reqtabheight]. It worked!The code has stars around it and was added to the code above.You can see the text size is now 20 and it displays in a nice large tab.That is two evenings wasted for such a simple task.

Annoyingly, while you've correctly set the tab width within the widget (I can see it using cget('width'), the megawidget doesn't appear to honor the width setting.
However, it does resize each tab to ensure that it's wide enough to correctly display the text. If you're not seeing the full text of each tab, it may be because the root window isn't sized wide enough to allow the full width to be displayed. Try adding a forced size (e.g., root.geometry('500x200')) and see if that helps.
I'd love to have a better answer for you, but that's the best I can do. Good luck!

Related

How to dynamically wrap label text in tkinter with .bind and <Configure>

I'm trying to create a page that outputs a large amount of data, and wraps the text dynamically depending on window size. I started by setting wraplength = self.master.winfo_width(), which sets the text wrapping to the current window size, but it does not change when the window does. I found this answer, which seemed like it would solve the problem, but when trying to recreate it myself, something went wrong. I suspect that I'm misunderstanding something with .bind or <Configure>, but I can't be sure. My base code is as follows:
from tkinter import *
class Wrap_example(Frame):
def __init__(self):
Frame.__init__(self)
self.place(relx=0.5, anchor='n')
#Initalize list and variable that populates it
self.data_list = []
self.data = 0
#Button and function for creating a bunch of numbers to fill the space
self.button = Button(self, text = "Go", command = self.go)
self.button.grid()
def go(self):
for self.data in range(1, 20000, 100):
self.data_list.append(self.data)
#Label that holds the data, text = list, wraplength = current window width
self.data = Label(self, text = self.data_list, wraplength = self.master.winfo_width(), font = 'arial 30')
self.data.grid()
#Ostensibly sets the label to dynamically change wraplength to match new window size when window size changes
self.data.bind('<Configure>', self.rewrap())
def rewrap(self):
self.data.config(wraplength = self.master.winfo_width())
frame01 = Wrap_example()
frame01.mainloop()
A few things of note: I tried using the lambda directly as shown in the linked answer, but it didn't work. If I remove the rewrap function and use self.data.bind('<Configure>', lambda e: self.data.config(wraplength=self.winfo_width()), it throws a generic Syntax error, always targeting the first character after that line, (the d in def if the function is left in, the f in frame01 if it's commented out). Leaving rewrap as-is doesn't throw an error, but it doesn't perform any other apparent function, either. Clicking 'Go' will always spawn data that wraps at the current window size, and never changes.
There are few issues:
frame Wrap_example does not fill all the horizontal space when window is resized
label self.data does not fill all the horizontal space inside frame Wrap_example when the frame is resized
self.rewrap() will be executed immediately when executing the line self.data.bind('<Configure>', self.rewrap())
To fix the above issues:
set relwidth=1 in self.place(...)
call self.columnconfigure(0, weight=1)
use self.data.bind('<Configure>', self.rewrap) (without () after rewrap) and add event argument in rewrap()
from tkinter import *
class Wrap_example(Frame):
def __init__(self):
Frame.__init__(self)
self.place(relx=0.5, anchor='n', relwidth=1) ### add relwidth=1
self.columnconfigure(0, weight=1) ### make column 0 use all available horizontal space
#Initalize list and variable that populates it
self.data_list = []
self.data = 0
#Button and function for creating a bunch of numbers to fill the space
self.button = Button(self, text = "Go", command = self.go)
self.button.grid()
def go(self):
for self.data in range(1, 20000, 100):
self.data_list.append(self.data)
#Label that holds the data, text = list, wraplength = current window width
self.data = Label(self, text = self.data_list, wraplength = self.master.winfo_width(), font = 'arial 30')
self.data.grid()
#Ostensibly sets the label to dynamically change wraplength to match new window size when window size changes
self.data.bind('<Configure>', self.rewrap) ### remove () after rewrap
def rewrap(self, event): ### add event argument
self.data.config(wraplength = self.master.winfo_width())
frame01 = Wrap_example()
frame01.mainloop()

Double Underline text Tkinter

please I need help using double underlines for a text in tkinter.
Sample code is:
allbl=tk.Label(hmpage,text='Purchases', font='calibri 25 bold doubleunderline',fg='white',bg='#2a475e')
allbl.place(relx=0.45,rely=0.25,anchor="center")
There is no font option for double underlining, therefore this cannot be done with a simple Label widget. However, one can create a custom class based on a Canvas to draw two lines below the text:
import tkinter as tk
from tkinter.font import Font
class DblUnderlineLabel(tk.Canvas):
def __init__(self, master, **kw):
# collect text's properties
font = Font(master, kw.pop('font', ''))
text = kw.pop('text', '')
if 'fg' in kw:
fg = kw.pop('fg')
else:
fg = kw.pop('foreground', 'black')
# initialize the canvas
tk.Canvas.__init__(self, master, **kw)
# display the text
self.create_text(2, 2, anchor='nw', font=font, text=text, fill=fg, tags='text')
h = font.metrics('linespace') # font property needed to position correctly the underlining
bbox = self.bbox('text') # get text bounding box in the canvas
w = font.actual('size')//8 # scale thickness of the underlining with fontsize
# create the double underlining
self.create_line(bbox[0], h - 1, bbox[2], h - 1, fill=fg, width=w, tags='line')
self.create_line(bbox[0], h + int(1.1*w), bbox[2], h + int(1.1*w), fill=fg, width=w, tags='line')
# resize the canvas to fit the text
bbox = self.bbox('all')
self.configure(width=bbox[2], height=bbox[3])
root = tk.Tk()
for size in [8, 16, 32, 64]:
DblUnderlineLabel(root, text="Arial %i bold" % size, font="Arial %i bold" % size).pack()
root.mainloop()
Text sample:
It seems that you are using the font attribute incorrectly.
I would say...
Instead of writing this:
font='calibri 25 bold doubleunderline'
Write this:
font=('calibri', 25, 'bold underline')
Also I didn't write doubleunderline as nothing like that exists in tkinter so you would get a straight Traceback.
So the corrected code would be:
allbl=tk.Label(hmpage,text='Purchases', font=('calibri', 25, 'bold underline'),fg='white',bg='#2a475e')
allbl.place(relx=0.45,rely=0.25,anchor="center")
After a long search, I have to terms with the sad reality as at this time of writing that tkinter doesn't support the double-underline feature.

Same configuration of all Label Widgets in Tkinter

Is there a way to use same configuration in the labels I want. For example, we put configuration in a variable and use as required:
from tkinter import *
mainWindow = Tk()
mainFrame = Frame(mainWindow,bg="Red")
mainFrame.pack(fill="both",expand=True)
# Create a variable that contains the configuration for Label
LabelSettings = '''font=("Arial", 18),bg="Black", fg="white", padx=5, pady=5'''
label = Label(mainFrame,text="Vertical Frame Example")
label.config(LabelSettings)
label.pack(fill="x")
mainWindow.mainloop()
What you could be also doing is making the options in a dictionary and then passing it on to the config() method, like:
LabelSettings = {'font':("Arial", 18),'bg':"Black",'fg':"white",'padx':5, 'pady':5}
....
label.config(LabelSettings)
And, if you want to pass it onto the initial creation of the widget, then you will need to specify it as keyword argument(**) in your example, like:
label = Label(mainFrame,text="Vertical Frame Example",**LabelSettings)
As said by jasonharper.
Or you could also replace your label.config(LabelSettings) with:
exec(f'label.config({LabelSettings})') #might not be recommended
Tkinter maintains an internal database of default option values (called the options database). If you want to change the default for all widgets, you can just change the database value and all widgets will pick up the changes unless overridden by a specific widget.
For example, you could add the following to your code to achieve what you want:
mainWindow.option_add("*Label*Font", ("Helvetica", 32))
mainWindow.option_add("*Label*Foreground", "white")
mainWindow.option_add("*Label*Background", "red")
mainWindow.option_add("*Label*padX", 5)
mainWindow.option_add("*Label*padY", 5)
from tkinter import *
mainWindow = Tk()
mainFrame = Frame(mainWindow,bg="Red")
mainFrame.pack(fill="both",expand=True)
Store everything in a separate variable, so it's easier to change.
# Create a variable that contains the
configuration for Label
labelFont = ('Arial',18)
labelBG = 'Black'
labelFG = 'white'
labelPadx = 5
labelPady = 5
label = Label(mainFrame,text="Vertical Frame
Example")
label.config(font = labelFont, bg = labelBG, fg =
labelFG, padx = labelPadx, pady = labelPady)
label.pack(fill="x")
mainWindow.mainloop()

tkinter grid manager behaviour

So i want to build an assistant off sorts which will do auto backs ups etc and instead of using .place i would like a proper grid to place widgets.
I cannot find a good example of the grid manager.
self.parent = tk.Frame(window, bg = BLACK)
username_label = ttk.Label(self.parent, text = "Username")
password_label = ttk.Label(self.parent, text = "Password")
self.parent.grid(column = 0, row = 0)
username_label.grid(column = 1, row = 1)
password_label.grid(column = 2, row = 2)
self.parent.grid_columnconfigure(0, weight = 1)
I want...
Button
Button
Label Entry Button
Label Entry Button
Button
I don't understand how i can position them like this as i want a blank space above the labels. so far grid has only let me place things next to each other.
Honestly, any websites or code examples would be greatly appreciated
So, if you want blank space above the label, you can either set pady as an argument to the grid method or simply put them in the corresponding row. Consider the following example:
import tkinter as tk
root=tk.Tk()
for i in range(6):
tk.Button(root,text='Button %d'%i).grid(row=i,column=1)
tk.Label(root,text='Label 0').grid(row=2,column=0,pady=20)
tk.Label(root,text='Label 1').grid(row=3,column=0)
root.mainloop()
Notice the effect of the pady argument. Also, if you only want a blank line above the Label, you can try to put a blank Label in the row above. E.g.:
import tkinter as tk
root=tk.Tk()
for i in range(6):
tk.Button(root,text='Button %d'%i).grid(row=i,column=1)
tk.Label(root,text='Label 0').grid(row=2,column=0,pady=20)
tk.Label(root,text='Label 1').grid(row=3,column=0)
tk.Label(root,text='').grid(row=6)
tk.Label(root,text='This is a Label with a blank row above').grid(row=7,columnspan=2)
root.mainloop()
You can refer to effbot for more information, which is the blog of tkinter's developer.

When using python Tkinter, how can I stop two entry box's in the same class showing the same input text?

I am having this issue with Python Tkinter. I am trying to make a user interface form screen which requires the user to enter values into entry box's displayed on screen. I have set it so the two Entry Box's are in the same class (that class being the interface screen). The problem is that while I type into one of the box's, the text which I type not only displays in the box in which I am typing into, but also in the other box.
Below is the code in question.
class GenericSkeleton: # The template for all the screens in the program
def __init__(self):
self.GenericGui = Tk()
self.GenericGui.title('Radial Arc Calculator')
self.GenericGui.geometry('360x540')
self.GenericGui.resizable(width = FALSE, height = FALSE)
Label(self.GenericGui,text = 'Radial Arc Calculator',font = ('Ariel',18)).place(x=65,y=35)
def destroy(self):
self.GenericGui.destroy()
class InputScreen(GenericSkeleton):
def __init__(self):
GenericSkeleton.__init__(self)
Button(self.GenericGui,text = 'CALCULATE',height = 1, width = 25, command = calculate, font = ('TkDefaultFont',14)).place(x=37,y=400)
Button(self.GenericGui,text = 'CLOSE',height = 1, width = 11, command = close, font = ('TkDefaultFont',14)).place(x=37, y=450)
Button(self.GenericGui,text = 'HELP', height = 1, width = 11, command = DisplayHelp, font = ('TkDefaultFont',14)).place(x=190, y=450)
Label(self.GenericGui,text = 'Enter Radius (mm):', font = ('TkDefaultFont',14)).place(x=37, y=180)
Label(self.GenericGui,text = 'Enter point distance (mm):', font = ('TkDefaultFont',14)).place(x=37, y=250)
Entry(self.GenericGui,textvariable = Radius, width = 10, font = ('TkDefaultFont',14)).place(x=210, y=180)
Entry(self.GenericGui,textvariable = Distance, width = 5, font = ('TkDefaultFont',14)).place(x=265, y=250)
run = InputScreen()
The entry box's are at the bottom of the code, I hope its enough/not too much to solve the problem.
The problem is that they both share the same textvariable (you use different variable names, but they have the same value which makes them the same in the eyes of tkinter). My advice is to not use the textvariable attribute. You don't need it.
However, if you remove the use of textvariable then you need to separate your widget creation from widget layout so that you can keep a reference to the widget. Then you can use the get method on the widget (rather than on the variable) to get the value:
self.entry1 = Entry(...)
self.entry2 = Entry(...)
self.entry1.place(...)
self.entry2.place(...)
Later, you can get the values like this:
radius = int(self.entry1.get())
distance = int(self.entry2.get())
If you do need the textvariable (usually only if you're using the trace feature of a tkinter variable), you must use a tkinter variable (StringVar, IntVar, etc) rather than a regular variable.

Categories

Resources