A simple application¶
This section describes the process of creation of a sample application, from the design with Glade, to the integration of views and code inside the MVC-O Infrastructure.
We want to design and implement a simple application constituted by only one window, containing two string labels. One label shows a text, while the other shows the number of characters displayed (i.e. the length of the string) by the first one. There is also a button the user can press. By pressing the button, the user can change the displayed text, and of course this action might change also the displayed text length accordingly. Figure The sample Application gives an idea on how the application should appear.
Glade¶
Figure Designing the example by means of Glade for GTK2 shows Glade and a project named example
.
The sample GUI has only one top-level window (named
window1
).
The Widget Tree Window shows the widgets hierarchy. There are essentially the three main components (one button and two labels), grouped inside a set of containers, which supplies alignments and resizing capabilities.
On the right side of Figure Designing the example by means of Glade for GTK2, the Properties Window
shows that the widget named button1
has signal
clicked
associated with function
on_button1_clicked
. This means that the Controller will
have to supply this function in order to handle the click
event occurring in button1
.
Implementation¶
The implementation is slightly elaborate for this example, because the goal here is to show how the sample application can be implemented by using the MVC-O Infrastructure.
A basic knowledge of any Object Oriented programming language is sufficient to understand how this example has been pushed inside the MVC-O framework. On the contrary, a fair knowledge of the Python language is required in order to understand the code details.
More description section is Details of implementation.
Model¶
Class ExampleModel
is as simple as class
ExampleView
. As for ExampleView
, it extends a
base class of the MVC-O Infrastructure, class Model
. The
state is represented by a set of possible messages, as well as by the
current message index. The current message index is also an
observable property. A couple of methods are supplied in order to
access the state.
# file model.py
from gtkmvc import Model
class ExampleModel (Model):
"""The model contains a set of messages
and an observable property that represent the current message
index"""
# Observable property: code for that is automatically generated
# by metaclass constructor. The controller will be the observer
# for this property
message_index = -1 # -1 is the initial value
__observables__ = ("message_index",)
def __init__(self):
Model.__init__(self)
self.messages= ("I am patient with stupidity",
"but not with those",
"who are proud of it.",
"(Edith Sitwell)",
)
return
def get_message(self, index): return self.messages[index]
def set_next_message(self):
# this changes the observable property:
self.message_index = (self.message_index + 1) % len(self.messages)
return
pass # end of class
Notice that class instance members are declared to be observable
through the special class variable __observables__
,
which is a list of names (string) of the properties that are
observable.
The base class Model belongs to a
meta-class which automatically searches for observable properties and
generates the needed code to handle the notification. When the value
of variable message_index
changes, all registered
observers will be notified.
View¶
In the example, the View is implemented inside the class
ExampleView
shown below.
# file view.py
import os.path
from gtkmvc import View
GLADE_PATH = "./"
class ExampleView (View):
"""The application view. Contains only the main window1 tree."""
builder = os.path.join(GLADE_PATH, "example.glade")
top = "window1"
def set_msg(self, msg):
self['label_text'].set_text(msg)
self['label_text_len'].set_text(str(len(msg)))
return
pass # end of class
Class ExampleView
extends the generic View
class, which performs most of the job, as described above.
Class members builder
and top
are used instead of
calling View
constructor directly.
Controller¶
Class ExampleController
contains the GUI logic of the
application. The controller handles two signals and the observable
property notification. Signals are the destroy
event,
invoked when the application quits, and the
on_button1_clicked
, fired when button1
is
pressed.
# file ctrl.py
from gtk import main_quit
from gtkmvc import Controller
class ExampleController(Controller):
"""The only one controller. Handles the button clicked signal, and
notifications about one observable property."""
def register_view(self, view):
# Connects the exiting signal:
view.get_top_widget().connect("destroy", main_quit)
return
# Signal
def on_button1_clicked(self, button):
"""Handles the signal clicked for button1. Changes the model."""
self.model.set_next_message()
return
# Observables notifications
@Controller.observe("message_index", assign=True)
def value_change(self, model, name, info):
"""The model is changed and the view must be updated"""
msg = self.model.get_message(info.new)
self.view.set_msg(msg)
return
pass # end of class
The destroy
signal is connected when the View registers itself
inside the controller, by using the method override of
register_view
. Method on_button1_clicked
calls a method
inside the model which changes a part of the state inside the
model. Since that part of the state is an observable property, the
associated observer (which is the controller itself) is notified of
the modification, by calling method value_change
. This method
updates the view connected to the controller.
The Launcher (main)¶
The main()
code creates a (m,v,c) triple and launches
gtk.main()
. All the rest is cosmetics.
import gtk
from model import ExampleModel
from ctrl import ExampleController
from view import ExampleView
import gtkmvc
def check_requirements():
gtkmvc.require("1.99.1")
return
def setup_env(development_state=False):
# This is how developers should set gtkmvc logging level (by
# default debugging info is not shown):
if development_state:
import logging
logging.getLogger("gtkmvc").setLevel(logging.DEBUG)
pass
return
def main():
m = ExampleModel()
v = ExampleView()
c = ExampleController(m, v)
gtk.main()
return
if __name__ == "__main__":
check_requirements()
setup_env(development_state=True)
main()
pass