What i'm trying to do is in the onopen function in Controller class i'm trying to run the update_listbox function in the View class which will update the listbox. This is giving me the error update_listbox() must be called with View instance as first argument. I don't fully understand what i'm doing so it would be very helpful if someone could explain to me what i'm doing wrong here and how to do it correctly.
cheers tchadwik
from Tkinter import *
import tkMessageBox
import tkFileDialog
from tkFileDialog import askopenfilename
from tkMessageBox import askokcancel
class Controller(object):
def __init__(self, master=None):
self._master = master
#filemenubar
self.menu()
#listbox
self.listboxFrame = View(master)
self.listboxFrame.pack(fill=BOTH, expand=YES)
#entry widget
self.EntryFrame = Other(master)
self.EntryFrame.pack(fill = X)
def menu(self):
menubar = Menu(self._master)
self._master.config(menu=menubar)
fileMenubar = Menu(menubar)
fileMenubar.add_command(label="Open Products File", command=self.onopen)
fileMenubar.add_command(label="Save Products File", command=self.onsave)
fileMenubar.add_command(label="exit", command=self.onExit)
menubar.add_cascade(label="File", menu=fileMenubar)
def onopen(self):
fname = askopenfilename()
products.load_items(fname)
View.update_listbox() #
#this gives me error stating that it needs View instance as first argument
#and adding self here only gives it the controller instance
class View(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.list = Listbox(self, selectmode=EXTENDED)
self.list.pack(fill=BOTH, expand=YES)
self.current = None
self.update_listbox()
def update_listbox(self):
temp=products.get_keys()
for i in temp:
self.list.insert(END, str(products.get_item(i)))
You need to instantiate a View object. For example:
def onopen(self):
fname = askopenfilename()
products.load_items(fname)
myview = View(self._master) # Instantiate the object
myview.update_listbox() # Now call update_listbox()
This is because the memory for the member variables (for example: self.list) is not allocated until the object is instantiated. Or another way to put it is that self.list is not created until View.__init__() is called, which happens when you create a View object from the View class definition.
Related
I am trying to write a gui which as a class which is the main application. A single instance of that class is created in the main root. At the same time I want a submit button to be clicked where some values are verified before a further submission to write the data. I am trying to do this by creating a new class for the Toplevel pop up window. But I am not sure how best to structure this. Ideally an instance of the pop up window class would be created each time the button is selected. It seems like with the way I have structured it another instance of the main application class has been created. I am a little confused how to correctly do this using OOP.
Below is some sample code to illustrate the problem.
import tkinter as tk
from tkinter import ttk
class Window(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.title = "TITLE"
self.master = master
self.submit = ttk.Button(self, text = 'SUBMIT', command = self.click_submit_button)
self.submit.grid(row = 0, column = 2, padx = 20, pady = 20)
def click_submit_button(self):
self.submit_pop_up = submit_button(self.master)
print('New PopUp')
class submit_button(tk.Toplevel):
def __init__(self, master):
tk.Toplevel.__init__(self, master)
self.master = master
self.title = 'TITLE'
if __name__ == "__main__":
root = tk.Tk()
app = Window(root)
app.pack()
root.mainloop()
There is something missing from my understanding of the best approach to using OOP to structure a program like this.
Solution
Just make the submit_button class inherit the Window class and instantiate only the submiit_button class. With this, you don't need to instantiate the Window class. This allows for a special trick so that you can access the attributes of the submit_class class in the Window class without creating an instance. Just use self since it is actually an instance of the submit_class passed on to the Window class. Here is your code with that. There are many other suggestions and to know, see the code.
Suggestions
In the first place, why are you making submit_button a separate class? You could include it as a method of Window class. If you have good reason, it is ok but otherwise make it a method.
And also, why are you creating a root window and making the window class save it as an attribute? Just make the Window class inherit tk.Tk instead of tk.Frame. You can then create frame inside the __init__ function. Here is the code working code with these rectifications:
Code
import tkinter as tk
from tkinter import ttk
class Window(tk.Tk):
def __init__(self):
super().__init__()
self.title("TITLE")
self.submit = ttk.Button(self, text = 'SUBMIT', command = self.click_submit_button)
self.submit.grid(row = 0, column = 2, padx = 20, pady = 20)
class submit_button(Window):
def __init__(self):
super().__init__()
self.submit_pop_up = tk.Toplevel(self)
self.submit_pop_up.withdraw()
print(self.submit_pop_up)
def click_submit_button(self):
self.submit_pop_up.deiconify()
print('New PopUp')
if __name__ == "__main__":
app = submit_button()
app.mainloop()
Is that what you want? You should used self.master for all widgets.
import tkinter as tk
from tkinter import ttk
class Window(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.master = master
self.master.title( "TITLE")
self.submit = ttk.Button(self.master, text='SUBMIT', command=self.click_submit_button)
self.submit.grid(row=0, column=2, padx=20, pady=20)
def click_submit_button(self):
self.submit_pop_up = submit_button(self.master)
print('New PopUp')
class submit_button(tk.Toplevel):
def __init__(self, master):
tk.Toplevel.__init__(self, master)
self.master = master
self.master.title('TITLE')
if __name__ == "__main__":
root = tk.Tk()
app = Window(root)
#app.pack()
root.mainloop()
Result:
I'm having problems calling the .tab method to rename my tab and trying to run it results in the error in the title. Not sure why it would recognize the notebook variable but not the method. Any help would be greatly appreciated. Code:
from tkinter import *
from tkinter import ttk
import sqlite3
from sqlite3 import Error
conn = sqlite3.connect('loadout.db')
cur = conn.cursor()
class App(Tk):
def __init__(self,*args,**kwargs):
Tk.__init__(self,*args,**kwargs)
self.NewLoadoutBtn = ttk.Button(self, text="New Loadout",command=self.add_tab)
self.NewLoadoutBtn.pack(anchor=W)
self.EditLoadoutBtn = ttk.Button(self, text="Edit Loadout",command=self.add_tab)
self.EditLoadoutBtn.pack(anchor=W)
self.EditTankBtn = ttk.Button(self, text="Edit Tank",command=self.add_tab)
self.EditTankBtn.pack(anchor=W)
self.notebook = ttk.Notebook()
def add_tab(self):
self.tab1 = Loadout(self.notebook)
self.notebook.add(self.tab1,text="Loadout")
self.notebook.pack(expand=1, fill=BOTH)
self.NewLoadoutBtn.pack(anchor=E)
self.EditLoadoutBtn.pack_forget()
self.EditTankBtn.pack_forget()
class Loadout(ttk.Frame):
def __init__(self,*args,**kwargs):
Frame.__init__(self,*args,**kwargs)
customerquery = cur.execute("SELECT customers FROM misc")
self.CustomerBox = ttk.Combobox(self)
self.CustomerBox.grid(column=0, row=0)
self.CustomerBox['values'] = customerquery.fetchall()
self.CustomerBox.bind('<<ComboboxSelected>>', self.UpdateTitle)
def UpdateTitle(self,event):
CBget = self.CustomerBox.get()
App.notebook.tab(0, text=CBget)
my_app = App()
my_app.mainloop()
conn.commit()
conn.close()
Consider this code:
App.notebook.tab(0, text=CBget)
App is a class, not an instance of the class. The error is correct that the class does not have a notebook attribute because that attribute is only created once the class has been instantiated.
That line of code needs to use the instance of App:
my_app.notebook.tab(0, text=CBget)
However, it's not the best idea to rely on a global variable. It would probably be better to pass the instance in, perhaps like this:
class App(Tk):
...
def add_tab(self):
self.tab1 = Loadout(self.notebook, app=self)
class Loadout(ttk.Frame):
def __init__(self,*args,**kwargs):
self.app = kwargs.pop("app")
Frame.__init__(self,*args,**kwargs)
...
def UpdateTitle(self,event):
...
self.app.notebook.tab(0, text=CBget)
The goal is to pass the variable string1 from class Display to be used in another Tkinter window.
So when the button named Next [in class Display load function] is clicked, it would open a new Tkinter window. And in the new window, the variable string1 from class Display needs to be retrieved for further action. May i know should i create another class Display2, or should i just add a method in the class Display?
Currently the string variable can be passed as reference from class Display to the class Action_Data. But how can it be passed to another Tkinter window when the button Next is clicked?
I am trying to get the variable via the callback function new_window. Just not sure if it's how it's done. Any pointer would be appreciated. Many thanks.
from tkinter import *
import tkinter as tk
#Application window
root = tk.Tk()
#Display Class
class Display (tk.Frame):
def __init__(self, master, display_data):
tk.Frame.__init__(self,master)
self.master = master
#passing data as reference
self.display= display_data
#button
self.load_button = tk.Button(self, text="Load", command=self.load)
self.load_button.pack()
def new_window(self):
self.master = tk.Tk() # create another Tk instance
var_string2 = Label(self, text="<<string1 value>>")
var_string2.pack()
print (var_string2)
def load(self):
#get value
string1='value1'
self.display.action1(string1)
self.acition_button = tk.Button(self, text="Next",
command=self.new_window)
self.acition_button.pack()
#Action_Data Class
class Action_Data(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self,master)
def action1(self, path1):
var_path1 = path1
print(var_path1)
display= Action_Data(root)
display.pack()
reader = Display(root, display)
reader.pack()
pathlabel2 = Label(root)
root.mainloop()
Issue
Now the new window is blank and cannot retrieve the value of variable string1 from the load function
Error
Use the lambda function to pass the button's command, that way you can pass the needed string as an argument.
You do not need to create two instances of Tk, if you need another window, create a Toplevel.
from tkinter import *
import tkinter as tk
#Application window
root = tk.Tk()
#Display Class
class Display (tk.Frame):
def __init__(self, master, display_data):
tk.Frame.__init__(self,master)
self.master = master
#passing data as reference
self.display= display_data
#button
self.load_button = tk.Button(self, text="Load", command=self.load)
self.load_button.pack()
def new_window(self, string):
#self.master.destroy() # close the current window
new_window = tk.Toplevel() # create toplevel
var_string2 = Label(new_window, text=string)
var_string2.pack()
new_window.focus()
print (var_string2.cget("text"))
def load(self):
#get value
string1='value1'
self.display.action1(string1)
self.acition_button = tk.Button(self, text="Next",
command= lambda : self.new_window(string1))
self.acition_button.pack()
#Action_Data Class
class Action_Data(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self,master)
def action1(self, path1):
var_path1 = path1
print(var_path1)
display= Action_Data(root)
display.pack()
reader = Display(root, display)
reader.pack()
pathlabel2 = Label(root)
root.mainloop()
I have ran into some trouble.
I'm quite new to OOP and working with tkinter and GUI's in general.
I have managed to find some code on the Internet and meshed it all together to create something and I'm nearly where I want to be.
So what I want is some help figuring this out.
How can I assign results of askdirectory to a variable I can use elsewhere?
# coding=utf-8
import tkinter as tk
from tkinter import font as tkfont
from tkinter import filedialog
class MainApp(tk.Tk):
....
class SelectFunction(tk.Frame):
....
class FunctionChangeName(tk.Frame):
....
a = Gui(self)
# this gets me the askdirectory but how to add it to a variable?
Above is the call to run askdirectory code, and it works, just need to find out how to save it to a variable so I can use it, I have tried to print it in several ways, but all I get is something along the lines .!frame.!functionchangename.!gui.
class SelectDir:
def __init__(self, container, title, initial):
self.master = container
self.initial = initial
self.selected = initial
self.options = {'parent': container,'title': title,'initialdir':initial,}
def show(self):
result = filedialog.askdirectory()
if result:
self.selected = result
def get(self):
return self.selected
class Gui(tk.Frame):
def __init__(self, container):
tk.Frame.__init__(self, container)
frame = tk.Frame(container)
frame.pack()
self.seldir = SelectDir(self, "Select directory", "D:\\MyPgm\\Python\\Tiles_8")
button = tk.Button(frame, text="Select directory", command=self.select_dir)
button.grid(column=0, row=0)
self.act_dir = tk.StringVar()
self.act_dir.set("D:\\MyPgm\\Python\\Tiles_8")
entry = tk.Entry(frame, textvariable=self.act_dir, width=30)
entry.grid(column=0, row=1)
def select_dir(self):
self.seldir.show()
self.act_dir.set(self.seldir.get())
# or
# result = seldir.show()
# self.act_dir.set(result)
if __name__ == "__main__":
app = MainApp()
app.mainloop()
I had an idea:
example, if you have f inside a function, you can make it global to have access as variable
def print_path():
# select working directory
global f #make f global to access the path
f = filedialog.askdirectory(parent=root, initialdir="/", title='Select Dir')
This question already has an answer here:
How to save askdirectory result in a variable I can use using tkinter with OOP?
(1 answer)
Closed 4 years ago.
first of all im pretty new to OOP coding, so sorry for asking stupid questions.
I have a problem returning a value from Class AskDir that gets its value from Class SelectDir to Class MainWindow if that makes sense
Currently the code i have works, in every other way, except I cannot save the actual "self" (which is the path i.e. "/home/etc/") return value from Class AskDir() to another variable no matter what i do.
From what i have read i should be able to get the return value with a = AskDir(self)
print(a), but what I get instead is ".!frame.!functionchangename.!askdir"
So in short, how to save actual return path from a function inside Class AskDir, to be saved to variable a in Class MainWindow()?
To clarify, what i want is for Class MainWindow() to have a variable (a) that gets the return value from a subfunction get() inside Class SelectDir(), and that value should be the path that get() function returns
simplified code:
class MainWindow:
self.controller = controller
# prints the button to this frame!
getdir = AskDir(self)
print(getdir) # this should return actual path (/home/etc/), but it doesnt
class AskDir(tk.Frame):
def __init__(self, container):
tk.Frame.__init__(self, container)
self.selectdir = SelectDir(self, "Select directory",
"/home/user/folder/")
button = tk.Button(frame, text="Select directory",
command=self.select_dir)
self.act_dir = tk.StringVar()
self.act_dir.set("/home/")
def select_dir(self):
self.selectdir.show()
self.act_dir.set(self.selectdir.get())
class SelectDir:
def __init__(self, container, title, initial):
self.master = container
def show(self):
result = filedialog.askdirectory()
if result:
self.selected = result
# THIS RETURNS THE ACTUAL PATH!
def get(self):
return self.selected
Does this work for you?
I can see no need to have SelectDir as a class given that its purpose is just to call the askdirectory method and return the selected folder path so I've change it to a function.
With this example, pressing the "Select directory" button will open the directory dialog and then place the selected directory text in to an Entry widget.
If the MainWindow class wishes to do anything with the path once it has been set then you just need to call the get_dir method of AskDir e.g. print(askDir.get_dir()
import tkinter as tk
from tkinter import filedialog
def doStuffFunction(value):
print(value)
class MainWindow(tk.Frame):
def __init__(self,master=None,**kw):
tk.Frame.__init__(self,master=master,**kw)
self.askDir = AskDir(self)
self.askDir.grid()
doStuffButton = tk.Button(self, text="Do Stuff", command=self.doStuff)
doStuffButton.grid()
def doStuff(self):
doStuffFunction(self.askDir.get_dir())
class AskDir(tk.Frame):
def __init__(self, master, **kw):
tk.Frame.__init__(self, master=master,**kw)
button = tk.Button(self, text="Select directory",
command=self.select_dir)
button.grid(row=0,column=1)
self.act_dir = tk.StringVar()
self.act_dir.set("/home/")
self.pathBox = tk.Entry(self, textvariable=self.act_dir,width=50)
self.pathBox.grid(row=0,column=0)
def select_dir(self):
selectdir = SelectDir("Select directory", "/home/user/folder/")
self.act_dir.set(selectdir)
def get_dir(self):
return self.act_dir.get()
def SelectDir(title,initial):
result = filedialog.askdirectory(initialdir=initial,title=title)
return result
if __name__ == '__main__':
root = tk.Tk()
MainWindow(root).grid()
root.mainloop()
You can now use the AskDir class as many times as you want in the MainWindow (perhaps to select Source and Destination paths). This has been added to my example with the doStuff method inside MainWindow.