I have a question about MVC pattern, concerning the controller part.

Most of the program I happen to write are basically spreadsheet like. They are written in C/GTK+ and Python3/Qt5/Tkinter but share a common design : I separate the gui from the logic (data struct and heavy lifting methods) that seems to be the MVC pattern.

Question Do I really need to pass data from view to model each time I edit the GUI data and want to compute something ? Then pass the new data back from model to view ? Is that how it should be done ? I understand this is fine for a small GUI that does heavy computation in the model. But what about a big GUI, like a big table or a spreadsheet.

Answer Here are the elements that I took from the discussion with @dcorking: You have to update the data yourself. One shortcut is to update on a per-case basis, only what needs to be updated and don't touch the rest. Obviously, if you are sorting the collection after a single change, you may need to re-update the whole GUI eitherway. "Observers" have been mentionned that sounds like an automated way to do as above.

Pseudo/Python Code

Let's consider a basic collection of items (shopping list, book library, ...). My main class Collection contains a list (dozens or hundreds) of Item and methods to be applied on the list.

#The model package

class Collection():
    def __init__(self, name=""):
        self.name = name
        self.items = []
        self.itmesToUpdate = []

    def sort(self):
        self.items.sort(key=lambda x:float(x.price))
        self.itemsToUpdate = []
        self.itemsToUpdate = listItemsThatWereMovedAround()

class Item():
    def __init__(self, name="", price=0.):
        self.name = name
        self.price = price

Now the Gui part:

class Gui():
    #The View part.
    def __init__(self):
        self.collection = model.Collection()
        ...then fill the View with widgets (Menus, EditTexts, Buttons, ...)

    #The Controller part starts here
    def setViewFromModel(self):
        for i in self.collection.itemsToUpdate:
            self.editTextName[i].setText(self.container.items[i].name)
            self.editTextPrice[i].setText(self.container.items[i].price)
        self.collection.itemsToUpdate = []# reset the list

    def setModelFromView(self):
        for i in self.collection.itemsToUpdate:
            self.container.items[i].name = self.editTextName[i].getText()
            self.container.items[i].price = self.editTextPrice[i].getText()

    #CALLBACKS
    def onItemModified(self, index):
        self.collection.itemsToUpdate.append(index)

    def onClickSort(self):
        self.setModelFromView()#Read the GUI data
        self.collection.sort()#Compute in Model
        self.setViewFromModel()#Update the View with new data

Is this design fine ? For spreadsheet-like app ? Thank you.

  • That does not look like MVC. In MVC the user actions are passed to the controller. – Goyo Mar 1 at 18:27
  • @Goyo I updated my post to explicitly show where I think my controller part is. – funkygoby Mar 3 at 10:26
up vote 2 down vote accepted

MVC does not tell you how small to make the models.

For a spreadsheet, when you find that passing the whole data structure between a View and a Model is too slow, or makes the code too complex, you could make an MVC triad to represent something as small as a single cell.

The model part of the cell could then connect in some appropriate way to a larger business object that represents the entire sheet.

Similarly, the cell views will be embedded in another view to represent a sheet, or a window onto a sheet.

Update

The main aim of MVC is not to improve performance, but to make the code more understandable, and therefore easier to maintain.

If you make an MVC triad for each individual cell, this should be easier to understand and modify than if the triad represents a whole column, or the entire sheet. However, performance may suffer for changes that affect the entire sheet, as you may end up with many small objects sending messages to each other.

One alternative to MVC that you might also want to consider if you want to avoid passing data between Views and Models is direct manipulation. In direct manipulation, the object the user interacts with is the object that stores the business data (the cell value) and the business rules (how the cell value can change.)

  • That would allow me to pass data only for concerned cells right ? But I believe I could do that in my current design too, by using a cell-specific flag that gets switched on when the user interact with the cell, then only proceed with flagged cells ? One last thing, would your "per cell" MVC improve performance if I need to update every cells (after a sort for example)? Thank you. – funkygoby Mar 11 at 15:32
  • It sounds like your first comment is an attempt to answer your own question, which is a good thing: please go ahead and post your own answer. I have tried to reply to your second comment in an update. – dcorking Mar 12 at 13:11
  • In the model, I added a itemsToUpdate list. When an item is modified in the gui (onItemModified), I append this item's index to the list. When the data is passed to the model (setModelFromView), only the flagged items are concerned. The model process the data and, just like the Gui, reset the itemsToUpdate list with only the items that were modified. When finally setViewFromModel is called, only the items which index are in itemsToUpdate will be refreshed in the Gui. Is that a reasonnable approach? In the case of a "sort" function does that make sense since all data may be moved around? – funkygoby Apr 1 at 13:32
  • I don't know, though that is possibly material for a new question. On the other hand, if you want to save some work you can use a GUI framework with Observers to handle some of the housekeeping of updates (models and views can subscribe to each other as observers.) – dcorking Apr 4 at 10:44

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.