Skip to content
Advertisement

Python MWC pattern GUI – Menubar command from controller class

I’m trying to build a tool for data analysis. I expect that this kind of application gets big, so I’m following the MVC pattern to organize it as best as possible.

My results are saved in .csv files, so the goal should be to “import” them into the GUI. I want to open these files using “CTRL + O” as keybinding and of course using the corresponding option in the menubar.

Now to the actual problem: The function which is called when hitting “CTRL + O” is in the Controller class and works as expected, I can open a bunch of files and the list saves each name. But when using the menubar I’m stucked how to implement the “command=” option.

This is my code:

import tkinter as tk
from tkinter.filedialog import askopenfilename
from tkinter.constants import ANCHOR, TRUE
from tkinter import Label, filedialog
from tkinter import ttk


class Model():
    # more to come
    pass

class View:

    def __init__(self, view):

        self.view = view
        self.view.title("analyzer")
        self.view.geometry("640x480")
        self.view.resizable(False, False)

        # menubar
        self.menubar = tk.Menu(self.view)
        self.view.config(menu=self.menubar)
        self.filemenu = tk.Menu(self.menubar, tearoff=False)

        self.menubar.add_cascade(label="File", menu=self.filemenu)
        
        self.filemenu.add_command(label="Open", accelerator="Ctrl+O", command=Controller.get_open_filename())
        self.filemenu.add_separator()
        self.filemenu.add_command(label="Remove", accelerator="Ctrl+R")
        self.filemenu.add_command(label="Information", accelerator="Ctrl+I")
        self.filemenu.add_separator()
        self.filemenu.add_command(label="Exit", accelerator="Ctrl+E", command=self.view.quit)


class Controller:

    def __init__(self):
        self.root = tk.Tk()
        self.view = View(self.root)

        # keybindings / shortcuts
        self.root.bind_all("<Control-e>", lambda event: self.root.quit())
        self.root.bind_all("<Control-o>", lambda event: self.get_open_filename())

        self.list_of_files = []
       
        
    def run(self):
        self.root.mainloop()


    def get_open_filename(self):
        self.filename = askopenfilename(title="Select data file", filetypes=(("csv files", "*.csv"), ("all files", "*.*")))
        self.list_of_files.append(self.filename)


        print(self.list_of_files)


if __name__ == "__main__":
    c = Controller()
    c.run()

I would really appreciate If some could give me a hint, want I’m doing wrong. Thanks!

Advertisement

Answer

You are trying to call a Controller function from a View Object, but View doesn’t know that Controller has that function. It doesn’t know that Controller exist.

There might be a better way than this, but you can pass a function as a parameter to View’s constructor. By passing the get_open_filename() function as a parameter to View’s constructor you can use that as the command.

NOTE: I called the parameter func so you can see what I’m doing. I’d recommend giving it a better name though.

import tkinter as tk
from tkinter.filedialog import askopenfilename
from tkinter.constants import ANCHOR, TRUE
from tkinter import Label, filedialog
from tkinter import ttk


class Model():
    # more to come
    pass

class View:

    def __init__(self, view, func):

        self.view = view
        self.view.title("analyzer")
        self.view.geometry("640x480")
        self.view.resizable(False, False)

        # menubar
        self.menubar = tk.Menu(self.view)
        self.view.config(menu=self.menubar)
        self.filemenu = tk.Menu(self.menubar, tearoff=False)

        self.menubar.add_cascade(label="File", menu=self.filemenu)
        
        self.filemenu.add_command(label="Open", accelerator="Ctrl+O", command=func)
        self.filemenu.add_separator()
        self.filemenu.add_command(label="Remove", accelerator="Ctrl+R")
        self.filemenu.add_command(label="Information", accelerator="Ctrl+I")
        self.filemenu.add_separator()
        self.filemenu.add_command(label="Exit", accelerator="Ctrl+E", command=self.view.quit)


class Controller:

    def __init__(self):
        self.root = tk.Tk()
        self.view = View(self.root, lambda: self.get_open_filename())

        # keybindings / shortcuts
        self.root.bind_all("<Control-e>", lambda event: self.root.quit())
        self.root.bind_all("<Control-o>", lambda event: self.get_open_filename())

        self.list_of_files = []
       
        
    def run(self):
        self.root.mainloop()


    def get_open_filename(self):
        self.filename = askopenfilename(title="Select data file", filetypes=(("csv files", "*.csv"), ("all files", "*.*")))
        self.list_of_files.append(self.filename)


        print(self.list_of_files)


if __name__ == "__main__":
    c = Controller()
    c.run()
User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement