Tkinter widgets not showing on mac (using PyCharm and Terminal) - python

I am using python 3.9 (and have also tried my code with Python 3.10 in terminal) on a mac (OS High Sierra) and my IDE is PyCharm. No Widgets I have tried with tkinter are showing up. Here I am using Label but I have also used Entry too - i just get a blank window:
Here is my code:
import tkinter
from tkinter import ttk
class View:
def __init__(self, model):
self.root = tkinter.Tk()
self.root.title("Play Suduko")
self.root.grid()
self.form = ttk.Frame(self.root, padding=10)
self.model = model
self.S = self.model.S
for self.i, self.row in enumerate(self.S):
for self.j, self.element in enumerate(self.row):
if self.element != 0:
self.label = ttk.Label(self.form, text=str(self.element))
self.label.grid(row=self.i, column=self.j)
self.root.mainloop()
Not sure what I'm doing wrong. I'm pretty sure it's not the IDE because as I said I tried it in the Terminal with Python 3.10 and got the same thing.
Any help much appreciated.
Edit:
I updated my code to remove form. So here is the new code:
from tkinter import *
from tkinter.ttk import *
class View(Frame):
def __init__(self, model):
self.root = Tk()
self.root.title("Play Suduko")
Frame.__init__(self)
self.model = model
self.S = self.model.S
for self.i, self.row in enumerate(self.S):
for self.j, self.element in enumerate(self.row):
if self.element != 0:
self.label = Label(self.root, textvariable=str(self.element), width=2)
self.label.grid(row=self.i, column=self.j, padx=10, pady=10)
self.root.mainloop()
And this is the window I am seeing now:

Fixed:
from tkinter import *
from tkinter.ttk import *
class View(Frame):
def __init__(self, model):
self.root = Tk()
self.root.title("Play Suduko")
Frame.__init__(self, self.root)
self.model = model
self.S = self.model.S
for self.i, self.row in enumerate(self.S):
for self.j, self.element in enumerate(self.row):
if self.element == 0:
self.element = ""
self.label = Label(self.root, text=str(self.element), width=2, font=("Arial", 40))
self.label.grid(row=self.i, column=self.j+1, padx=5, pady=5)
self.root.mainloop()
:)

Related

Resizing ttk.frame of a ttk notebook, frame defined in a other class than the notebook

I have a main class for my gui and I added a ttk.NoteBook after a label:
class MainApplication(tk.Tk):
def __init__(self):
super().__init__()
self.geometry('1000x500')
self.configure(background='#F0F8FF')
#TOP LABEL
load = Image.open("my_image")
load = load.resize((200, 67), Image.ANTIALIAS)
self.render = ImageTk.PhotoImage(load)
self.Label_top = tk.Label(self, image=self.render, compound=tk.LEFT, text="TOOL")
self.Label_top.pack()
#--Notebook---------
self.notebook = ttk.Notebook(self)
self.Page1 = Page1(self.notebook)
self.Page2 = Page2(self.notebook)
self.Page3 = Page3(self.notebook)
self.Page4 = Page4(self.notebook)
self.notebook.add(self.Page1, text='PAGE1')
self.notebook.add(self.Page2, text='PAGE2')
self.notebook.add(self.Page3, text='PAGE3')
self.notebook.add(self.Page4, text='PAGE4')
self.notebook.pack(fill='x', side=TOP)
#expand=True create empty space between my top label and my notebook, even with side=TOP
And I defined each frame in a class like this :
class Page1(ttk.Frame):
def __init__(self, container):
super().__init__()
self.(width=400, height=280) #Error message
self.pack(expand=True) #Doesn't work
Do you know how can I expand my frame for that it fills my page and pack the notebook just after my top label
I think this will do what you want. I've incorporated most of the things #Bryan Oakley mentioned in his answer except I also added a BasePage class and derived all the other Page classes from it. This was done to provide a place to put code that would otherwise need to be repeated each of the subclasses.
I also changed some of your variable names to conform to PEP 8 Naming Conventions.
import tkinter as tk
import tkinter.ttk as ttk
from tkinter.constants import *
class BasePage(ttk.Frame):
def __init__(self, container):
super().__init__(container, width=400, height=280)
classname = type(self).__name__
tk.Label(self, text=f'Welcome to {classname}').place(relx=0.5, rely=0.25,
anchor=CENTER)
class Page1(BasePage):
def __init__(self, container):
super().__init__(container)
class Page2(BasePage):
def __init__(self, container):
super().__init__(container)
class Page3(BasePage):
def __init__(self, container):
super().__init__(container)
class Page4(BasePage):
def __init__(self, container):
super().__init__(container)
class MainApplication(tk.Tk):
def __init__(self):
super().__init__()
self.geometry('1000x500')
self.configure(background='#F0F8FF')
#--Notebook---------
self.notebook = ttk.Notebook(self)
self.page1 = Page1(self.notebook)
self.page2 = Page2(self.notebook)
self.page3 = Page3(self.notebook)
self.page4 = Page4(self.notebook)
self.notebook.add(self.page1, text='Page1')
self.notebook.add(self.page2, text='Page2')
self.notebook.add(self.page3, text='Page3')
self.notebook.add(self.page4, text='Page4')
self.notebook.pack(expand=True, fill=BOTH)
app = MainApplication()
app.mainloop()
I see three problems.
First, each "page" needs to be a child of the notebook. You do that by making sure the notebook is passed to the __init__ of the frame:
class Page1(ttk.Frame):
def __init__(self, container):
super().__init__(container)
Second, you need to not call pack on the page. self.notebook.add is already adding the frame to the notebook. So, remove the line self.pack(expand=True) from each page.
Third, self.(width=400, height=280) needs to be self.configure(width=400, height=280)

Tkinter - The Root Window hides itself after close its Toplevel window

below my code:
from tkinter import *
from tkinter import ttk
class MainWindow:
def __init__(self):
self.parent=Tk()
self.parent.geometry("494x410+370+100")
self.parent.title("My Software - TEST")
self.parent.iconbitmap("icon.ico")
Button = ttk.Button(self.parent, text="open a new widnow", command=self.OpenNewWindow)
Button.place(x=16, y=16)
def OpenNewWindow(self):
self.obj = NewWindow(self)
class NewWindow:
def __init__(self, mw):
self.window, self.mw = Toplevel(mw.parent), mw
self.window.geometry("200x150+360+200")
self.window.title("New Window")
self.window.iconbitmap("icon.ico")
# the "try/except" code has an issue..
try:
self.window.focus()
self.mw.parent.attributes('-disabled', 1)
self.window.transient(mw.parent)
self.window.grab_set()
self.mw.parent.wait_window(self.window)
finally:
self.mw.parent.attributes('-disabled', 0)
def main():
app=MainWindow()
if __name__=="__main__":
main()
the try/except code makes the Toplevel window important. when it runs, the user can't touch the root window, and if he try to do it, a bell songs and the Toplevel window flashes. it's the exactly behaivour that I want! but this piece of code has an issue.. when the user close the Toplevel window, the root doesn't became the active window. it's a big issue because it makes the root window to go back the other ones. see my gif to understand better what I mean:
http://www.imagebam.com/image/c983ce1356199964
how can I solve this issue?
You've got the wrong idea about try ... finally; it does not work that way. There are 2 ways to do what you want. One way is to simply put that code in the main window:
from tkinter import *
from tkinter import ttk
class MainWindow:
def __init__(self):
self.parent=Tk()
self.parent.geometry("494x410+370+100")
self.parent.title("My Software - TEST")
self.parent.iconbitmap("icon.ico")
Button = ttk.Button(self.parent, text="open a new widnow", command=self.OpenNewWindow)
Button.place(x=16, y=16)
def OpenNewWindow(self):
self.parent.attributes('-disabled', 1)
self.obj = NewWindow(self)
self.parent.attributes('-disabled', 0)
class NewWindow:
def __init__(self, mw):
self.window, self.mw = Toplevel(mw.parent), mw
self.window.geometry("200x150+360+200")
self.window.title("New Window")
self.window.iconbitmap("icon.ico")
self.window.focus()
self.window.transient(mw.parent)
self.window.grab_set()
self.mw.parent.wait_window(self.window)
def main():
app=MainWindow()
app.parent.mainloop()
if __name__=="__main__":
main()
Another way is to make a method to run when the toplevel closes:
from tkinter import *
from tkinter import ttk
class MainWindow:
def __init__(self):
self.parent=Tk()
self.parent.geometry("494x410+370+100")
self.parent.title("My Software - TEST")
self.parent.iconbitmap("icon.ico")
Button = ttk.Button(self.parent, text="open a new widnow", command=self.OpenNewWindow)
Button.place(x=16, y=16)
def OpenNewWindow(self):
self.obj = NewWindow(self)
class NewWindow:
def __init__(self, mw):
self.window, self.mw = Toplevel(mw.parent), mw
self.window.geometry("200x150+360+200")
self.window.title("New Window")
self.window.iconbitmap("icon.ico")
self.window.protocol("WM_DELETE_WINDOW", self.on_close)
self.window.focus()
self.mw.parent.attributes('-disabled', 1)
self.window.transient(mw.parent)
self.window.grab_set()
self.mw.parent.wait_window(self.window)
def on_close(self):
self.mw.parent.attributes('-disabled', 0)
self.window.destroy()
def main():
app=MainWindow()
app.parent.mainloop()
if __name__=="__main__":
main()

Class Inheriting Tkinter LabelFrame Not Displaying Contents

As the title says, I've created a class that inherits from tkinter's LabelFrame (BatteryCapacityLFrame), and I'm trying to initialize it such that it will display like a LabelFrame. However, when I run the code the Battery tab doesn't display a LabelFrame. How do I fix this (what I presume to be) inheritance issue?
Code:
import tkinter as tk
from tkinter import ttk
from tkinter import *
class PanelManager(tk.Tk):
def __init__ (self):
#initializing tkinter within initialization function
tk.Tk.__init__(self)
self.title("Combat Robotics Calculator")
self.panel_manager = ttk.Notebook(self)
self.add_battery_tab("Batteries")
self.add_tab("Pulleys")
self.add_tab("Drive System")
self.add_tab("Weapon System")
def add_tab(self, title):
tab_frame = Frame(self.panel_manager)
self.panel_manager.add(tab_frame, text = title)
self.panel_manager.pack()
def add_battery_tab(self, title):
battery_tab = BatteryTab(self)
self.panel_manager.add(battery_tab, text = title)
self.panel_manager.pack()
def run(self):
self.mainloop()
class BatteryTab(tk.Frame):
def __init__ (self, master):
tk.Frame.__init__(self, master)
#Capacity Calculator
capacity_calcf = BatteryCapacityLFrame(self)
capacity_calcf.grid(column = 1, row = 1, sticky = "news")
#I'm trying to initialize the class here, not sure what's going wrong
class BatteryCapacityLFrame(tk.LabelFrame):
def __init__ (self, master):
tk.LabelFrame.__init__(self, master)
self.config(text = "Battery Capacity Calculator")
root_window = PanelManager()
root_window.run()
Thank you all so much for your time!
You need to add something to the labelframe for it to show up.
class BatteryCapacityLFrame(tk.LabelFrame):
def __init__(self, master):
tk.LabelFrame.__init__(self, master)
self.config(text="Battery Capacity Calculator")
tk.Label(self, text='Test message.').grid()

Tkinter passing callback function result to another class

I wonder how to pass return value from one class to another class in tkinter.
In my program I have DataChosenForm class where I want to choose option in Combobox and pass this result to another class ReturnData to set a variable in a Label.
import tkinter as tk
from tkinter import ttk
class DataChosenForm(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
chosen = tk.LabelFrame(self, text="wybór")
chosen.grid(row=0)
self.combo = ttk.Combobox(chosen)
self.combo['values'] = ('wizz', 'ryanair', 'lot')
self.combo.grid(row=0, column=2, padx=80, pady=10)
self.combo.bind("<<ComboboxSelected>>", self.callback)
def callback(self, event=None):
if event.widget.get() == 'wizz':
print('wizz')
return 'wizz'
elif event.widget.get() == 'ryanair':
print('ryanair')
return 'ryanair'
elif event.widget.get() == 'lot':
print('lot')
return 'lot'
class ReturnData(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
var = tk.StringVar()
message_box = tk.LabelFrame(self, text="wynik")
message_box.grid(row=1)
mb = tk.Label(message_box, textvariable=var,anchor='nw')
mb.pack(padx=120, pady=30)
class Application(tk.Tk):
def __init__(self):
super().__init__()
self.title("program do wyszukiwania cen lotów")
self.geometry('300x200')
self.resizable(width=False, height=False)
DataChosenForm(self).grid(row=0, column=0)
ReturnData(self).grid(row=1)
if __name__ == "__main__":
app = Application()
app.mainloop()
You could first display the combobox DataChosenForm(self).grid(row=0, column=0) without calling the ReturnData in the Application class.
Then, in the callback() method collect the choice choice = event.widget.get() and pass it to ReturnData. This would mean, however, that the LabelFrame is displayed only after a choice is made.
import tkinter as tk
from tkinter import ttk
class DataChosenForm(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
chosen = tk.LabelFrame(self, text="wybór")
chosen.grid(row=0)
self.combo = ttk.Combobox(chosen)
self.combo['values'] = ('wizz', 'ryanair', 'lot')
self.combo.grid(row=0, column=2, padx=80, pady=10)
self.combo.bind("<<ComboboxSelected>>", self.callback)
def callback(self, event=None):
choice = event.widget.get()
print(choice)
ReturnData(self, choice).grid(row=1)
class ReturnData(tk.Frame):
def __init__(self, parent, choice):
super().__init__(parent)
message_box = tk.LabelFrame(self, text="wynik")
message_box.grid(row=1)
mb = tk.Label(message_box, text=choice, anchor='nw')
mb.pack(padx=120, pady=30)
class Application(tk.Tk):
def __init__(self):
super().__init__()
self.title("program do wyszukiwania cen lotów")
self.geometry('300x200')
self.resizable(width=False, height=False)
DataChosenForm(self).grid(row=0, column=0)
if __name__ == "__main__":
app = Application()
app.mainloop()
I think #Julia has the basically the right architecture for your tkinter application, but her answer could be improved by using a tkinter Variable — because all widgets associated with one will automatically update their displayed value whenever the Variable is changed (by one of them or something else).
Here's a little documentation on Variable Classes. Note that since ttk.Combobox with have all the methods of a tk.Entry widgets, here's a bit of documentation about them (which also happens to illustrate the "pattern" of using of a StringVar in conjunction with one so it also applies to a ttk.Comboboxs).
Generally, you can tell a widget to use a tkinter Variable by specifying an instance of one as the option textvariable= keyword argument when the widget is created. You can also set the option using the partial dictionary interface most widgets support, so assignments like widget['textvariable'] = variable are another way to make use of them — the code below makes use of both of these ways.
Here's Julia's code modified to use a tk.StringVar. Note the Combobox doesn't need a callback function to bind the <<ComboboxSelected>> event to it, so all that complexity has been eliminated.
import tkinter as tk
from tkinter import ttk
class DataChosenForm(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
choice = tk.LabelFrame(self, text="wybór")
choice.grid(row=0)
self.combo = ttk.Combobox(choice)
self.combo['textvariable'] = parent.var # Use shared variable.
self.combo['values'] = ('wizzair', 'ryanair', 'lot')
self.combo.grid(row=0, column=2, padx=80, pady=10)
class ReturnData(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
message_box = tk.LabelFrame(self, text="wynik")
message_box.grid(row=1)
mb = tk.Label(message_box, textvariable=parent.var, # Use shared variable.
anchor='nw', width=20)
mb.pack(padx=120, pady=30)
class Application(tk.Tk):
def __init__(self):
super().__init__()
self.title("program do wyszukiwania cen lotów")
self.geometry('300x200')
self.resizable(width=False, height=False)
self.var = tk.StringVar(value='Dokonać wyboru') # Create shared variable.
DataChosenForm(self).grid(row=0, column=0)
ReturnData(self).grid(row=1)
if __name__ == "__main__":
app = Application()
app.mainloop()
You can just pass other class or it's field to __init__ of the DataChosenForm, and to callback function from there, where then you can change class/field directly. Here's what I mean, but I use TreeView:
import tkinter as tk
from tkinter import ttk
class ConnectedClass:
def __init__(self):
self.received = "will be changed"
class TreeViewWrapper(ttk.Treeview):
def __init__(self, master, connected_class, **kw):
super().__init__(master, **kw)
parents = []
for i in range(10):
parent = self.insert("", "end", text="Item %s" % i, tags=str(i))
for i in range(3):
self.insert(parent, "end", text="Item %s" % i, tags=str(i))
self.bind("<Control-r>", lambda e: self.pass_to_other(e, connected_class))
def pass_to_other(self, _, connected_class):
items = self.selection()
connected_class.received = items
class App:
def __init__(self):
self.root = tk.Tk()
self.con_class = ConnectedClass()
self.tree = TreeViewWrapper(self.root,self.con_class)
self.tree.pack()
self.root.bind("<Control-p>",lambda e:print(self.con_class.received))
self.root.mainloop()
if __name__ == "__main__":
app = App()

How to communicate between classes in Python?

I'm trying to build calculator based on tkinter. I also follow tips from there.
Program for sure is still in progress. I'm currently stuck because of this error:
AttributeError: 'Calculator_GUI' object has no attribute 'main'
I have no idea how to get to work communication between both classes. I'm also asking for short review of what already is done. Thanks in advance!
Here's the code:
import tkinter as tk
from tkinter import ttk
class Calculator_Core():
def __init__(self):
self.input = tk.StringVar()
def buttonClick(self, a):
self.input.set(a)
#DEBUG:
print(self.input)
class Calculator_GUI(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.master = master
self.button = {}
self.configure_gui()
self.create_widgets()
self.main = Calculator_Core()
def configure_gui(self):
self.master.title("Calculator")
self.master.resizable(False, False)
def create_widgets(self):
self.configure_input_space()
row_number = 1
column_number = 0
a = -1
text_buttons = ('789+', '456-', '123*', '0=/C')
for button_row in text_buttons:
for text_button in button_row:
a += 1
self.configure_button(text_button, row_number, column_number, a)
column_number += 1
row_number += 1
column_number = 0
def configure_button(self, text, row, column, key):
self.button[key] = ttk.Button(self.master, text=text,
command=lambda: self.main.buttonClick(text))
self.button[key].grid(row=row, column=column)
def configure_input_space(self):
self.entry = ttk.Entry(self.master, textvariable=self.main.input)
self.entry.grid(columnspan=4, sticky='we')
if __name__ == "__main__":
root = tk.Tk()
Calculator_GUI(root)
root.mainloop()
You need to 'make' the self.main before running the create_widgets() funtion, so this:
class Calculator_GUI(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.master = master
self.button = {}
self.configure_gui()
self.create_widgets()
self.main = Calculator_Core()
Should be like this:
class Calculator_GUI(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.master = master
self.main = Calculator_Core()
self.button = {}
self.configure_gui()
self.create_widgets()
Because the create_widgets() function runs the configure_button() and configure_input_space() functions which have references to self.main.

Categories

Resources