This is the front end I developed for my application using Tkinter:
from Tkinter import *
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("Simple")
self.pack(fill=BOTH, expand=1)
frame = Frame(self, relief="flat", borderwidth=1)
label=Label(frame,text="Scope:")
label.pack(side="left", fill=None, expand=False)
var = StringVar()
var.set("today")
list = OptionMenu(frame, var, "today","yesterday","this week","last week","this month","last month")
list.pack(side="left", fill=None, expand=False)
fetchButton = Button(frame, text="Fetch",command=self.handle(var))
fetchButton.pack(side="left", fill=None, expand=False)
frame.grid(row=1,column=1,pady=4,padx=5,sticky=W)
area = Text(self,height=15,width=60)
area.grid(row=2,column=1,rowspan=1,pady=4,padx=5)
scroll = Scrollbar(self)
scroll.pack(side=RIGHT, fill=Y)
area.config(yscrollcommand=scroll.set)
scroll.config(command=area.yview)
scroll.grid(row=2, column=2, sticky='nsew')
quitButton = Button(self, text="Cancel",command=self.quit)
quitButton.grid(pady=4,padx=5,sticky=W,row=3, column=1)
root = Tk()
app = Example(root)
root.mainloop()
Where exactly do I have to put the handle() method so it can write repeatedly to the text widget? When I put handle() within the Example class and use self.area.insert(), it shows an error saying
Example instance has no attribute 'area'
Please help out.
You need to pass the function object to the Button instance, not a function call. i.e.
fetchButton = Button(frame, text="Fetch",command=self.handle)
To make the handle work in the context of the rest of the code:
from Tkinter import *
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.parent.title("Simple")
self.pack(fill=BOTH, expand=1)
self.init_ui()
def init_ui(self):
self.frame = Frame(self, relief="flat", borderwidth=1)
self.frame.grid(row=1,column=1,pady=4,padx=5,sticky=W)
self.label=Label(self.frame,text="Scope:")
self.label.pack(side="left", fill=None, expand=False)
self.var = StringVar()
self.var.set("today")
self.list = OptionMenu(self.frame, self.var, "today","yesterday",
"this week","last week","this month",
"last month")
self.list.pack(side="left", fill=None, expand=False)
self.fetchButton = Button(self.frame, text="Fetch",command=self.handle)
self.fetchButton.pack(side="left", fill=None, expand=False)
self.area = Text(self,height=15,width=60)
self.area.grid(row=2,column=1,rowspan=1,pady=4,padx=5)
self.scroll = Scrollbar(self)
self.scroll.pack(side=RIGHT, fill=Y)
self.area.config(yscrollcommand=self.scroll.set)
self.scroll.config(command=self.area.yview)
self.scroll.grid(row=2, column=2, sticky='nsew')
self.quitButton = Button(self, text="Cancel",command=self.quit)
self.quitButton.grid(pady=4,padx=5,sticky=W,row=3, column=1)
def handle(self):
self.area.delete(1.0, END)
self.area.insert(CURRENT,self.var.get())
if __name__ == "__main__":
root = Tk()
app = Example(root)
root.mainloop()
Declaring your widgets as attributes will save you a lot of pain an suffering as your application expands. Also keeping references to everything in Tk can stop some unwanted garbage collection, particularly with images in Label instances.
It is also worth noting that using grid and pack interchangeably is likely to lead to bugs later on.
Related
I have made my custom infobox class, InfoBox, that I am using in my application. The tk.messagebox.showinfo did not suit my needs to the poor shape. But InfoBox does not adjust its size to fit the widgets I place inside. How can I make it as small as possible without cutting the widgets?
The class receives a string, msg, and a PhotoImage object, image, which are placed in the InfoBox. I added a screenshot of one such InfoBox.
class InfoBox(tk.Toplevel):
def __init__(self, parent, msg, image):
tk.Toplevel.__init__(self, parent)
self.parent = parent
self.msg = msg
self.image = image
self.title = "Gassy"
self.font = font.Font(family="Optima", size=20)
frame_left = tk.Frame(self)
frame_right = tk.Frame(self)
frame_left.grid(row=0, column=0, sticky=tk.NSEW)
frame_right.grid(row=0, column=1, sticky=tk.NSEW)
tk.Label(frame_left, image=self.image).grid(row=0, column=0, sticky=tk.N)
textbox = tk.Text(frame_right, font=self.font)
textbox.grid(row=0, column=0)
textbox.insert(tk.END, self.msg)
textbox.config(state=tk.DISABLED)
tk.Button(frame_left, text="Den er grei!", font=self.font, command=self.destroy).grid(row=1, column=0)
As #kevin mentioned, it works as intended, the textwidget is mostly empty and occupies a large blank area, this is what makes you think that the geometry manager is not shrinking the window to the widgets.
this:
(I removed the images and fonts that were not provided, and unnecessary)
import tkinter as tk
class InfoBox(tk.Toplevel):
def __init__(self, parent, msg):
tk.Toplevel.__init__(self, parent)
self.parent = parent
self.msg = msg
self.title = "Gassy"
frame_left = tk.Frame(self)
frame_right = tk.Frame(self)
frame_left.grid(row=0, column=0, sticky=tk.NSEW)
frame_right.grid(row=0, column=1, sticky=tk.NSEW)
# textbox = tk.Text(frame_right)
# textbox.grid(row=0, column=0)
# textbox.insert(tk.END, self.msg)
# textbox.config(state=tk.DISABLED)
tk.Button(frame_left, text="Den er grei!", command=self.destroy).grid(row=1, column=0)
root = tk.Tk()
info = InfoBox(root, '123 ' * 1000)
root.mainloop()
produces that:
whereas that:
import tkinter as tk
class InfoBox(tk.Toplevel):
def __init__(self, parent, msg):
tk.Toplevel.__init__(self, parent)
self.parent = parent
self.msg = msg
self.title = "Gassy"
frame_left = tk.Frame(self)
frame_right = tk.Frame(self)
frame_left.grid(row=0, column=0, sticky=tk.NSEW)
frame_right.grid(row=0, column=1, sticky=tk.NSEW)
textbox = tk.Text(frame_right)
textbox.grid(row=0, column=0)
textbox.insert(tk.END, self.msg)
textbox.config(state=tk.DISABLED)
tk.Button(frame_left, text="Den er grei!", command=self.destroy).grid(row=1, column=0)
root = tk.Tk()
info = InfoBox(root, '123 ' * 1000)
root.mainloop()
produces this:
Clearly, the Toplevel subclass adjusts its size to the widgets it contains
The test widget is displayed at a certain size, regardless of its content. The Toplevel resizes around the widgets, NOT around whatever is inserted in the text widget; like with a text processor rudimentary window, the text processor does not shrink or expand as text is typed or edited. The same applies here.
The keyword args width and height allow to configure the size (as a number of characters, or lines) of a text widget
I'm working in Python 3.5 and TKinteer. Inside of a text widget, I have created a context menu that appears when the user right-clicks. However, when I try and create the commands I want (cut, copy, paste), the commands seem to have no effect.
The relevant code is as follows:
from tkinter import *
class Application:
def __init__(self,master):
self.master = master
self.initUI()
def initUI(self):
root.title("Simple Text Editor")
scrollBar = Scrollbar(root)
self.textPad = Text(root, width=100, height=100, wrap='word',
yscrollcommand=scrollBar.set,
borderwidth=0, highlightthickness=0)
scrollBar.config(command=self.textPad.yview)
scrollBar.pack(side='right', fill='y')
self.textPad.pack(side='left', fill='both', expand=True)
class PopupMenu:
def __init__(self, master, *args, **kwargs):
self.popup_menu = Menu(root, tearoff=0)
self.popup_menu.add_command(label="Cut",
command=lambda: app.textPad.event_generate('<Control-x>'))
self.popup_menu.add_command(label="Copy",
command=lambda: app.textPad.event_generate('<Control-c>'))
self.popup_menu.add_command(label="Paste",
command=lambda: app.textPad.event_generate('<Control-v>'))
app.textPad.bind("<Button-3>", self.popup)
self.popup_menu.bind("<FocusOut>",self.popupFocusOut)
def popup(self, event):
self.popup_menu.post(event.x_root, event.y_root)
self.popup_menu.focus_set()
def popupFocusOut(self, event=None):
self.popup_menu.unpost()
root = Tk()
app = Application(root)
popupMenu = PopupMenu(root)
root.mainloop()
You don't want to generate <Control-x>, etc. Instead, generate the virtual events <<Cut>>, <<Copy>> and <<Paste>>.
I want to create some simple tkinter python app (like StickyNotes on Windows), i have create the class mainApplication and i do not know how to by just simply triggering the button create another instance of this class which will be displayed pararell to other window (or even multiple windows). I know how to assigned function to pushButton, and other simple stuff but the problem is with this pararell window displaying. Thanks in advance for help.
class mainApplication(Frame):
_ids = count(0)
def __init__(self, parent):
""" """
self.id = next(self._ids)
Frame.__init__(self, parent)
self.parent = parent
self.parent.minsize(width=200,height=100)
self.parent.geometry(('%dx%d+%d+%d' % (200, 100, 1700, 0+self.id*100)))
self.initUI()
def initUI(self):
""" """
self.parent.title("a2l")
self.pack(fill=BOTH, expand=True)
style = Style()
style.configure("TFrame", background="#333")
frame1 = Frame(self, style="TFrame")
frame1.pack(fill=X)
self.lbl0 = Label(frame1, text="api", width=7, background="#333", foreground = "red")
self.lbl0.pack(side=TOP, padx=5, pady=5)
self.closeButton = Button(self, text="new", command = self.createNewInstance)
self.closeButton.pack(side=RIGHT, padx=5, pady=5)
#=======================================================================
# self.generateButton = Button(self, text="GENERATE", command = self.)
# self.generateButton.pack(side=RIGHT, padx=5, pady=5)
#=======================================================================
def createNewInstance(self):
y = mainApplication()
return y
if __name__ == "__main__":
root = Tk()
x = mainApplication(root).pack(side="top", expand=False)
Tk().mainloop()
You shouldn't create more than one Tk() window in one application with tkinter. Instead tkinter provides a widget called Toplevel which can be useful for this kind of thing.
It creates another window which can exist along side the Tk() window and alongside other Toplevel widgets.
You could use this to create a series of persistent windows with whatever text or widgets you wanted on them at any kind of trigger including a Button.
See my example below for a demonstration:
from tkinter import *
class App:
def __init__(self, root):
self.root = root
self.top = [] #list to contain the Toplevel widgets
self.entry = Entry(self.root)
self.button = Button(self.root, text="Create window", command=self.command)
self.entry.pack()
self.button.pack()
def command(self): #called when button is pressed
self.top.append(Toplevel(self.root)) #adds new Toplevel to the list
Label(self.top[len(self.top)-1], text=self.entry.get()).pack() #Adds label equal to the entry widget to the new toplevel
root = Tk()
App(root)
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()
this is my code. I can't put label2 to self.main, and I don't know, how to write a generic function code, that would close the child widgets, that can be specified in the arguments.
import tkinter
class mainwin:
def __init__(self):
self.root = tkinter.Tk()
self.main = tkinter.Canvas(self.root, width=200, height=400)
self.main.place(x=0, y=0, relwidth=1, relheight=1)
self.main.config(bg='green')
self.root.mainloop()
class addlabel:
def __init__(self):
self.label2 = tkinter.Label(mainwin.main, height=2, width=50, text='Hello Noob!!')
#can't put on the canvas 'main'
self.label2.place(x=0, y=50)
self.exit_button = tkinter.Button(self.label2, text='Exit')
self.exit.button.bind('<1>', quit_from_widget)
'''
class quit_from_widget:
def __init__(self):
# what code should be written here, to quit any child widget.
'''
mainwin()
addlabel()
You might be able to use:
mylist = parent.winfo_children();
Then use a for loop and destroy() to close them off
The main reason you can't put the label in is because you are calling mainloop() before addLabel. The program loops through the code and doesn't execute addlabel() until you close the mainwin() function.
secondly, you can't do mainw.main. the class has no reference to that function. instead try adding a parent function to your addlabel like so:
class addlabel:
def __init__(self, parent):
self.label2 = tkinter.Label(parent, height=2, width=50, text='Hello Noob!!')
self.label2.place(x=0, y=50)
self.exit_button = tkinter.Button(self.label2, text='Exit')
self.exit_button.bind('<1>', quit)
Then, when you call the function in mainw class (before the self.root.mainloop() line) you would write:
addlabel(self.main)
import tkinter
class mainwin:
def __init__(self):
self.root = tkinter.Tk()
self.main = tkinter.Canvas(self.root, width=200, height=400)
self.main.place(x=0, y=0, relwidth=1, relheight=1)
self.main.config(bg='green')
self.root.mainloop()
class CustomLabel(tkinter.Frame):
def __init__(self, parent):
tkinter.Frame.__init__(self, parent)
self.label = tkinter.Label(self, height=20, width=30, bg='Red', fg='white', text='Hello')
self.exit_button = tkinter.Button(self, command=self.destroy)
# pack these widgets into this frame. You can use grid or
# place, but pack is easiest for such a simple layout
self.exit_button.pack(side="right")
self.label.pack(side="left", fill="both", expand=True)
window = mainwin()
label = CustomLabel(window.main)
nothing happens. gives green background and the child widget is not visible. but when closing, he writes an error:
....
(widgetName, self._w) + extra + self._options(cnf))
_tkinter.TclError: can't invoke "frame" command: application has been destroyed
Process finished with exit code 1