How to create a class to close GUI and exit Python - python-3.x

I have been trying to find a way to do this for a while to no avail. I would like to create a class to completely close my GUI in tkinter and I'm not having much luck. I've tried sys.exit and .destroy() a few different ways. I can manage to do what I want without using classes but I'm rather new to OOP. Here is my code:
import sys as system
import tkinter as tk
from tkinter import ttk
class headerFrame(ttk.Frame):
def __init__(self, container):
super().__init__(container)
#setup the grid layout manager
self.columnconfigure(0, weight=1)
self._create_widgets()
def _create_widgets(self):
#header bar
canvas = tk.Canvas(self, bg='#0066cc', highlightthickness=0, height=45, width=600)
canvas.grid(column=0, row=0, sticky=tk.W)
label = ttk.Label(self, text='Production Assistant', background='#0066cc', foreground='White', font=('calibri', 18, 'bold'))
label.grid(row=0, column=0)
class loginFrame(ttk.Frame):
def __init__(self, container):
super().__init__(container)
#setup the grid layout manager
self.columnconfigure(0, weight =1)
self.columnconfigure(0, weight=3)
self._create_widgets()
def _create_widgets(self):
#username
ttk.Label(self, text='Username: ', justify='right').grid(row=0, column=0, sticky=tk.E)
username = ttk.Entry(self, width=33)
username.focus()
username.grid(row=0, column=1, sticky=tk.W)
#password
ttk.Label(self, text='Password: ', justify='right').grid(row=1, column=0, sticky=tk.E)
password = ttk.Entry(self, width=33, show='*')
password.grid(row=1, column=1, sticky=tk.W)
#add padding
for widget in self.winfo_children():
widget.grid(padx=0, pady=5)
class loginButtonFrame(ttk.Frame):
def __init__(self, container):
super().__init__(container)
#setup the grid layout manager
self.columnconfigure(0, minsize=62)
self._create_widgets()
def _create_widgets(self):
#buttons
ttk.Button(self, text='Login', width=15).grid(row=0, column=1)
ttk.Button(self, text='Forgot Login', width=15).grid(row=0, column=2)
ttk.Button(self, text='Request Access', width=15).grid(row=1, column=1)
ttk.Button(self, text='Exit', width=15, command=exitButton).grid(row=1, column=2)
#add padding to buttons
for widget in self.winfo_children():
widget.grid(padx=3, pady=3)
class exitButton():
def exit():
#code to close gui and program
#create the main application
class mainLogin(tk.Tk):
def __init__(self):
super().__init__()
self.title('Login')
self.geometry('325x175')
self.resizable(0, 0)
self.configure(background='#444444')
#windows only (remove the minimize/maximize buttons)
self.attributes('-toolwindow', True)
#TCL to center the screen
self.eval('tk::PlaceWindow . center')
#layout on the root window
self.columnconfigure(0, weight=1)
self._create_Styles()
self._create_widgets()
def _create_Styles(self):
#create styles
s = ttk.Style()
s.configure('TFrame', background='#444444')
s.configure('TLabel', background='#444444', foreground='white')
s.configure('TButton', background='#878683', foreground='black')
def _create_widgets(self):
#create the header frame
_header_frame = headerFrame(self)
_header_frame.grid(column=0, row=0)
#create the login frame
_login_frame = loginFrame(self)
_login_frame.grid(column=0, row=1, sticky=tk.N)
#create the button frame
_login_button_frame = loginButtonFrame(self)
_login_button_frame.grid(column=0, row=2)
if __name__ == '__main__':
app = mainLogin()
app.mainloop()
class exitButton() is what I would like to call from multiple different pages in the application to close everything.
Any help is appreciated, I'm trying to learn as I build so if you have any suggested reading based around Python that would help with this I would appreciate it!

Related

Trouble Binding a Button in a Class - Python (Tkinter)

Just started learning Tkinter and was hoping someone could help me. I've been trying to bind a keyboard character (Enter button) to a tk button following this example and not getting anywhere.
Say I take the button (Enter) and try bind it nothing happens:
Enter.bind('<Return>', lambda:self.retrieve_Input(t))
If I bind to self instead using Lambda nothing happens also. I can get it to trigger if I remove the lambda but that's not the desired outcome
self.bind('<Return>', lambda:self.retrieve_Input(t))
My Code:
import sys
import tkinter as tk
from tkinter import ttk
class windows(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.wm_title("Test Application")
self.lift() #Bringing the GUI to the front of the screen
main_frame = tk.Frame(self, height=400, width=600) #Creating a main Frame for all pages
main_frame.pack(side="top", fill="both", expand=True)
main_frame.grid_rowconfigure(0, weight=1) #Configuring the location of the main frame using grid
main_frame.grid_columnconfigure(0, weight=1)
# We will now create a dictionary of frames
self.frames = {}
for F in (MainPage, CompletionScreen): #Add the page components to the dictionary.
page = F(main_frame, self)
self.frames[F] = page #The windows class acts as the root window for the frames.
page.grid(row=0, column=0, sticky="nsew")
self.show_page(MainPage) #Method to switch Pages
def show_page(self, cont):
frame = self.frames[cont]
frame.tkraise()
##########################################################################
class MainPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
#switch_window_button = tk.Button(self, text="Go to the Side Page", command=lambda: controller.show_page(SidePage))
#switch_window_button.pack(side="bottom", fill=tk.X)
tk.Label(self, text="Project Python Search Engine", bg='white').pack()
tk.Label(self, text="", bg='white').pack()
tk.Label(self, text="Song", bg='white').pack()
tk.Label(self, text="", bg='white').pack()
t = tk.Entry(self, bg='white', width = 50)
t.pack()
tk.Label(self, text="", bg='white').pack()
Enter = tk.Button(self, text='Search', command= lambda:self.retrieve_Input(t))
Enter.pack()
tk.Button(self, text="Latest Popular Songs", command=lambda:self.Popular_Songs(t)).pack() #Line 210 onwards
Enter.bind('<Return>', lambda:self.retrieve_Input(t))
def retrieve_Input(self, t):
print ("work")
print (t)
class CompletionScreen(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Completion Screen, we did it!")
label.pack(padx=10, pady=10)
switch_window_button = ttk.Button(
self, text="Return to menu", command=lambda: controller.show_page(MainPage)
)
switch_window_button.pack(side="bottom", fill=tk.X)
if __name__ == "__main__":
App = windows()
App.mainloop()
I'm not really sure what I'm missing
Answer: The button probably doesn't have the keyboard focus. When I run your code and then use the keyboard to move the focus to the button, your binding works. You probably want to bind to the entry widget rather than the button since that's what will have the keyboard focus. – Thanks Bryan Oakley

How to show two Frames side by side in Tkinter

This is what I got
This is what I want to get
I've tried it like 2 hours with reading the documentation and every tutorial I could possibly find but after all I'm not able to display two ttk.Frames side by side with a border around them.
What have I done wrong?
This is my code so far (and yeah, I know, I've never used self.path but I just like to have it available in need):
import os
import tkinter as tk
import tkinter.ttk as ttk
from ttkthemes import ThemedTk
class GUI(ThemedTk):
def __init__(self):
super().__init__()
self.set_theme("breeze")
self.path = os.path.dirname(__file__)
self.title("Kniffel All In One")
self.defineVariables()
# Root Window
self.rowconfigure(0, weight=1)
self.columnconfigure(0, weight=5)
self.columnconfigure(1, weight=1)
self.LeftFrame = ttk.Frame(self, border=True, borderwidth=1)
self.LeftFrame.grid(row=0, column=0, padx=5, pady=5, sticky="NSWE")
self.RightFrame= ttk.Frame(self, border=True, borderwidth=1)
self.RightFrame.grid(row=0, column=1, padx=5, pady=5, sticky="NSWE")
# LeftFrame
self.LeftEntry = ttk.Entry(self.LeftFrame, textvariable=self.text1).grid(row=0, column=0, ipadx=5, ipady=5, sticky="WE")
self.LeftLabel = ttk.Label(self.LeftFrame, text="Little test left side").grid(row=1, column=0, ipadx=5, ipady=5, sticky="WE")
# RightFrame
self.RightEntry = ttk.Entry(self.RightFrame, textvariable=self.text2).grid(row=0, column=0, ipadx=5, ipady=5, sticky="WE")
self.RightLabel = ttk.Label(self.RightFrame, text="Litte test right side").grid(row=1, column=0, ipadx=5, ipady=5, sticky="WE")
self.mainloop()
def defineVariables(self):
self.text1 = tk.StringVar()
self.text2 = tk.StringVar()
if __name__ == '__main__':
GUI()
Note that I didn't use .pack() and .grid() together and it doesn't display a border even though I thought border=True would show one.
Please be so kind to just answer my question since I haven't ask for aesthetics, readability or programming style improvements. Also this won't be a professional application.
Thanks for understanding this.

Ttk Frame, Background color

I'm using ttk, for my GUI. I know that it is also a very simple question ... I am trying to change the background color of the main window.
I tried to change the theme, because I am working on a Mac, (and Python 3.5) to avoid the problem with the theme 'aqua', which is the default.I've been reading about several solutions like these questions which are about the same problem... These are the numbers of the questions:
54476511,
38712352,
47327266,
23750141.
But, I haven't Solve the problem, yet.
Here it's my code.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from tkinter.scrolledtext import *
from tkinter import Tk, BOTH, W, N, E, S, messagebox, END
from tkinter.ttk import Button, Label, Style, Frame
class Example(Frame):
def __init__(self,master):
super().__init__()
self.initUI()
def initUI(self):
self.master.title("Example")
Style().theme_use("classic")
self.pack(fill=BOTH, expand=1)
self.columnconfigure(1, weight=1)
self.columnconfigure(3, pad=7)
self.rowconfigure(3, weight=1)
self.rowconfigure(5, pad=7)
self.txt_Pad = ScrolledText(self)
self.txt_Pad.grid(row=1, column=0, columnspan=2, rowspan=4, padx=5, sticky=E+W+S+N)
self.txt_Pad.insert(END,'Type your info here')
btn_save = Button(self, text="Save", command=self.save_command)
btn_save.grid(row=1, column=3)
btn_close = Button(self, text="Close", command=self.onClose)
btn_close.grid(row=2, column=3, pady=4)
btn_help = Button(self, text="Help", command=self.about_command)
btn_help.grid(row=5, column=0, padx=5)
def onClose(self):
self.master.destroy()
def about_command(self):
msb = messagebox.showinfo("About", "\"Insert a useful tip Here\"")
def save_command(self):
print('Your info it\'s save now')
def open_command(self):
print('Choose your File')
def main():
root = Tk()
root.geometry("350x300+300+300")
root.configure(bg='#0059b3')
app = Example(root)
root.mainloop()
if __name__ == '__main__':
main()
Any Suggestions would be appreciated.
Create a style then apply it.
from tkinter.scrolledtext import *
from tkinter import Tk, BOTH, W, N, E, S, messagebox, END
from tkinter.ttk import Button, Label, Style, Frame
class Example(Frame):
def __init__(self, master):
super().__init__()
self.initUI()
def initUI(self):
self.master.title("Example")
# create a new style
self.style = Style()
# configure it to the background you want
self.style.configure('My.TFrame', background='#0059b3')
#Style().theme_use("classic")
# apply it
self.config(style='My.TFrame')
self.pack(fill=BOTH, expand=1)
self.columnconfigure(1, weight=1)
self.columnconfigure(3, pad=7)
self.rowconfigure(3, weight=1)
self.rowconfigure(5, pad=7)
self.txt_Pad = ScrolledText(self)
self.txt_Pad.grid(row=1, column=0, columnspan=2, rowspan=4, padx=5, sticky=E+W+S+N)
self.txt_Pad.insert(END,'Type your info here')
btn_save = Button(self, text="Save", command=self.save_command)
btn_save.grid(row=1, column=3)
btn_close = Button(self, text="Close", command=self.onClose)
btn_close.grid(row=2, column=3, pady=4)
btn_help = Button(self, text="Help", command=self.about_command)
btn_help.grid(row=5, column=0, padx=5)
def onClose(self):
self.master.destroy()
def about_command(self):
msb = messagebox.showinfo("About", "\"Insert a useful tip Here\"")
def save_command(self):
print('Your info it\'s save now')
def open_command(self):
print('Choose your File')
def main():
root = Tk()
root.geometry("350x300+300+300")
root.configure(background='#0059b3')
app = Example(root)
root.mainloop()
if __name__ == '__main__':
main()
I left comments at the parts I changed.

Resize columns in Frame with Scrollbar

Based on the example from here: Scrollbar in Tkinter grid
I made a simplified version which is more or less what i need except i would like the columns to always fill the width of the frame as the window is beeing resized.
Without the scrollbar it was super easy, i just added grid_columnconfigure and that worked out of the box, but when I added the scrollbar i couldn't figure out how to get the columns to resize again.
Here is the example:
import tkinter as tk
row = 1
class ProgramWindow(tk.Frame):
def __init__(self):
self.canvas = tk.Canvas(root, borderwidth=0, background="#ffffff")
tk.Frame.__init__(self, self.canvas)
self.grid(column=0, row=0, sticky='ESW')
self.grid_columnconfigure(0, weight=1)
self.grid_columnconfigure(1, weight=1)
self.grid_columnconfigure(2, weight=1)
tk.Label(self, text="FirstCol", ).grid(row=0, column=0)
tk.Label(self, text="SecndCol", ).grid(row=0, column=1)
tk.Label(self, text="ThirdCol", ).grid(row=0, column=3)
self.vsb = tk.Scrollbar(root, orient="vertical", command=self.canvas.yview)
self.canvas.configure(yscrollcommand=self.vsb.set)
self.vsb.pack(side="right", fill="y")
self.canvas.pack(side="left", fill="both", expand=True)
self.canvas.create_window((4, 4), window=self)
self.bind("<Configure>", self.OnFrameConfigure)
def OnFrameConfigure(self, event):
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
def addrow(self, stuff, otherstuff):
global row
var = tk.StringVar(value=stuff)
entry = tk.Entry(self, textvariable=var)
entry.grid(row=row, column=0)
var = tk.StringVar(value=otherstuff)
entry = tk.Entry(self, textvariable=var)
entry.grid(row=row, column=1)
var = tk.StringVar(value="foobar")
entry = tk.Entry(self, textvariable=var)
entry.grid(row=row, column=3)
row += 1
def SomeProg():
for i in range(20):
stuff = "Stuff is " + str(i)
otherstuff = i * 4
win.addrow(stuff, otherstuff)
root = tk.Tk()
root.title("Stuff")
win = ProgramWindow()
SomeProg()
root.mainloop()
I've adapted Bryan Oakley's answer to Adding a scrollbar to a group of widgets in Tkinter so that the frame contained in the canvas fit the width of the canvas.
Canvas window objects have a width option. So, each time the canvas is resized, I pass the new canvas width to this width option using
self.canvas.itemconfigure(<frame tag>, width=self.canvas.winfo_width())
Here is the full code
import tkinter as tk
class Example(tk.Frame):
def __init__(self, root):
tk.Frame.__init__(self, root)
self.canvas = tk.Canvas(root, borderwidth=0, background="#ffffff")
self.frame = tk.Frame(self.canvas, background="#ffffff")
self.frame.columnconfigure(0, weight=1)
self.frame.columnconfigure(1, weight=1)
self.frame.columnconfigure(2, weight=1)
self.vsb = tk.Scrollbar(root, orient="vertical", command=self.canvas.yview)
self.canvas.configure(yscrollcommand=self.vsb.set)
self.vsb.pack(side="right", fill="y")
self.canvas.pack(side="left", fill="both", expand=True)
self.canvas.create_window((4,4), window=self.frame, anchor="nw",
tags="self.frame")
self.canvas.bind("<Configure>", self.onCanvasConfigure)
self.populate()
def populate(self):
'''Put in some fake data'''
for i in range(100):
tk.Entry(self.frame).grid(row=i, column=0, sticky='ew')
tk.Entry(self.frame).grid(row=i, column=1, sticky='ew')
tk.Entry(self.frame).grid(row=i, column=2, sticky='ew')
def onCanvasConfigure(self, event):
self.canvas.itemconfigure("self.frame", width=self.canvas.winfo_width())
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
if __name__ == "__main__":
root=tk.Tk()
Example(root).pack(side="top", fill="both", expand=True)
root.mainloop()

How to open and close another window with scrollbar in tkinter for python 3.5.?

I want to build a Tkinter app in python 3.5. with a StartPage and a another window PageTwo that includes a table with a scolldownbar. I have tried to apply a framework from an online tutorial and the listbox example from another website.
My problem is: when I run the program both pages are loaded directly. How can I manage to let PageTwo only open on click on Button in StartPage, and then apply another button in PageTwo that closed PageTwo again and redirects to StartPage?
Second question: Alternatively to the listbox example I would like to use canvas with scrollbar on PageTwo. But how and where do I have to introduce the canvas? I get totally messed up with all the inheritances throughout the different classes.
If you would suggest a complete different setup, this would also be fine.
Many thanks for your help.
import tkinter as tk
class GUI(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, PageTwo):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
frame = StartPage(container, self)
self.frames[StartPage] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise() # zeigt Frame oben an
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Your choice?")
label.pack(pady=10,padx=10)
button1 = tk.Button(self, text="Open PageTwo",
width = 25, command=lambda: controller.show_frame(PageTwo))
button1.pack(pady=10, padx=10)
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
master = tk.Tk()
scrollbar = tk.Scrollbar(master)
scrollbar.pack(side=tk.RIGHT, fill="y")
listbox = tk.Listbox(master, yscrollcommand=scrollbar.set)
for i in range(1000):
listbox.insert(tk.END, str(i))
listbox.pack(side=tk.LEFT, fill="both")
scrollbar.config(command=listbox.yview)
if __name__ == '__main__':
app = GUI()
app.mainloop()
To fix the issues:
initialize PageTwo only when the button is clicked
use Toplevel for popup window
use root as the StartPage
Below is a demo based on your posted code:
import tkinter as tk
from tkinter import ttk
class GUI(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
label = tk.Label(self, text="Your choice?")
label.pack(pady=10,padx=10)
button1 = ttk.Button(self, text="Open PageTwo", width=25, command=lambda: self.show_frame(PageTwo))
button1.pack(pady=10, padx=10)
button2 = ttk.Button(self, text="Open PageCanvas", width=25, command=lambda: self.show_frame(PageCanvas))
button2.pack(pady=10, padx=10)
def show_frame(self, page):
win = page(self)
# make window modal
win.grab_set()
self.wait_window(win)
class PageTwo(tk.Toplevel):
def __init__(self, parent):
tk.Toplevel.__init__(self, parent)
self.title('Two')
scrollbar = tk.Scrollbar(self)
scrollbar.pack(side=tk.RIGHT, fill="y")
listbox = tk.Listbox(self, yscrollcommand=scrollbar.set)
for i in range(1000):
listbox.insert(tk.END, str(i))
listbox.pack(side=tk.LEFT, fill="both")
scrollbar.config(command=listbox.yview)
class PageCanvas(tk.Toplevel):
def __init__(self, parent):
tk.Toplevel.__init__(self, parent)
self.title('Canvas')
self.geometry('400x600')
canvas = tk.Canvas(self, bg='white', scrollregion=(0, 0, 400, 20000))
canvas.pack(fill='both', expand=True)
vbar = tk.Scrollbar(canvas, orient='vertical')
vbar.pack(side='right', fill='y')
vbar.config(command=canvas.yview)
canvas.config(yscrollcommand=vbar.set)
for i in range(1000):
canvas.create_text(5, i*15, anchor='nw', text=str(i))
if __name__ == '__main__':
app = GUI()
app.mainloop()

Resources