Project

General

Profile

How to write a Brick

Overview of a Brick

A brick is a part of a GUI, generally used to display information about a piece of equipment, and/or allow the user to interact with it.

Architecture

A brick is a class inheriting from Framework4.GUI.Core.BaseBrick.

The Brick must also have an ''init'' method, with no arguments. This method is called by the framework after the Brick has been instantiated.

The Brick class may also have a 'properties' variable. See below.

On the GUI (Qt) side of things, each Framework4.GUI.Core.BaseBrick object has a brick_widget variable. This is the base QWidget you should add your GUI elements to.

Brick metadata

Bricks usually have some metadata declared at the module level. Three metadata are used to specify the brick's author, version and category.

The metadata is declared using 3 module-level special variables: __author__, __version__ and __category__. See below for an example.

The __author__ and __version__ variables are self-explanatory. The former is usually a string while the latter is a number, though you may use whatever type you want for now, as the Framework doesn't use these variables; they are only there for documentation.

The __category__ variable is used by the GUI Builder. In the GUI Builder, bricks are sorted by category in a toolbox. The bricks lacking a __category__ are placed in the General tab of this toolbox.

Brick properties

Each Brick may have properties, which can be configured at GUI creation time, in the property editor.

The properties are declared in a class variable. The following declaration is taken, as usual, from the Shutter brick:

properties = { "test": Property("file", "Test", "File, just for a test", "file_selected"),
               "icons": PropertyGroup("Icons", "Select icons for different elements", "set_icons",
                                      { "opened": Property("image", "Opened"),
                                        "closed": Property("image", "Closed") })

Like connection definitions, it is a dictionary object, indexed by property name. The values can be either Property instances, or PropertyGroup instances. Property constructors take the following arguments:
  • type: one of "integer", "float", "boolean", "combo" or "image"
  • label: displayed in the property editor
  • description: like the label
  • onchange_cb: the name of a method (a string) to be called whenever the property changes. It will be called with the new property value as an argument
  • default: the default value of the property
  • choices: a python list of acceptable values, used when the property type is a combo
PropertyGroup constructors take the following arguments:
  • label: same as Property
  • description: same as Property
  • onchange_cb: same as Property
  • properties: a python list of Property instances, member of this PropertyGroup

Propertygroups allow to group properties together, and are displayed as an expandable element in the property editor.

Connection definitions

Connections are declared in the Brick's code. For example (taken from the Shutter Brick):

connections = { "shutter": Connection("Shutter object",
                                      [ Signal("stateChanged", "shutter_state_changed") ],
                                      [ Slot("open"), Slot("close") ],
                                      "shutter_connection_status") }

connections is a class variable. It is a dictionary object, containing one or more connection definition, indexed by the connection's name.

Connections are instances of the Connection class. The constructor takes 4 or 5 arguments:

  • A mnemonic, in this case Shutter object
  • A list of Signal instances, with the corresponding method to call when a signal is received
  • A list of Slot instances
  • A connection callback, the name of a method which will be called with either True or False as an argument depending on whether the connection has been established or lost
  • An optional low-level connection callback, with the same semantics as the connection callback, but being called earlier the process of establishing a connection. Typically used with object groups

The Connection, Signal and Slot classes are located in the Framework4.GUI.Core package.

Particularities when using object groups

When using object groups, the group contains Pyro proxies to the control objects. Since the connections cannot be made using the normal mechanism (the connections definition), they must be established manually. This is done using the Core.BaseBrick connectObject method. It is used as follow:

self.connectObject(pyro_proxy, 'signal_name', callback_for_signal) 

Commented Brick code example

# FOr the BaseBrick class we have to inherit from
from Framework4.GUI import Core
# Other Framework stuff
from Framework4.GUI.Core import Property, PropertyGroup, Connection, Signal, Slot

from PyQt4 import Qt
import os

__author__ = "Matias Guijarro" 
__version__ = 1.0
__category__ = "General" 

# Bricks classes need to inherit from the BaseBrick class
class ShutterBrick(Core.BaseBrick):

    # Properties class variable, a dictionnary of all properties
    # See the doc on the wiki
    properties = { "test": Property("file", "Test", "File, just for a test", "file_selected"),
                   "icons": PropertyGroup("Icons", "Select icons for different elements", "set_icons",
                                          { "opened": Property("image", "Opened"),
                                            "closed": Property("image", "Closed") })
                   }

    # connections class variable, contains the connection definitions
    # see doc on the wiki
    connections = { "shutter": Connection("Shutter object",
                                          [ Signal("stateChanged", "shutter_state_changed") ],
                                          [ Slot("open"), Slot("close") ],
                                          "shutter_connection_status") }

    shutterState = {
          'unknown': 'gray',
          'closed': '#ff00ff',
          'opened': '#00ff00',
          'moving': '#663300',
          'automatic': '#009900',
          'fault': '#990000',
          'disabled': '#ec3cdd',
          'error': '#990000'
    }

    # This init method is called by the Framework. This is where you
    # should setup your GUI, adding your GUI elements to the brick's
    # brick_widget (which is a QWidget instance.)
    def init(self):
        Qt.QVBoxLayout(self.brick_widget)
        self.shutter_state = Qt.QLabel("unknown", self.brick_widget)
        self.shutter_state.setAlignment(Qt.Qt.AlignHCenter)
        self.shutter_cmd = Qt.QPushButton("open/close", self.brick_widget)
        Qt.QObject.connect(self.shutter_cmd, Qt.SIGNAL("clicked()"), self.shutter_cmd_clicked)
        self.brick_widget.layout().addWidget(self.shutter_state)
        self.brick_widget.layout().addWidget(self.shutter_cmd)

    # The following 2 methods are callbacks, called when their
    # corresponding property is changed. They get the new value
    # of the property as an argument. These callbacks are setup
    # in the properties variable above.
    def set_icons(self, icons):
        print "NEW ICONS =", icons

    def file_selected(self, filename):
        print "FILE SELECTED =", filename

    # This is a callback called when the Control Object emits its
    # stateChanged signal. This callback is setup in the connection
    # definition above.
    def shutter_state_changed(self, state):
        print '**** SHUTTER STATE CHANGED TO', state
        if state in ("opened", "automatic"):
            self.shutter_cmd.setText("close")
            self.shutter_cmd.setEnabled(True)
        elif state == "closed":
            self.shutter_cmd.setText("open")
            self.shutter_cmd.setEnabled(True)
        else:
            self.shutter_cmd.setText("")
            self.shutter_cmd.setEnabled(False)

        self.shutter_state.setStyleSheet("* { background-color:%s }" % ShutterBrick.shutterState[state])
        self.shutter_state.setText(state)

    # This method is a connection callback, called when we connect to
    # or disconnect from the shutter object. The connected argument is
    # True or False depending on the situation. This callback is setup
    # in the connection definition above.
    def shutter_connection_status(self, connected):
        print "SHUTTER CONNECTION STATUS CALLED", connected
        self.brick_widget.setEnabled(connected)

    # In this method you can see how we can interact with the Control
    # Object. We use the getObject method (inherited) to get a proxy
    # to the Control Object. The method argument is a name used to
    # define a connection in the connection definitions dictionnary
    # above. We can use the proxy to call methods exposed by the Control
    # Object.
    def shutter_cmd_clicked(self):
        print 'Shutter magic button clicked'
        if self.shutter_cmd.text()=="open":
            self.getObject('shutter').open()
        else:
            self.getObject('shutter').close()