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()
Related
I'd like to position one of my python tkinter Frames (ButtonWindow) within my other Frame (MainWindow), so that when I run the app the widgets in ButtonWindow are present in MainWindow along with the MainWindow widget.
In the code below the Buttons from ButtonWindow are present along with the MainWindow Label, but the ButtonWindow Label is missing.
I looked at the answers in Frame inside another frame In python Tkinter and tried to set the background to purple to understand where the borders of ButtonWindow actually are, but I can't see any purple?
Thanks for any help!
import tkinter as tk
class ButtonWindow(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
self.bd = 5
self.bg = "purple"
self.label = tk.Label(text="Button window", font=12)
for i in range(3):
self.button = ttk.Button(text="button", command= lambda: button_fun())
self.button.pack(side = tk.LEFT)
def button_fun(self):
pass
class MainWindow(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
self.label = tk.Label(text="Main Window", font=12)
self.label.pack(pady=10,padx=10)
self.button_window = ButtonWindow()
self.button_window.pack()
app = MainWindow()
app.mainloop()
It is better to specify the parent of widgets when creating them, otherwise they will be children of root window.
Also you have never called any layout function on the "Button window" label so it is not visible.
self.bd = 5 and self.bg = "purple" will not change the border width and the background color. Use self.config(bd=5, bg="purple") instead.
import tkinter as tk
from tkinter import ttk
class ButtonWindow(tk.Frame):
def __init__(self, master=None, *args, **kwargs):
super().__init__(master, *args, **kwargs)
self.config(bd=5, bg="purple") # replace self.bd = 5 and self.bg = "purple"
self.label = tk.Label(self, text="Button window", font=12, fg='white', bg='purple') # specify parent
self.label.pack() # pack the label, otherwise it is not visible
for i in range(3):
self.button = ttk.Button(self, text="button", command=self.button_fun) # specify parent
self.button.pack(side=tk.LEFT)
def button_fun(self):
pass
class MainWindow(tk.Frame):
def __init__(self, master=None, *args, **kwargs):
super().__init__(master, *args, **kwargs)
self.label = tk.Label(self, text="Main Window", font=12) # specify parent
self.label.pack(pady=10, padx=10)
self.button_window = ButtonWindow(self) # specify parent
self.button_window.pack()
root = tk.Tk() # create root window explicitly
MainWindow(root).pack()
root.mainloop()
Also I have changed command=lambda: button_fun() to command=self.button_fun. The former one will raise exception when any of the buttons is clicked.
If I understood correctly what you want is create a Main Frame with another frame inside it which include 3 buttons.
In this case, I changed a little bit your code to do that. One of the changes was replace the tk.Label for the tk.LabelFrame (This corrects the Label that was missing in the ButtonWindow as you stated).
The second change that I suggest is to pass the MainFrame to the ButtonWindow as a parent frame. To do this I created the myCoreFrame inside the MainWindowclass. Also, for all widgets I've set a parent frame.
import tkinter as tk
class ButtonWindow():
def __init__(self, Frame, *args, **kwargs):
self.label = tk.LabelFrame(Frame, text="Button window", font=12, bg = "purple")
self.label.pack()
for i in range(3):
self.button = tk.Button(self.label, text="button", command= lambda: button_fun())
self.button.pack(side = tk.LEFT)
def button_fun(self):
pass
class MainWindow():
def __init__(self, window, *args, **kwargs):
myCoreFrame = tk.Frame(window)
myCoreFrame.pack()
self.label = tk.LabelFrame(myCoreFrame, text="Main Window", font=12, bg = "red")
self.label.pack(pady=10,padx=10)
self.button_window = ButtonWindow(self.label)
root = tk.Tk()
app = MainWindow(root)
root.mainloop()
The problem is that you aren't putting the labels and buttons inside frames. You need to explicitly set the frame as the parent of the button and label. If you don't, the widgets become children of the root window.
class ButtonWindow(tk.Frame):
def __init__(self, *args, **kwargs):
...
self.label = tk.Label(self, text="Button window", font=12)
# ^^^^^^
for i in range(3):
self.button = ttk.Button(self, text="button", command= lambda: button_fun())
# ^^^^^^
...
class MainWindow(tk.Frame):
def __init__(self, *args, **kwargs):
...
self.label = tk.Label(self, text="Main Window", font=12)
# ^^^^^
self.label.pack(pady=10,padx=10)
self.button_window = ButtonWindow(self)
# ^^^^
...
I have a Tkinter app in which I would like to include some buttons in a frame, and then place this frame in the main window.
However running the code returns just an empty window. So I guess I miss completely how to build a Tkinter app with modular classes.. The atomic code is:
import Tkinter as tk
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.navbar = NavBar(self)
self.navbar.grid(row=0, column=0)
class NavBar(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
self.fetchDataBtn = tk.Button(self, text='Fetch data')
self.filterDataBtn = tk.Button(self, text='Filter data')
self.fetchDataBtn.pack(padx=5, pady=10, side=tk.LEFT)
self.filterDataBtn.pack(padx=5, pady=20, side=tk.LEFT)
def main():
root = tk.Tk()
app = MainApplication(root)
root.mainloop()
if __name__ == '__main__':
main()
I thus wonder what I miss. I searched but cannot find duplicates ..(if they are some, you can point out and I will close the topic).
NB: I am using Python 2.7.10
The problem is that you don't pack (or grid or place) your MainApplication instance.
Since your MainApplication extends the tk.Frame class, its instances are widgets, and thus need to be packed into their master.
def main():
root = tk.Tk()
app = MainApplication(root) <--- here: where does it go in the root?
root.mainloop()
Pack it and it will work:
app.pack()
You must put the navbar in the parent frame, using pack, or grid:
import Tkinter as tk
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.navbar = NavBar(self)
self.navbar.grid(row=0, column=0)
self.pack() # <-- here ---------
class NavBar(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
self.fetchDataBtn = tk.Button(self, text='Fetch data')
self.filterDataBtn = tk.Button(self, text='Filter data')
self.fetchDataBtn.pack(padx=5, pady=10, side=tk.LEFT)
self.filterDataBtn.pack(padx=5, pady=20, side=tk.LEFT)
def main():
root = tk.Tk()
app = MainApplication(root)
# app.pack() # <-- or here for a better control of the placement of several instances
root.mainloop()
if __name__ == '__main__':
main()
Credits to #RightLeg for pointing out an initial mistake.
I would like to align text widgets horizontally and be able to scroll left and right the frame where are placed these widgets. The code below is almost what I want except the fact that my scrollbar doesn't work.
I found some example where it is said not to use pack or grid in Canvas. However, if I use place layout, my widgets simply disapear.
from tkinter import *
class MainView(Frame):
def __init__(self, *args, **kwargs):
Frame.__init__(self, *args, **kwargs)
self.canvas = Canvas(self)
self.sensorsStatsFrame = Frame(self.canvas)
myscrollbar = Scrollbar(self,orient=HORIZONTAL,command=self.canvas.xview)
myscrollbar.pack(side=BOTTOM,fill=X)
self.canvas.configure(xscrollcommand=myscrollbar.set)
self.canvas.pack(side=TOP, fill=BOTH)
test0 = Text(self.sensorsStatsFrame, bg="red", state=DISABLED, width=150)
test1 = Text(self.sensorsStatsFrame, bg="green")
test0.pack(side=LEFT)
test1.pack(side=LEFT)
self.canvas.create_window((0,0),window=self.sensorsStatsFrame,anchor='nw')
self.canvas.config(scrollregion=self.canvas.bbox("all"))
if __name__ == "__main__":
root = Tk()
main = MainView(root)
main.pack(fill="both", expand=1)
root.wm_geometry("1100x500")
root.wm_title("MongoDB Timed Sample Generator")
root.mainloop()
I would like to align text widgets horizontally and be able to scroll left and right the frame where are placed these widgets.
If i didn't misunderstand you, you should add an event function to your codes.
from tkinter import *
class MainView(Frame):
def __init__(self, *args, **kwargs):
Frame.__init__(self, *args, **kwargs)
self.canvas = Canvas(self)
self.sensorsStatsFrame = Frame(self.canvas)
myscrollbar = Scrollbar(self,orient=HORIZONTAL,command=self.canvas.xview)
myscrollbar.pack(side=BOTTOM,fill=X)
self.canvas.configure(xscrollcommand=myscrollbar.set)
self.canvas.pack(side=TOP, fill=BOTH)
test0 = Text(self.sensorsStatsFrame, bg="red", state=DISABLED, width=150)
test1 = Text(self.sensorsStatsFrame, bg="green")
test0.pack(side=LEFT)
test1.pack(side=LEFT)
self.canvas.create_window((0,0),window=self.sensorsStatsFrame,anchor='nw')
# Call the function like the below.
self.sensorsStatsFrame.bind("<Configure>", self.onFrameConfigure)
# Add below function to your codes.
def onFrameConfigure(self, event):
self.canvas.config(scrollregion=self.canvas.bbox("all"))
if __name__ == "__main__":
root = Tk()
main = MainView(root)
main.pack(fill="both", expand=1)
root.wm_geometry("1100x500")
root.wm_title("MongoDB Timed Sample Generator")
root.mainloop()
How would I create a new window when the user clicks a button (still needs creating)? I have took some code out to make this shorter. I need a button creating and when they hit that button, a new window opens. I haven't created the button because the button has to be linked to the new window. Please help
My imports...
class App:
def __init__(self, master):
self.master = master
# call start to initialize to create the UI elemets
self.start()
def start(self):
self.master.title("E-mail Extranalyser")
self.now = datetime.datetime.now()
tkinter.Label(
self.master, text=label01).grid(row=0, column=0, sticky=tkinter.W)
# CREATE A TEXTBOX
self.filelocation = tkinter.Entry(self.master)
self.filelocation["width"] = 60
self.filelocation.focus_set()
self.filelocation.grid(row=0, column=1)
# CREATE A BUTTON WITH "ASK TO OPEN A FILE"
# see: def browse_file(self)
self.open_file = tkinter.Button(
self.master, text="Browse...", command=self.browse_file)
# put it beside the filelocation textbox
self.open_file.grid(row=0, column=2)
# now for a button
self.submit = tkinter.Button(
self.master, text="Execute!", command=self.start_processing,
fg="red")
self.submit.grid(row=13, column=1, sticky=tkinter.W)
def start_processing(self):
#code here
def browse_file(self):
# put the result in self.filename
self.filename = filedialog.askopenfilename(title="Open a file...")
# this will set the text of the self.filelocation
self.filelocation.insert(0, self.filename)
root = tkinter.Tk()
app = App(root)
root.mainloop()
Use a Toplevel to open a new one. Modify your code as shown below.
self.NewWindow = tkinter.Button(self.master,
text="New Window",
command=self.CreateNewWindow)
def CreateNewWindow(self):
self.top = tkinter.Toplevel()
self.top.title("title")
Take a look at https://www.youtube.com/watch?v=jBUpjijYtCk. Working through this tutorial would probably help you but this specific video shows how to work with multiple pages.
Something like this:
from tkinter import *
class Sample(Tk):
def __init__(self,*args, **kwargs):
Tk.__init__(self, *args, **kwargs)
container = Frame(self)
container.pack(side="top", fill="both", expand = True)
self.frames = {}
for F in (MainPage, OtherPage):
frame=F(container, self)
self.frames[F]=frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(MainPage)
def show_frame(self, page):
frame = self.frames[page]
frame.tkraise()
class MainPage(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
Label(self, text="Start Page").pack()
Button(self, text="other page?", command=lambda:controller.show_frame(OtherPage)).pack()
class OtherPage(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
Label(self, text="Next Page").pack()
Button(self, text="back", command=lambda:controller.show_frame(MainPage)).pack()
app = Sample()
app.mainloop()
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.