Custom renderer for configuring rows in lists or on pages
Product line
Standard
|Expert
Operating mode
CLOUD ABO
|ON-PREMISES
Modules
Services & CRM
Budget & Phases
Purchases
Resource Planning
Business Intelligence
You can use custom renderers to configure the display and calculation of the underlying data of list rows and fields on pages for yourself.
In contrast to the built-in renderers , which, if used in the column configuration of list settings, apply a predefined and unchangeable operation to the displayed values, custom renderers can be used to define custom operations, which are applied to the display.
The custom renderers are created in Python . A Python class as a custom renderer can define the following attributes and methods:
ValueExpression
.Attributes | Method | Description |
---|---|---|
value |
get_value(self, rowobj, expression, subscriber) |
This is the actual custom renderer method. It specifies what is shown in the row. A custom renderer without def get_value(self, rowobj, expression, subscriber): return self.evalocl(expression, rowobj) |
set_value(self, rowobj, expression, value) |
If the row is writable, the value entered by the user is written using this method. def set_value(self, rowobj, expression, value): setattr(rowobj, expression, value) You must make sure that the given value is of the desired type. By default, Important note: No dialogs or other code with breakpoints after changing values which trigger a subscription in a get method: A set method changes data. If this triggers a subscription, the list is recalculated. All existing renderers are also reloaded. If the set method shows a dialog or stops the code in any other way, the modification process is technically completed, and the subscription recalculates the list. This also removes the renderer showing the dialog, which results in an error: |
|
value_type |
get_value_type(self) |
Allows you to specify the expected data type for the custom renderer. The The following values are allowed:
Example of application: A custom renderer works with hourly rates (currency). In order for the class Ansatz: value_type = "currency" def get_value(self, rowobj, expression, subscriber): return self.evalocl(expression, rowobj) def set_value(self, rowobj, expression, value): setattr(rowobj, expression, value) Important note:
|
display_type |
get_display_type(self) The |
Permitted values are:
class Aktiv: display_type = 'checkbox' value_type = 'boolean' def get_value(self, rowobj, expression, subscriber): return self.evalocl('aktiv', rowobj) def set_value(self, rowobj, expression, value): rowobj.aktiv = value
class CopyProjectRenderer: display_type = "button" def get_value(self, rowobj, expression, subscriber): return "Projekt kopieren" def get_buttoniconid(self, rowobj, expression, subscriber): return self.evalocl('iconIndex', rowobj) def button_clicked(self, rowobj, expression): scripttext = self.evalocl("scripteintrag->select(bezeichnung='Projekt kopieren')->first.scripttext") return vtcapp.scriptexecute(scripttext, rowobj) |
buttoniconid |
get_buttoniconid(self, rowobj, expression, subscriber) |
display_type = "button" def get_buttoniconid(self, rowobj, expression, subscriber): return self.evalocl('iconIndex', rowobj) If no icon
is specified or if the icon is not present, then the If an empty string ““ is explicitly specified, the button for this row is hidden. |
button_clicked(self, rowobj, expression) |
display_type = "button" def button_clicked(self, rowobj, expression): return vtcapp.msgbox("Hello World") Important note: No dialogs or other code with breakpoints after changing values that trigger a subscription in a get method: If data is changed in a button_clicked method, subscription can be triggered and the list recalculated. This will also reload all existing renderers. If you now display a dialog after changing the data or stop the code in any other way, the modification process is technically completed and the subscription recalculates the list. This also removes the renderer showing the dialog, which results in an error: |
|
buttontooltip |
get_buttontooltip(self, rowobj, expression, subscriber) |
With the class simpleBtn: display_type='button' buttoniconid='flash' buttontooltip='Hier ein Tipp' class complexBtn: display_type='button' buttoniconid='shark_fin' def get_buttontooltip(self,rowObject, expression, subscriber): return self.evalocl("bemerkung", rowObject) |
converter |
get_converter(self) The |
The following converters can be used:
class Minutes: value_type = "integer" converter = "minutes" def get_value(self, rowobj, expression, subscriber): return rowobj.evalocl(expression) def set_value(self, rowobj, expression, value): setattr(rowobj, expression, value) |
is_readonly |
get_is_readonly(self, rowobj, expression, subscriber) |
This can be used to control whether or not a row is read-only. Possible values: |
is_cascadedset |
get_is_cascadedset(self, rowobj, expression, subscriber) |
More experienced Vertec users are familiar with the behavior that the font turns green when you overwrite certain values and is black when it is a calculated value, for example in project budgeting . In this way, you can see at first glance on the interface whether a value has been set on the object itself (i.e. a default has been overwritten) or whether it is the default value. This can be replicated with
Possible values: |
text_color |
get_text_color(self, rowobj, expression, subscriber) |
This allows you to control the font color. Valid are all values of the Vertec color palette . Default: The Vertec default applies. |
background_color |
get_background_color(self, rowobj, expression, subscriber) |
This allows you to control the background color of the row. All values in the Vertec color palette are valid. Default: The Vertec default applies. |
The object instance ( self
) offers the following attributes and methods:
self.evalocl(expression, rootobj=None) |
Allows OCL expressions to be evaluated on the object.
The OCL evaluator provides a variable self.evalocl("varRowObject") |
self.container |
Provides access to the container business object of the list view or page. |
self.context |
Available from Vertec 6.6. Refers to the context object. The context object is the container (folder or link container) for normal lists. For Resource planning views , the context object can also be a single user entry. |
self.controller |
Available from Vertec 6.6. Used with list controllers
which refer to the list controller object: If a column in a list with a list controller uses a custom renderer, then the custom renderer gets a reference to the list controller under self.controller. This is the same |
Note: Custom renderers can be used with restrict scripting , but the Python code itself is subject to the usual limitations.
Subscribe means that you tell a certain member that you want to be notified when things change. You subscribe to that member’s notification list.
That is why a subscriber is supplied as a parameter with each get
method so that the displayed values can be updated when the data changes.
The subscriptions are set automatically when the values in the renderer are calculated via self.evalocl()
:
self.evalocl(expression, object)
rowobj
.This means that the values are automatically correctly subscribed and the get
method is called again when the underlying data changes.
Important: self.evalocl()
must be used, evalocl()
on an object is not enough. Therefore, self.evalocl("eintraege", rowobj)
must be used instead of
self.evalocl("eintraege", rowobj)
.
def get_value(self, rowobj, expression, subscriber): return self.evalocl(expression, rowobj)
If, for some reason, this is not possible and the value is determined via Python code, you can use the subscriber
parameter to set this subscription manually. Python methods exist for this
subscribetomember(membername, subscriber)
on all Vertec objects.
Example with OCL and without subscriber | Example with Python and with subscriber |
---|---|
def get_value(self, rowobj, expression, subscriber): return self.evalocl("code", rowobj) |
def get_value(self, rowobj, expression, subscriber): rowobj.subscribetomember("code", subscriber) return rowobj.code |
It is important that you subscribe to all the members about which you want to be notified when they change (see description above).
A manually set subscription is always set only on the corresponding member so it does not chain. Example, starting from a service:
projektleiter = self.evaolocl("projekt.projektleiter", leistung)
would have to be split with subscriptions via the Python method as follows:
projekt = leistung.projekt leistung.subscribetomember("projekt", subscriber) if projekt: projektleiter = projekt.projektleiter projekt.subscribetomember("projektleiter", subscriber) else: projektleiter = None
This means that errors are quickly built in, and there are also other disadvantages:
Therefore, we recommend that you always use self.evalocl(expression, rowobj)
.
The custom renderer is created as a Python script . A relevant class is defined for each custom renderer, in which the individual attributes or methods are then defined.
You can call up the individual renderer via <Designation of script>.<Name of Python class in script>
. Therefore, we recommend you assign meaningful names here.
Below is an example of a complete renderer:
class BemerkungProjectCustomRenderer: def get_value(self, rowobj, expression, subscriber): return self.evalocl("bemerkung", rowobj) def set_value(self, rowobj, expression, value): rowobj.bemerkung = value def get_is_readonly(self, rowobj, expression, subscriber): return False def get_is_cascadedset(self, rowobj, expression, subscriber): return False def get_value_type(self): return 'string' def get_text_color(self, rowobj, expression, subscriber): return 'clBlack' def get_background_color(self, rowobj, expression, subscriber): return 'clWhite'
Or, via definition of variables:
class Compact: value = '120' converter = 'minutes' is_readonly = True value_type = 'integer' background_color = 'clLightYellow' text_color = 'clDarkBlue'
Important: The value value
or the method get_value()
must always be defined, otherwise the row or field does not show anything.
In the relevant list column, the renderer is specified as follows:
<Designation of script>.<Name of Python class in script>
Important: After adjusting the renderer, the list must be reloaded for the changes to take effect. The easiest way to do this is to click away and back.
When customizing pages, the attribute Renderer
can be set in XML. The convention is the same as in the list settings
<Designation of script>.<Name of Python class in script>
As soon as a custom renderer is set, everything is looped over that renderer. A custom renderer must always have a get_value()
method, otherwise nothing is shown in the list. The expression (or ValueExpression) is transferred to the renderer, but is ignored for display.
You can use custom renderers to enter key values on user entries directly in the list. The key is created with the first entry. For the display, the value is read directly from the key. The structure is as follows:
In the list settings, the name of the key is written as an expression. You must make sure that the term is written in lowercase. When leaving the field, the message appears that the OCL expression is invalid.
You can reply to this notification with No
. This does not cause problems if the relevant key is set as the renderer.
Important: The error message MUST appear. Otherwise, you might accidentally use an existing member or name the key the same as an existing member, which leads to an invalid state!
With the following code, the key with the specified name (as written in expression
, in our example amount
) is set:
class KeyCurr: value_type = 'currency' def get_value(self, rowobj, expression, subscriber): return self.evalocl("keycurr('{}')".format(expression), rowobj) def set_value(self, rowobj, expression, value): if str(value): rowobj.setkeyvalue(expression, value)
Hint: You can use this code for any key of type currency.
Using a custom renderer on a page:
In the page settings
, the name of the key is written in the ValueExpression
.
<Page Override="InvoiceFurtherInfo"> <TextBox Name="KeyBetrag" Renderer="CustomRendererClasses.KeyCurr" Label="Betrag" ContentAlignment="Right" ValueExpression="amount" WidthFraction="0.5" PlaceAfter="ReminderLevel" /> </Page>
You can use custom renderers to set tags on user entries directly in the list . The tag is shown by a checkbox and can be set or removed.
The relevant renderer is as follows:
class SetTag: display_type = 'checkbox' value_type = 'boolean' def get_value(self, rowobj, expression, subscriber): return self.evalocl("hasTag('{}')".format(expression), rowobj) def set_value(self, rowobj, expression, value): if value: rowobj.addtag(expression) else: rowobj.removetag(expression)
The name of the relevant tag (without quotation marks) is specified as Expression
in the list settings
or as ValueExpression
in the page settings
.