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.
Related
I am trying to create a terminal GUI using python tkinter module and I already managed to access the tk.Label from TextFrame class.
class TextFrame(tk.Frame):
def __init__(self, container):
super().__init__()
self.container = container
self['bg']='black'
self.pack(fill='both', expand=True)
self.label = tk.Label(self)
Now the problem is that, I can't change the row everytime I call show() function inside Instances class.
class Instances(Window):
def __init__(self):
super().__init__()
self.string = tk.StringVar()
self.index_row = 0
self.text_frame = TextFrame(self)
self.text_frame.pack()
self.entry_frame = EntryFrame(self)
self.entry_frame.pack(side='bottom',fill='both')
def show(self, e):
textvar = self.string.get()
self.entry_frame.entry.delete(0, 'end')
self.text_frame.label.config(text=textvar)
self.text_frame.label.grid(column=0, row=self.index_row, sticky="w")
self.index_row += 1
Here is the full code of my problem:
import tkinter as tk
class Window(tk.Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.geometry('800x600')
self.title('Terminal')
class Instances(Window):
def __init__(self):
super().__init__()
self.string = tk.StringVar()
self.index_row = 0
self.text_frame = TextFrame(self)
self.text_frame.pack()
self.entry_frame = EntryFrame(self)
self.entry_frame.pack(side='bottom',fill='both')
def show(self, e):
textvar = self.string.get()
self.entry_frame.entry.delete(0, 'end')
self.text_frame.label.config(text=textvar)
self.text_frame.label.grid(column=0, row=self.index_row, sticky="w")
self.index_row += 1
class TextFrame(tk.Frame):
def __init__(self, container):
super().__init__()
self.container = container
self['bg']='black'
self.pack(fill='both', expand=True)
self.label = tk.Label(self)
class EntryFrame(tk.Frame):
def __init__(self, container):
super().__init__()
self.entry = tk.Entry(self, textvariable=container.string)
self.entry.pack(fill='both')
self.entry.bind('<Return>', container.show)
if __name__ == '__main__':
window = Instances()
window.mainloop()
Any help would be appreciated.
Problem Solved
I added 1 line of code on show() function
def show(self, e):
textvar = self.string.get()
self.entry_frame.entry.delete(0, 'end')
self.text_frame.label = tk.Label(self.text_frame, fg='green', bg='black')
self.text_frame.label.config(text=textvar)
if len(textvar) > 0 :
self.text_frame.label.grid(column=0, row=self.index_row, sticky="w")
self.index_row += 1
I have this code below, and I want to pass variable n in class Data to be used in class tab1 and be used as the textvariable of entry1. however, I get this error:
AttributeError: 'dict' object has no attribute 'n'
or in general, I want to be able to pass variables between tabs.
import tkinter as tk
from tkinter import ttk
class Data:
def __init__(self):
self.n = tk.IntVar()
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.minsize(400, 400)
self.maxsize(400, 400)
self.my_notebook = ttk.Notebook(self)
self.my_notebook.pack(fill="both", expand=True)
self.app_data = {}
lst = [tab1, tab2, tab3]
for N, F in enumerate(lst):
tab = F(self.my_notebook, self.app_data)
self.my_notebook.add(tab, text="Tab"+str(N+1))
self.data = Data()
class tab1(tk.Frame):
def __init__(self, parent, data):
super().__init__(parent)
self.data = data
entry1 = tk.Entry(self, textvariable=data.n)
entry1.pack()
class tab2(tk.Frame):
def __init__(self, parent, data):
super().__init__(parent)
self.data = data
label2 = tk.Label(self, text="Tab2")
label2.pack()
class tab3(tk.Frame):
def __init__(self, parent, data):
super().__init__(parent)
self.data = data
label3 = tk.Label(self, text="Tab3")
label3.pack()
app = SampleApp()
app.mainloop()
You never create an instance of Data. Instead, you're initializing self.data to an empty dictionary. You need to create an instance of Data and pass that to the tabs.
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
...
self.app_data = Data()
...
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()
I'm trying to get this so when you press 'HELLO' five time the text turn red
just its not adding any thing on when i add anything to the viable.this is the code.
from tkinter import *
class application(Frame):
global t
t=1
def info(self):
print ("test")
global t
t=t+5
def createWidgets(self):
global t
t=t
self.exet= Button(self)
self.exet["text"] = "QUIT"
self.exet["fg"] = "red"
self.exet["command"] = self.quit
self.exet.pack({"side": "left"})
self.hi = Button(self)
self.hi["text"] = "HELLO",
if t == 5:
self.hi["fg"] = "red"
self.hi["command"] = self.info
self.hi.pack({"side": "left"})
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack()
self.createWidgets()
Thanks to anyone helps!
You have a couple of problems here: First, you use a global variable and then you include it within the scope of your functions. Instead of this, you should use an instance variable (self.t, or even self.counter for better readability). Secondly, you are checking the value of the counter in createWidgets, which is only called once by the __init__ method. You should increase and check its value in the event handler function on the button.
class application(Frame):
def info(self):
self.counter += 1
print(self.counter)
if self.counter == 5:
self.hi["fg"] = "red"
def createWidgets(self):
self.counter = 0
self.exet= Button(self)
self.exet["text"] = "QUIT"
self.exet["fg"] = "red"
self.exet["command"] = self.quit
self.exet.pack({"side": "left"})
self.hi = Button(self)
self.hi["text"] = "HELLO",
self.hi["command"] = self.info
self.hi.pack({"side": "left"})
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack()
self.createWidgets()
In Tkinter, how would look like the code of button that adds a widget when clicked, infinitely if necessary?
Thanks and sorry for bad english.
This is a more "classy" version:
from Tkinter import *
class Application(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.number = 0
self.widgets = []
self.grid()
self.createWidgets()
def createWidgets(self):
self.cloneButton = Button ( self, text='Clone', command=self.clone)
self.cloneButton.grid()
def clone(self):
widget = Label(self, text='label #%s' % self.number)
widget.grid()
self.widgets.append(widget)
self.number += 1
if __name__ == "__main__":
app = Application()
app.master.title("Sample application")
app.mainloop()
Note you keep your widgets in self.widgets list, so you can recall them and modify them if you like.
Well it could look something like this (it could look like a lot of different things):
import Tkinter as tk
root = tk.Tk()
count = 0
def add_line():
global count
count += 1
tk.Label(text='Label %d' % count).pack()
tk.Button(root, text="Hello World", command=add_line).pack()
root.mainloop()