tkinter class to class 'scroll' is not defined - python

I am trying to call multiple definitions and have each print their results to the ScrolledText.
If I define ScrolledText in the class MainPage, then the button OpenFile works fine , but the Menus commands do not with the 'scroll' is not defined error.
If I define ScrolledText in the class SampleApp, then the Menu OpenFile works fine , but the button commands do not with the 'scroll' is not defined error.
If I define both, then I get two scrolls that both work only in their own class.
How do I pass the scroll object between the 2 classes to use the same scroll in both class SampleApp and class MainPage?
#!/usr/bin/env python
# Python3.7.0
# Tkinter
import tkinter as tk # python 3
#from tkinter import ttk, font as tkfont, messagebox as mb, filedialog # python 3
from tkinter import ttk
import tkinter.scrolledtext as tkst
from tkinter.ttk import *
# Global scope
FilePath = "C:\Myfiles"
ScrollMsg = ""
# window definitions ----------------------------------
# Menu definitions ----------------------------------
def OpenFile(scroll): # Add a filedialog (file & directory chooser)
global FilePath, ScrollMsg
FilePath = "C:\Myfiles\file.txt"
print("filedialog path & file :\n", FilePath)
ScrollMsg = "filedialog path & file : " + FilePath
print ("SCROLL MESSAGE")
print ( ScrollMsg +"\n")
scroll.insert(tk.INSERT,ScrollMsg )
# ------------------------------------------------------
class SampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self._frame = None
self.replace_frame(MainPage)
# scrolledtext
scroll = tkst.ScrolledText(self,width=90,height=15)
scroll.grid(column=0,row=15, columnspan = 6, sticky='W')
scroll.insert(tk.INSERT," OK here" +"\n")
# Menu for all frames ----------------------------------
menubar = tk.Menu(self)
# File
filemenu = tk.Menu(menubar, tearoff=0)
filemenu.add_command(label='Open', command=lambda: OpenFile(scroll))
menubar.add_cascade(label='File', menu=filemenu)
self.config(menu=menubar)
def replace_frame(self, frame_class):
"""Destroys current frame and replaces it with a new one."""
new_frame = frame_class(self)
if self._frame is not None:
self._frame.destroy()
self._frame = new_frame
self._frame.grid()
# FRAME CLASSES --------------------------------------
class MainPage(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
lbl1 = tk.Label(self, text="File selection: ")
lbl1.grid(column=0, row=1)
lbl2 = tk.Label(self, text="File Path")
lbl2.grid(column=0, row=2)
_FP_ = Entry(self,width=30)
_FP_.insert(0,FilePath) #set the item
_FP_.grid(column=1, row=2)
btn = Button(self, text='OpenFile', command=lambda: OpenFile(scroll))
btn.grid(column=0, row=4)
# scrolledtext
'''
scroll = tkst.ScrolledText(self,width=90,height=15)
scroll.grid(column=0,row=15, columnspan = 6, sticky='W')
scroll.insert(tk.INSERT," OK here" +"\n")
'''
_FP_.focus() # Place cursor into File Name Entry
if __name__ == "__main__":
app = SampleApp()
app.mainloop()

I'm not sure I understand exactly what you're trying to accomplish, but think I can answer your question about having a ScrolledText that will work with both of the classes — SampleApp and MainPage — at the same time.
To do this I've added another class named SingleScroll which is what is known as a "singleton" class, because it restricts the number of instances of it that can be created to just a "single" one.
It's used by both SampleApp and MainPage just like a regular class, and the object returned from calling it is passed to the OpenFile() function as an argument for the Menu or Button commands in the respective classes.
import tkinter as tk # python 3
#from tkinter import ttk, font as tkfont, messagebox as mb, filedialog # python 3
from tkinter import ttk
import tkinter.scrolledtext as tkst
from tkinter.ttk import *
# Global scope
FilePath = r"C:\Myfiles"
ScrollMsg = ""
# window definitions ----------------------------------
# Menu definitions ----------------------------------
def OpenFile(scroll): # Add a filedialog (file & directory chooser)
global FilePath, ScrollMsg
FilePath = r"C:\Myfiles\file.txt"
print("filedialog path & file :\n", FilePath)
ScrollMsg = "filedialog path & file : " + FilePath
print("SCROLL MESSAGE")
print(ScrollMsg + "\n")
scroll.insert(tk.INSERT, ScrollMsg + "\n" )
# SHARED SCROLL CLASS (ADDED) --------------------------
class SingleScroll:
""" Singleton ScrolledText object. """
def __new__(cls, master):
if '_inst' not in vars(cls): # Create instance?
scroll = tkst.ScrolledText(master, width=90, height=15)
scroll.grid(column=0, row=15, columnspan=6, sticky='W')
scroll.insert(tk.INSERT, " OK here\n")
cls._inst = scroll
return cls._inst
# ------------------------------------------------------
class SampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self._frame = None
self.replace_frame(MainPage)
scroll = SingleScroll(self) # ScrolledText
# Menu for all frames ----------------------------------
menubar = tk.Menu(self)
# File
filemenu = tk.Menu(menubar, tearoff=0)
filemenu.add_command(label='Open', command=lambda: OpenFile(scroll))
menubar.add_cascade(label='File', menu=filemenu)
self.config(menu=menubar)
def replace_frame(self, frame_class):
"""Destroys current frame and replaces it with a new one."""
new_frame = frame_class(self)
if self._frame is not None:
self._frame.destroy()
self._frame = new_frame
self._frame.grid()
# FRAME CLASSES --------------------------------------
class MainPage(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
lbl1 = tk.Label(self, text="File selection: ")
lbl1.grid(column=0, row=1)
lbl2 = tk.Label(self, text="File Path")
lbl2.grid(column=0, row=2)
_FP_ = Entry(self,width=30)
_FP_.insert(0,FilePath) #set the item
_FP_.grid(column=1, row=2)
scroll = SingleScroll(master) # ScrolledText
btn = Button(self, text='OpenFile', command=lambda: OpenFile(scroll))
btn.grid(column=0, row=4)
_FP_.focus() # Place cursor into File Name Entry
if __name__ == "__main__":
app = SampleApp()
app.mainloop()

Related

How to destroy a window after opening a new one?

I am working on a game called 'Flag Quiz' using tkinter. I have a script called mainmenu where I can choose between an easy mode and a hard mode. If I click on one of the buttons the recent mainmenu window disappears and a new tkinter window opens.
Here is my mainmenu script:
from tkinter import *
import tkinter as tk
from hardmode import HardApp
from easymode import EasyApp
class TitleScreen(tk.Tk):
def __init__(self):
super().__init__()
self.title('Flag Quiz')
self.geometry('600x600')
self.resizable(0,0)
self.make_widgets()
def make_widgets(self):
self.background = PhotoImage(file = './background.png')
self.label = Label(self, image=self.background)
self.label.place(x=0, y=0, relwidth=1, relheight=1)
self.easy = Button(self, text="Easy Mode", height=2, width=6, font=('default', 20), command=self.play_easy)
self.hard = Button(self, text="Hard Mode", height=2, width=6, font=('default', 20), command=self.play_hard)
self.easy.place(relx=0.5, rely=0.45, anchor=CENTER)
self.hard.place(relx=0.5, rely=0.55, anchor=CENTER)
def play_easy(self):
self.withdraw()
self.app = EasyApp()
#self.app.start()
def play_hard(self):
self.withdraw()
self.app = HardApp()
#self.app.start()
def start(self):
self.mainloop()
TitleScreen().start()
And here is my easy mode script:
import tkinter as tk
from tkinter import *
import random
import os
import json
class EasyApp(tk.Toplevel):
def __init__(self):
super().__init__()
self.title('Flag Quiz')
self.geometry('')
self.resizable(0,0)
self.score = 0
self.create_widgets()
def create_widgets(self):
# variables
self.user_guess = StringVar(self)
self.text = StringVar(self)
self.text.set(" ")
# initial image
self.scoretext = Label(self, text="Score: ").pack(side='top', fill='x')
self.scorevalue = Label(self, text=self.score).pack(side='top', fill='x')
self.file = random.choice(os.listdir('pngs'))
self.randimg = PhotoImage(file='pngs/{}'.format(self.file))
self.randimg = self.randimg.subsample(2, 2)
self.panel = Label(self, image=self.randimg)
self.panel.pack()
self.country, self.ext = self.file.split('.')
self.countries = self.load_lookup()
self.countryname = [country for country in self.countries if country['alpha2'] == self.country]
self.s = []
for i in range(0,3):
country = random.choice(self.countries)
self.s.append(country['de'])
self.s.append(self.countryname[0]['de'])
random.shuffle(self.s)
self.btndict = {}
for i in range(4):
self.btndict[self.s[i]] = Button(self, text=self.s[i], height=2, width=35, font=('default', 20), command=lambda j=self.s[i]: self.check_input(j))
self.btndict[self.s[i]].pack()
def check_input(self, d):
if d != self.countryname[0]['de']:
print("Falsch")
else:
self.score += 5
for widget in self.winfo_children():
widget.destroy()
self.create_widgets()
def load_lookup(self):
with open('lookup.json') as file:
self.obj = file.read()
self.countryobj = json.loads(self.obj)
return self.countryobj
# def start(self):
# self.mainloop()
After clicking the close button (the default button on windows/osx to close a window) the window from my easy mode app disappears but PyCharm says that my program is still running.
I made some investigations and removed the self.withdraw() function in the function play_easy(self) in my mainmenu script. So now the mainmenu is still open after I click on the easy mode button. If I'm closing both windows now, the program fully ends.
Replacing self.withdraw() with self.destroy() is not working. The main menu is closed, but a new empty window opens instead.
Any suggestions on how to handle this problem so that my program fully ends if I click the close button within the easy/hard mode window?
You have two windows - main window created with Tk and subwindow created with Toplevel. When you use close button to close main window then it should also close all subwindows but when you close subwindow then it doesn't close main window (parent window) but only own subwindows - because usually it can be useful to display again main window to select other options and open again subwindow.
One of the methods is to destroy first window and use Tk to create new window.
But in this method you can't use some button in second window to go back to first window - and sometimes it can be problem. Even if you create again first window then it will not remeber previous values (if you have some Entry or other widgets to set values)
# from tkinter import * # PEP8: `import *` is not preferred`
import tkinter as tk
class EasyApp(tk.Tk): # use `Tk` instead of `Toplevel`
def __init__(self):
super().__init__()
self.scoretext = tk.Label(self, text="EasyApp")
self.scoretext.pack()
#def start(self):
# self.mainloop()
class TitleScreen(tk.Tk):
def __init__(self):
super().__init__()
self.button = tk.Button(self, text="Easy Mode", command=self.play_easy)
self.button.pack()
def play_easy(self):
self.destroy() # destroy current window
self.app = EasyApp()
def start(self):
self.mainloop()
TitleScreen().start()
Other method is to use self.wm_protocol("WM_DELETE_WINDOW", self.on_close) to execute function on_close when you use close button and in this function destroy main window (master).
This way you can still use Button to go back to main window which will remember previous content.
# from tkinter import * # PEP8: `import *` is not preferred`
import tkinter as tk
class EasyApp(tk.Toplevel): # still use `Toplevel`
def __init__(self, master): # send main window as master/parent
super().__init__(master) # it will also set `self.master = master`
self.scoretext = tk.Label(self, text="EasyApp")
self.scoretext.pack()
self.button = tk.Button(self, text="Go Back", command=self.go_back)
self.button.pack()
# run `on_close` when used `close button`
#self.protocol("WM_DELETE_WINDOW", self.on_close)
self.wm_protocol("WM_DELETE_WINDOW", self.on_close)
def go_back(self):
self.destroy() # destroy only current window
self.master.deiconify() # show again main window
def on_close(self):
self.destroy() # destroy current window
self.master.destroy() # destroy main window
class TitleScreen(tk.Tk):
def __init__(self):
super().__init__()
self.entry = tk.Entry(self)
self.entry.pack()
self.entry.insert('end', "You can change text")
self.button = tk.Button(self, text="Easy Mode", command=self.play_easy)
self.button.pack()
def play_easy(self):
self.withdraw()
self.app = EasyApp(self) # send main window as argument
def start(self):
self.mainloop()
TitleScreen().start()
Here's a fairly simple architecture for doing what you want. An application class is derived from Tk() which hides the default "root" window it normally displays and all the windows it does display are subclasses of a custom Toplevel subclass I've named BaseWin.
This class is just a Toplevel with its protocol for being delete (closed) set to call an a method named on_close(). This additional method simply destroys the current window before quits the application's mainloop() causing it to terminate.
The first window—an instance of the TitleScreen class—is displayed automatically when an instance of the application class is created. This window has two Buttons one labelled Easy Mode and the other Hard Mode. When one of them is clicked, an instance of the appropriate Toplevel subclass is created after the current window is removed by it call its destroy() method.
mainmenu.py
import tkinter as tk
from tkinter.constants import *
from tkinter import font as tkfont
from basewin import BaseWin
from easymode import EasyApp
from hardmode import HardApp
class SampleApp(tk.Tk):
def __init__(self):
super().__init__()
self.title('Flag Quiz')
self.geometry('600x600')
self.resizable(FALSE, FALSE)
self.title_font = tkfont.Font(family='Helvetica', size=18, weight="bold")
self.withdraw() # Hide default root Tk window.
startpage = TitleScreen(self.master)
self.mainloop()
class TitleScreen(BaseWin):
def __init__(self, master):
super().__init__(master)
self.make_widgets()
def make_widgets(self):
label = tk.Label(self, text="This is the Start Page", font=self.master.title_font)
label.pack(side="top", fill="x", pady=10)
self.easy = tk.Button(self, text="Easy Mode", font=('default', 20),
command=self.play_easy)
self.hard = tk.Button(self, text="Easy Mode", font=('default', 20),
command=self.play_hard)
self.easy.pack()
self.hard.pack()
def play_easy(self):
self.destroy()
self.app = EasyApp(self.master)
def play_hard(self):
self.destroy()
self.app = HardApp(self.master)
if __name__ == '__main__':
SampleApp()
basewin.py
import tkinter as tk
from tkinter.constants import *
class BaseWin(tk.Toplevel):
def __init__(self, master):
super().__init__(master)
self.protocol("WM_DELETE_WINDOW", self.on_close)
def on_close(self):
self.destroy() # Destroy current window
self.master.quit() # Quit app.
easymode.py
import tkinter as tk
from tkinter.constants import *
from basewin import BaseWin
class EasyApp(BaseWin):
def __init__(self, master):
super().__init__(master)
self.title('Flag Quiz')
self.resizable(FALSE, FALSE)
self.make_widgets()
def make_widgets(self):
label = tk.Label(self, text="This is the Easy App", font=self.master.title_font)
label.pack(side="top", fill="x", pady=10)
hardmode.py
import tkinter as tk
from tkinter.constants import *
from basewin import BaseWin
class HardApp(BaseWin):
def __init__(self, master):
super().__init__(master)
self.title('Flag Quiz')
self.resizable(FALSE, FALSE)
self.make_widgets()
def make_widgets(self):
label = tk.Label(self, text="This is the Hard App", font=self.master.title_font)
label.pack(side="top", fill="x", pady=10)

Modules and classes in Python for a desktop application

This is to be a desktop application for opening multiple small databases and running queries on them. So far I've written some code for opening forms as necessary. Is this a good way to do it? Also - the code shown opens two copies of each form - what am I doing wrong? It's my first attempt at Python and I'm a rudimentary programmer so simple answers would be of most help please. TIA (Python 3.9.6)
link_1.py
from tkinter import Tk
import link_2
root = Tk()
class ClassLink_1:
#if another function in the class is called, the __init__ function is run at start up
def __init__(self):
print("in link_1 __init__ instruction")
#the call_function function can be called at start up, or not, and will act accordingly
def call_function(self):
print("in call_function")
#the line below is run at start up whether or not another function in the class is called
print("in link_1")
root.withdraw() #hides the blank form at start up
#if __name__ == "__main__":
#the line below shows the link_2 form, whether or not the if __name__==__main__ condition is used as its condition
link_2.ClassLink_2(root).__init__(root)
#link_3.ClassLink_3(root).__init__(root)
#the line below runs call_function on start up to print text
ClassLink_1().call_function()
root.mainloop()
link_2.py
from tkinter import Tk, Button
from tkinter import * #for Toplevel
import link_3
root = Tk()
class ClassLink_2:
def __init__(self, master):
self.openNewWindow()
def openNewWindow(self):
newWindow = Toplevel(root) #creates a top level widget with the parent root (first parameter)
newWindow.title("Title opened from link_1")
newWindow.geometry("500x500")
label = Label(newWindow, text ="Opened from link_1").grid(row=1, column=1)
self.add_button = Button(newWindow, text="in ClassLink_2", command= self.do_add)
self.add_button.grid(row=3, column=1)
def do_add(self):
print("button pressed")
link_3.ClassLink_3(root).__init__(root)
root.withdraw() #hides the blank form at start up
link_3.py
from tkinter import Tk, Button
from tkinter import * #for Toplevel
root = Tk()
class ClassLink_3:
def __init__(self, master):
self.openNewWindow()
def openNewWindow(self):
newWindow = Toplevel(root) #creates a top level widget with the parent root (first parameter)
newWindow.title("Title opened from link_2")
newWindow.geometry("500x500")
label = Label(newWindow, text ="Opened from link_2").grid(row=1, column=1)
self.add_button = Button(newWindow, text="in ClassLink_3", command= self.do_add)
self.add_button.grid(row=3, column=1)
def do_add(self):
print("button pressed")
# link_4.ClassLink_4(root).__init__(root) this file has not yet been made
root.withdraw() #hides the blank form at start up
This is a proposed solution, can be expanded as needed. Constructive suggestions for improvement of the structure or code would be appreciated. TIA. I've left in the details in case they are of use to anyone.
link_1
from tkinter import Tk
import link_2
root = Tk()
class ClassLink_1:
def __init__(self):
print("in link_1 __init__ instruction")
root.withdraw() #hides the blank form at start up
link_2.ClassLink_2(root).openNewWindow(0)
root.mainloop()
link_2
from tkinter import Tk, Button
from tkinter import *
import link_3
root = Tk()
class ClassLink_2:
root.withdraw() #hides the blank form at start up
class_var_1 = 0
inst_var_1 = 0
def __init__(self, incoming_inst_var_1):
pass
def openNewWindow(self, inst_var_1_to_open):
newWindow = Toplevel(root)
newWindow.title("Title opened from link_1")
newWindow.geometry("500x500")
label = Label(newWindow, text ="Opened from link_1").grid(row=1, column=1)
self.add_button = Button(newWindow, text="in ClassLink_2", command= self.do_add)
self.add_button.grid(row=3, column=1)
self.add_button = Button(newWindow, text="increment class_var_1", command= self.inc_class_var_1)
self.add_button.grid(row=5, column=1)
self.inst_var_1 = inst_var_1_to_open
def do_add(self):
print("button pressed")
link_3.ClassLink_3(root).openNewWindow(0)
def inc_class_var_1(self):
ClassLink_2.class_var_1 += 2
self.inst_var_1 += 1
print("self.class_var_1 = " + (str)(self.class_var_1))
print("self.inst_var_1 = " + (str)(self.inst_var_1))
link_3
from tkinter import Tk, Button
from tkinter import *
from tkinter.ttk import Combobox
import tkinter.scrolledtext as tkscrolled
# import link_4 <filename of next form>
root = Tk()
class ClassLink_3:
class_var_1 = 0
inst_var_1 = 0
# ------------------------------- START CLASS CONTROLS -----------------------------
root.withdraw()
def __init__(self, incoming_inst_var_1):
pass
def openNewWindow(self, inst_var_1_to_open):
new_window = Toplevel(root)
self.widget_factory(new_window)
self.inst_var_1 = inst_var_1_to_open
def do_add(self):
print("button pressed")
# link_4.ClassLink_4(root).openNewWindow(0) # <filename of next form>
# ---------------------------------- END CLASS CONTROLS -----------------------------
# -------------------------------------- START CALCS --------------------------------------
def inc_class_var_1(self):
ClassLink_3.class_var_1 += 2
self.inst_var_1 += 1
print("self.class_var_1 = " + (str)(self.class_var_1))
print("self.inst_var_1 = " + (str)(self.inst_var_1))
# ---------------------------------------- END CALCS --------------------------------------
# ---------------------------------------- START FACTORY SHOPS-----------------------------------------
def widget_factory(self, new_window):
self.shop_window(new_window)
self.shop_labels(new_window)
self.shop_buttons(new_window)
self.shop_menu(new_window)
self.shop_listbox(new_window)
self.shop_combobox(new_window)
self.shop_radiobuttons(new_window)
self.shop_checkbuttons(new_window)
self.shop_entries(new_window)
self.shop_canvas(new_window)
self.shop_scale(new_window)
self.shop_scrollbars(new_window)
self.shop_textbox(new_window)
self.shop_menu(new_window)
pass
# ------------------------
def shop_window(self, new_window):
new_window.title("Title opened from link_2")
new_window.geometry("800x900")
def shop_labels(self, new_window):
self.label_1 = Label(new_window, text ="Opened from link_2").grid(row=1, column=1)
def shop_buttons(self, new_window):
self.button_1 = Button(new_window, text="in ClassLink_3", command= self.do_add)
self.button_1.grid(row=3, column=1)
self.button_2 = Button(new_window, text="increment class_var_1", command= self.inc_class_var_1)
self.button_2.grid(row=5, column=1)
def shop_listbox(self, new_window):
data=("one", "two", "three", "four")
self.listbox_1 = Listbox(new_window, height=5, selectmode='multiple')
for num in data:
self.listbox_1.insert(END,num)
self.listbox_1.grid(row=7, column=1)
def shop_combobox(self, new_window):
data=("one", "two", "three", "four")
self.combobox_1 = Combobox(new_window, values=data)
self.combobox_1.grid(row=8, column=3)
def shop_radiobuttons(self, new_window):
var_1 = IntVar()
var_1.set(1)
self.rad_btn_1 = Radiobutton(new_window, text="male", variable=var_1, value=1)
self.rad_btn_2 = Radiobutton(new_window, text="female", variable=var_1, value=2)
self.rad_btn_1.grid(row=9, column=1)
self.rad_btn_2.grid(row=9, column=2)
def shop_checkbuttons(self, new_window):
var_1 = IntVar()
var_2 = IntVar()
self.checkbtn_1 = Checkbutton(new_window, text = "Cricket", variable = var_1)
self.checkbtn_2 = Checkbutton(new_window, text = "Tennis", variable = var_2)
self.checkbtn_1.grid(row=10, column=1)
self.checkbtn_2.grid(row=10, column=2)
def shop_entries(self, new_window):
self.entry_1 = Entry(new_window, width = 20)
self.entry_1.insert(0,'Username')
self.entry_1.grid(row= 11, column=2)
self.entry_2 = Entry(new_window, width = 15)
self.entry_2.insert(0,'password')
self.entry_2.grid(row= 12, column=2)
#might want to place on a frame, see example https://coderslegacy.com/python/list-of-tkinter-widgets/
def shop_canvas(self, new_window):
canvas = Canvas(new_window, bg= 'white', width = 260,height = 260) #this is the background for the figure
# left and top of figure is from [x from left, y from top] of canvas right and bottom of figure from [x from left, y from top] of canvas
coordinates = 20, 50, 210, 230
arc = canvas.create_arc(coordinates, start=0, extent=250, fill="blue") #start is from E, extent is in degrees CCW
arc = canvas.create_arc(coordinates, start=250, extent=50, fill="red")
arc = canvas.create_arc(coordinates, start=300, extent=60, fill="yellow")
canvas.grid(row=2, column=1)
def shop_scale(self, new_window):
self.scale_1 = Scale(new_window, from_=0, to=10, orient=VERTICAL)
self.scale_1.grid(row=15, column=2)
self.scale_2 = Scale(new_window, from_=0, to=10, orient = HORIZONTAL)
self.scale_2.grid(row=15, column=3)
def shop_scrollbars(self, new_window):
self.scroll_vert = Scrollbar(new_window)
self.scroll_vert.grid(row=19, column=3)
self.listbox_3 = Listbox(new_window, yscrollcommand = self.scroll_vert.set )
for line in range(1, 100):
self.listbox_3.insert(END, "Number " + str(line))
self.listbox_3.grid(row=19 , column=2)
self.scroll_vert.config(command = self.listbox_3.yview)
def shop_textbox(self, new_window):
#make a frame with parent new_window
self.frame_textbox_1 = Frame(new_window)
self.frame_textbox_1.grid(row=1, column=5)
#make the textbox with parent frame
self.textbox_1 = Text(self.frame_textbox_1)
self.textbox_1.config(wrap=NONE, width=15, height=8) #width, height are characters x rows permitted wrap values should be WORD CHAR, or NONE
#make the X scrollbar with parent frame
self.scroll_text_horiz = Scrollbar(self.frame_textbox_1, orient="horizontal")
self.scroll_text_horiz.config(command = self.textbox_1.xview)
#make the Y scrollbar with parent frame
self.scroll_text_vert = Scrollbar(self.frame_textbox_1, orient="vertical")
self.scroll_text_vert.config(command = self.textbox_1.yview)
#pack the scrollbars before the texbox to allow them to fill the frame width and height
self.scroll_text_horiz.pack(side=BOTTOM, fill=X)
self.scroll_text_vert.pack(side=RIGHT, fill=Y)
self.textbox_1.pack(fill="y")
#connect the scrollbars to the textbox
self.textbox_1["xscrollcommand"] = self.scroll_text_horiz.set
self.textbox_1["yscrollcommand"] = self.scroll_text_vert.set
message = "the quick brown fox"
self.textbox_1.insert(1.1, message)
# ----------------------------------------------
def shop_menu(self, new_window):
print("in shop menu1")
menubar = Menu(new_window)
print("in shop_menu2")
file = Menu(menubar, tearoff=0)
file.add_command(label="New")
file.add_command(label="Open")
file.add_command(label="Save")
file.add_command(label="Save as...")
file.add_command(label="Close")
file.add_separator()
file.add_command(label="Exit", command=new_window.quit)
menubar.add_cascade(label="File", menu=file)
edit = Menu(menubar, tearoff=0)
edit.add_command(label="Undo")
edit.add_separator()
edit.add_command(label="Cut")
edit.add_command(label="Copy")
edit.add_command(label="Paste")
edit.add_command(label="Delete")
edit.add_command(label="Select All")
menubar.add_cascade(label="Edit", menu=edit)
help = Menu(menubar, tearoff=0)
help.add_command(label="About")
menubar.add_cascade(label="Help", menu=help)
new_window.config(menu=menubar)
print("in shop menu3")
# --------------------------------------- END FACTORY SHOPS---------------------------------------

tkinter display new frame from menu

I'm trying to write my fisrt script in python & tkinter.
I block to display a new frame from a function (def onDisplay) when a click is made from menu bar button, but nothing append.
No error is display :-(
The new frame is made with dynamic checkboxes from text files:
txt file:
item1
item2
...
item100
A screen of the GUI:
Here my code:
from tkinter import Tk, Frame, Menu, Checkbutton, Text, TOP, BOTH, X, N, LEFT, BooleanVar
from tkinter.ttk import Frame, Label, Entry
import glob
class Example(Frame):
def __init__(self):
super().__init__()
self.initUI()
#self.display_srv()
def initUI(self):
self.master.title("Submenu")
menubar = Menu(self.master)
self.master.config(menu=menubar)
fileMenu = Menu(menubar)
submenu = Menu(fileMenu)
submenu.add_command(label="lst1", command=self.onDisplay)
submenu.add_command(label="lst2")
submenu.add_command(label="lst3")
fileMenu.add_cascade(label='Listing', menu=submenu, underline=0)
fileMenu.add_separator()
fileMenu.add_command(label="Exit", underline=0, command=self.onExit)
menubar.add_cascade(label="File", underline=0, menu=fileMenu)
#The frame i tried to display
def onDisplay(self):
self.master.title("display it")
self.pack(fill=BOTH, expand=True)
frame1 = Frame(self)
frame1.pack(fill=X)
path = '/root/liste/*.txt'
files=glob.glob(path)
count = 0
for file in files:
with open(file, 'r') as lst_file:
for item in lst_file:
# Need to split all item by 10
Checkbutton(self, text=item.rstrip()).grid(row=count//10, column=count%10)
count += 1
def onClick(self):
if self.var.get() == True:
self.master.title("Checkbutton")
else:
self.master.title("")
def onExit(self):
self.quit()
def main():
root = Tk()
root.geometry("850x550+300+300")
app = Example()
root.mainloop()
if __name__ == '__main__':
Many thanks for any help
Regards,
The main problem is that you're mixing grid and pack in the same parent container. You are calling pack on frame1 but you are calling grid on the checkbuttons, and they both have a master or parent of self.
That can't work, because each of grid and pack will try to resize the container according to it's rules, triggering the other one to reconfigure according to it's rules, and so on until the end of time.
So, simply changing your call of .grid(...) to .pack(...) on the checkbuttons will fix that problem.
My guess is that you intended to put the checkbuttons inside frame1. If that's the case, you need to specify frame1 as the master for the checkbuttons. For readability and ease of debugging I also recommend placing the call to grid on a separate line. With that, you can continue to use grid for the checkbuttons and pack for everything else.
cb = Checkbutton(frame1, text=item.rstrip())
cb.grid(row=count//10, column=count%10)
I posted the correct code:
from tkinter import Tk, Frame, Menu, Checkbutton, Text, TOP, BOTH, X, N, LEFT, BooleanVar
from tkinter.ttk import Frame, Label, Entry
import glob
class Example(Frame):
def __init__(self):
super().__init__()
self.initUI()
#self.display_srv()
def initUI(self):
self.master.title("Submenu")
menubar = Menu(self.master)
self.master.config(menu=menubar)
fileMenu = Menu(menubar)
submenu = Menu(fileMenu)
submenu.add_command(label="lst1", command=self.onDisplay)
submenu.add_command(label="lst2")
submenu.add_command(label="lst3")
fileMenu.add_cascade(label='Listing', menu=submenu, underline=0)
fileMenu.add_separator()
fileMenu.add_command(label="Exit", underline=0, command=self.onExit)
menubar.add_cascade(label="File", underline=0, menu=fileMenu)
def onDisplay(self):
self.master.title("display it")
self.pack(fill=BOTH, expand=True)
frame1 = Frame(self)
frame1.pack(fill=X)
path = '/root/liste/*.txt'
files=glob.glob(path)
count = 0
for file in files:
with open(file, 'r') as lst_file:
for item in lst_file:
cb = Checkbutton(frame1, text=item.rstrip())
cb.grid(row=count//10, column=count%10)
count += 1
def onClick(self):
if self.var.get() == True:
self.master.title("Checkbutton")
else:
self.master.title("")
def onExit(self):
self.quit()
def main():
root = Tk()
root.geometry("850x550+300+300")
app = Example()
root.mainloop()
if __name__ == '__main__':
main()

Python tkinter checkbutton value not accessible

I want to build a little GUI application in Python. The goal is to have a main window calling several other windows. In one of these called windows I have a checkbutton. My problem is that I cannot read the value of this checkbutton, whereas I can read the value of an Entry widget. What am I doing wrong?
from tkinter import *
import tkinter as tk
class mainwindow():
def __init__(self, master):
self.master = master
menubalk = Menu(self.master)
menubalk.add_command(label="New window", command=self.openNewwindow)
self.master.config(menu=menubalk)
def openNewwindow(self):
window = newwindow()
window.mainloop()
class newwindow(Tk):
def __init__(self):
Tk.__init__(self)
self.var = BooleanVar()
self.checkbutton = Checkbutton(self, text="Check", variable=self.var)
self.checkbutton.grid(column=0, row=0)
self.var2 = StringVar()
self.entry = Entry(self, textvariable=self.var2)
self.entry.grid(column=2,row=0)
self.button2 = Button(self,text=u"Show", command=self.showValues).grid(column=1, row=0)
def showValues(self):
print('Value checkbutton:', self.var.get(), ';', 'Value entryfield: ', self.entry.get())
def main():
root = Tk()
window = mainwindow(root)
root.mainloop()
if __name__ == '__main__':
main()
You are making multiple, separate Tkinter applications in your program. Do not do that. To create new windows, use the Toplevel widget.
from tkinter import *
class mainwindow():
def __init__(self, master):
self.master = master
menubalk = Menu(self.master)
menubalk.add_command(label="New window", command=self.openNewwindow)
self.master.config(menu=menubalk)
def openNewwindow(self):
def showValues(var, entry):
print('Value checkbutton:', var.get(), ';', 'Value entryfield: ', entry.get())
window = Toplevel(self.master)
var = BooleanVar()
checkbutton = Checkbutton(window, text="Check", variable=var)
checkbutton.grid(column=0, row=0)
var2 = StringVar()
entry = Entry(window, textvariable=var2)
entry.grid(column=2,row=0)
button2 = Button(window,text=u"Show", command=lambda: showValues(var, entry))
button2.grid(column=1, row=0)
def main():
root = Tk()
window = mainwindow(root)
root.mainloop()
if __name__ == '__main__':
main()
Tkinter's variable objects (IntVar, StringVar, etc.) must take argument "master" as their firs parameter. i.e. replace
self.var=StringVar()
With
self.var=StringVar(self)
Or
self.var=StringVar(master=self)

Update User interface when a button is pressed Tkinter

So basically i want the user interface to be updated when a user presses the start button, however when i call the maininit function it returns an error saying that root is not defined, is there any way i can get around this?
from PIL import Image, ImageTk
from Tkinter import Tk, Label, BOTH,W, N, E, S, Entry, Text, INSERT, Toplevel
from ttk import Frame, Style, Button, Label
import Tkinter
import Callingwordlist
class MainGameUI(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("Type!")
self.pack(fill=BOTH, expand=1)
style = Style()
style.configure("TFrame", background="black")
Type = Image.open("Type!.png")
Typet = ImageTk.PhotoImage(Type)
label3 = Label(self, image=Typet)
label3.image = Typet
label3.place(x=0, y=0)
self.pack(fill=BOTH, expand=1)
MenuButton = Button(self, text="Main Menu")
MenuButton.pack()
MenuButton.place(x=560,y=20,height = 80,width = 100)
QuitButton = Button(self,text="Quit",command=self.parent.destroy)
QuitButton.pack()
QuitButton.place(x=680,y=20,height = 80,width = 100)
StartButton = Button(self, text="Start",command=maininit)
StartButton.pack()
StartButton.place(x=440,y=20,height=80,width=100)
def maininit():
entry1 = Entry(root,font =("Courier",38), width = 22)
entry1.pack(ipady=10)
entry1.config(bg="#CEF6F5")
entry1.place(x=90,y=200)
entry2 = Entry(root,font =("Courier",38), width = 22)
entry2.pack(ipady=10)
entry2.config(bg="#CEF6F5")
entry2.place(x=90,y=350)
text1 = Text(root,width=23,height=1,font=("Courier",38))
text1.pack()
text1.config(bg="black",fg="white",bd=0)
text1.place(x=90,y=150)
text1.insert(INSERT,"Hello")
text2 = Text(root,width=23,height=1,font=("Courier",38))
text2.pack()
text2.config(bg="black",fg="white",bd=0)
text2.place(x=90,y=300)
text2.insert(INSERT,"Test")
dtext = Text(root,font=("Courier",28),width=10,height=1)
dtext.pack()
dtext.config(bg="black",fg="white",bd=0)
dtext.insert(INSERT,"Difficulty")
dtext.place(x=90,y=500)
atext = Text(root,font=("Courier",28),width=8,height=1)
atext.pack()
atext.config(bg="black",fg="white",bd=0)
atext.insert(INSERT,"Accuracy")
atext.place(x=595,y=500)
dentry = Text(root,font=("Courier",28),width=1,height=1)
dentry.pack()
dentry.config(bg="white",bd=0)
dentry.place(x=180,y=550)
dentry.insert(INSERT,"Test")
def main():
root = Tk()
root.geometry("860x640+300+300")
app = MainGameUI(root)
root.mainloop()
if __name__ == '__main__':
main()
In your application, root is a locally defined variable confined to main(). You need to somehow pass root as an argument to maininit. Here is one way to do this:
First, change maininit() so that it accepts a parameter root:
def maininit(root):
...
Now, change the callback on StartButton so it passes maininit() the root object:
class MainGameUI(Frame):
...
def initUI(self):
...
StartButton = Button(self, text="Start",command=lambda: maininit(self.parent))
...
You are defining root inside a function, whose calling namespace is not available to the maininit() function. If you omit the definition of main() and instead write
if __name__ == "__main__":
root = Tk()
root.geometry("860x640+300+300")
app = MainGameUI(root)
root.mainloop()
root will then be defined in the global module namespace, where it IS available to the code in the function.

Categories

Resources