PCE contains a large number of classes related to creating and manipulating graphical objects. The graphics facilities were designed for interactively manipulating diagrams and graphical representations of information and knowledge. The graphics facilities are not suitable for 2-D or 3-D modeling of real-life objects (such as cars and planes).
The most commonly used subclasses of class graphical are:
class box Rectangle (filled, rounded) class ellipse class line class path Multiple segment line (curved) class bitmap Image class text Short text objects class device Compound graphical objects class dialog_item Specialised items for dialogue
Example types of diagrams for which PCE has been used are the representation of graphs (hierarchies, lattices, flow-charts), tabular information, iconic representations of computer networks, knowledge bases and various graphical editors.
A graphical describes an image, all of whose affected pixels are
within a rectangular area, represented by graphical<-area.
The main role of class graphical
to facilitate the communication to its graphical<-device.
A
device object is a
collection of graphical
objects. Class device
is a subclass of class graphical.
This allows us to display devices on devices, leading to nested
graphicals with local coordinate systems.
Graphics in PCE is more complicated than most of the other facilities, although the maxim “if your needs are simple, it will be simple in PCE as well” still applies. Advanced use of graphics, however, requires a good understanding of the basic concepts and how they are applied.
Mouse and keyboard events are made available to a graphical using the
method graphical->event.
By default the primitive graphical classes do not respond to this
message. There are two ways to make a graphical sensitive to the mouse
and/or keyboard. The first is to associate a
recogniser object
using object->recogniser. The second is to
redefine the graphical->event
method.
A couple of methods are commonly redefined on class graphical for creating customised graphical objects:
->event:
event May be redefined to modify event processing. This is a class-level
alternative for graphical->recogniser.
->device:
device Called if the device of the graphical
object is changed. May be redefined to take care of side-effects, but
the user must call the super-behaviour.
->displayed:
bool Called if the graphical<-displayed
attribute is changed. Like
graphical->device,
this method may be redefined, but the user
->reparent:
int Indicates that the consists-of tree of graphicals has changed
between this graphical and the window. May be redefined, but the user must
call the super-behaviour.
->in_event_area
and graphical->in_event_area.->paint_selected
draws the selection indication, provided graphical<-selected
is @on and the graphical<-window’s
window<-selection_feedback
equals handles. Values:
->paint_selected).
->flash
@see window->flash
@see graphical->alert<-class_variable_value:
inactive_colour. The method graphical->event
will simply fail if graphical<-active
is @off.
below the mouse. The
get method first invokes graphical->compute
to update the graphical.
Defaults: The default area of most graphicals is area(0,0,0,0).
|pixmap]<->colour
attribute of a graphical describes the colour used to draw the affected
pixels. It is either a colour
object or @default.
The latter implies the graphical<->colour
attribute of the device is used.
Defaults: @default.
For windows, the default colour is display<-foreground.
Defaults: @nil.
<->device
slot is used by graphicals to inform their device of changes, so the
device can perform the appropriate redraw operations. The following is
always true:
If the device of a graphical is not @nil, the graphical is member of `Device <-graphicals` of this device and visa-versa.
Normally, a graphical is assigned a device using device->display.
->display
an @off by device->erase.
graphical->displayed
is used to remove a graphical temporary from the display. See also figure->status.
Defaults: Initially @off.
Set to @on by device->display.
Handles can be attached at the instance level and at the class level
(see class->handles)
or both.
Defaults: @nil (no handles at the instance level)
<->area
is inverted after the graphical is painted, inverting all pixels in the
bounding-box of the graphical. Note that this implies at inverted circle
appears as a white circle in a black square (using white background and
black foreground colours). Also note that graphicals below (i.e.
hidden by this graphical) are inverted too.
Defaults: @off
<-name.
See device<-member
and device<-catch_all.
Defaults: 1
When no argument is specified with graphical->request_compute
the variable will be filled with @on.
If a graphical->request_compute
is invoked on a graphical, PCE's graphical kernel will invoke a graphical->compute
on it if the graphical needs to be repainted or one of the dimensions of
the graphical is requested. See graphical<-area,
display->dispatch
and graphical->compute.
.selection_style.
none Straight line dotted Pattern: 1 on 2 off ... dashed Pattern: 7 on 7 off ... dashdot Pattern: 7 on 3 off 1 on 7 off ... dashdotted Pattern: 11 on 3 off 1 on 3 off 1 on 3 off 1 on 3 off longdash Pattern: 15 on 7 off ...
<-area,
the bounding box of the pixels affected by this graphical.
This method is called by the graphical infra-structure of XPCE and
should never be called directly. See also graphical->draw
and
graphical->compute.
This method may be redefined to create custom graphical
objects. It is not obligatory for the redefinition to call the method of
the super-class. The definition should not apply paint outside the graphical<-area
of the graphical. If there is no simple way to avoid this, use graphical->clip
and graphical->unclip
to ensure this.
The definition can use graphical->draw
to paint other graphical
objects or one or more of the primitive drawing operations listed below:
graphical ->draw_arcDraw ellipse-part or pie-part graphical ->draw_boxDraw rectangle graphical ->draw_fillFill/clear a rectangle graphical ->draw_imageDraw an image object graphical ->draw_lineDraw a line segment graphical ->draw_polyDraw a polygon graphical ->draw_textDraw a string
The pen, colours, etc. may be manipulated using the following methods:
graphical ->save_graphics_stateSave current settings graphical ->restore_graphics_stateRestore old values graphical ->graphics_stateModify graphics state
The User Guide (Programming in PCE/Prolog) provides further documentation on redefining graphicals.
Another common approach to create custom graphics is by subclassing class device to define compound graphicals.
->advance’and redefined by
various sub-classes.->above
relation is stored using
object->attribute.
This method handles combinations of plain
graphical objects
and dialog_item
objects correctly.
See also graphical->above, dialog->layout
and
device->layout_dialog.
.visual_bell,
either
graphical->flash
or graphical->bell
are invoked.
Defaults: Depends on the resource graphical.visual_bell.
->attribute.
See dialog_item->alignment
and
dialog_item->reference
for details.<-message
of the dialog_item if the dialog_item has been modified by the user or
the argument is @on.
See also dialog->apply->set.
<-auto_align->set.
->clip.<-area
of the graphical. Note that on entry of graphical->_redraw_area
clipping is set to the argument area, which will normally be larger than graphical<-area.
Each graphical->clip
must be undone by an graphical->unclip
before graphical->_redraw_area
returns. See also graphical->_redraw_area, graphical->graphics_state
graphical->draw_line,
etc.
|pixmap]<-display_colour
@see device->foreground
@see class colour
->request_compute graphical->compute
mechanism is used by graphical
objects that need complex computations to determine their area and
appearance from some internal state.
For example, a menu is determined by its member menu_items and
several flags manipulating its visual appearance. It would be a waste to
recompute the entire layout of the menu on each change of its state as
there will often be multiple changes before it actually needs to be
repainted on the screen. A menu will therefore just issue
graphical->request_compute
on itself if recomputation is needed. PCE graphics kernel will cause a
message graphical->compute
to be send to the graphical just before the graphical->_redraw_area.
The definition of this method at the level of class graphical
just assigns graphical<-request_compute
to @nil.
graphical->connect
simply invokes link<-connection
passing the receiver as from, as well as to, from_kind
and to_kind. The normal way to redefine the type of
connection established is using either link->connection_class
or by redefining link<-connection.
anything. See
graphical<-connections
for a description of the arguments. The method
graphical<-connected
performs the same test, returning the connection
object found.
<-width or graphical<-height
of graphical<-contained_in
changed. Currently used only by class parbox.
See parbox->line_width->orientation’)
to the indicated position in the coordinate system of the graphical's
device.
This method used to support resizing graphicals: sending graphical->corner
messages to with the location of the device resizes the graphical.
Resizing is now dealt with by class resize_gesture.
->corner->corner->focus
@see window-displayed_cursor->free
all connections to- and from this graphical that match the arguments. @default
is taken to match anything. The arguments are described with graphical<-connections.
->display:
graphical’, which is the recommended way to display graphical
objects.
->geometry
rather then graphical->request_geometry.offset is
given, the coordinate system of the device is moved by the given offset
prior to painting the graphical. The optional argument is the changed
area of the device.
This method may be used to redefine graphical->_redraw_area.
It should not be called outside this context.
|image]*Fill defines the fill-pattern used. If @nil, the shape is not filled.
This method is part of the user-defined graphics infra-structure
described with graphical->_redraw_area
and should not be called outside this context.
|colour|elevation],
up=[bool]up
defines whether the box is painted elevated or lowered.
This method is part of the user-defined graphics infra-structure
described with graphical->_redraw_area
and should not be called outside this context.
|image]*To fill a rounded rectangle, use graphical->draw_box
with the pen set to 0.
This method is part of the user-defined graphics infra-structure
described with graphical->_redraw_area
and should not be called outside this context.
<-kind),
the on pixels are painted in the current foreground and the
off pixels in the current background. If transparent is @on,
the off pixels are not painted.
This method is part of the user-defined graphics infra-structure
described with graphical->_redraw_area
and should not be called outside this context.
->graphics_state).
This method is part of the user-defined graphics infra-structure
described with graphical->_redraw_area
and should not be called outside this context.
|vector,
closed=[bool], fill=[colour|image]*This method is part of the user-defined graphics infra-structure
described with graphical->_redraw_area
and should not be called outside this context.
.0 and
hadjust/vadjust center/center may be used to center text around a point.
Multiple lines are aligned according to hadjust (default: left).
The redraw-method below draws a text-box:
'_redraw_area'(TB, _Area:area) :->
"Redraw text-box"::
get_object(TB, area, area(X,Y,W,H)),
send(TB, draw_box, X, Y, W, H),
get(TB, font, Font),
get(TB, string, String),
send(TB, draw_text, String, Font,
X, Y, W, H, center, center).
This method is part of the user-defined graphics infra-structure
described with graphical->_redraw_area
and should not be called outside this context.
->post.
If the graphical has recogniser
objects associated (see‘object
graphical<-all_recognisers’)
and graphical<-active
equals @on,
the recognisers are activated in the order they appear in this chain
until one succeeds, after which this method returns successfully.
This method is redefined by many subclasses of class graphical to achieve predefined response to user-events. When programming user-defined (graphical) classes, this method is commonly used to attach predefined user-interface behaviour to the new class.
The following creates a box that may be moved using the middle button:
?- new(B, box(100,100)), send(B, recogniser, new(move_gesture)).
And the following example defines a class movable_box from which all instances can be moved:
:- pce_begin_class(movable_box, box).
:- pce_global(@movable_box_gesture,
new(move_gesture)).
event(B, Ev:event) :->
( send(B, send_super, event, Ev)
-> true
; send(@movable_box_gesture, event, Ev)
).
:- pce_end_class.
Diagnostics: Fails silently if there are no recognisers or none of the recognisers accepted the event.
See also graphical->hide, graphical->swap
and graphical->overlap.
Defaults: If the argument graphical is omited, the graphical will be moved to the top of the device's graphical stack.
Diagnostics: Always succeeds but has no effect if the argument graphical is not displayed on the same device.
<-area
of the graphical itself. Time is the time in milliseconds which the
graphical remains in the inverted time; the time of the total operation
is this time, plus two time the required time to invert the area. The
default time the graphical is inverted is determined by graphical.visual_bell_duration.
See also window->flash, device->flash
and tab->flash.
Normally one should invoke graphical->alert,
which depending on graphical.visual_bell
will either ring the bell or flash the graphical.
send(Window, focus, Graphical, Recogniser, Cursor, Button).
Focusing further user-events to this graphical object.
->focus_cursor
it the graphical is displayed in a window. Always succeeds. This will
modify the cursor of the window until the window's focus is changed.
Used internally to generate area_enter and area_exit events from other pointer-oriented events.
Diagnostics: Returns the value of event->post
->request_geometry,
which is, through
graphical->set,
invoked by all the methods that manipulate the geometry of graphical
objects. The parameters are the requested X-, Y-, W and H values for the
graphical. Values that need not be changed are passed as @default.
Sets the area and informs the graphical's device on the changed
geometry. The device will answer with a graphical->_redraw_area
when this is necessary.
This method is commonly refined, dealing with all possible resize and
move requests. If this method is refined if should always call
the
graphical->geometry
method of the super-class. It is allowed to change the parameters though
The example below defines a box fits on a 10x10 pixel grid.
:- pce_begin_class(grid_box, box).
geometry(B, X:[int], Y:[int], W:[int], H:[int]) :->
align_to_grid(X, NX),
align_to_grid(Y, NY),
align_to_grid(W, NW),
align_to_grid(H, NH),
send(B, send_super, geometry, NX, NY, NW, NH).
align_to_grid(@default, @default).
align_to_grid(Value, Aligned) :-
Aligned is ((Value + 4) // 10) * 10.
:- pce_end_class.
|pixmap],
background=[colour|pixmap]..., send(Gr, graphics_state, pen := 2, colour := red), ...
It is not allowed to return from graphical->_redraw_area
with a modified state. Therefore redraw-methods that modify the state
should normally use graphical->save_graphics_state
and graphical->restore_graphics_state.
Here is a typical example, drawing a thicker line.
'_redraw_area'(MyGr, Area:area) :->
send(MyGr, save_graphics_state),
send(MyGr, graphics_state, pen := 2),
send(MyGr, draw_line, 0,0,100,100),
send(MyGr, restore_graphics_state).
NOTE: It is generally advised to paint graphical objects that require the same graphics state together. So, if you need to paint alternating thick and thin lines, first draw all thick lines and then all thin lines instead of switching the state between each line.
<-width and graphical<-height
of the graphical. Connections between graphicals can only be made if
both graphicals have suitable handles defined.
Note that handles may also be attached at the class of the graphical.
Such handles are used by all instances of the class. See class->handle.
->set
width specified H-value.
<-graphicals
chain.
See also graphical->expose, graphical->swap
and graphical->overlap.
device to determine device<-pointed_objects.
Performs the following steps:
.event_tolerance.
Bugs: # The 5 pixels should be a resource
<-x, graphical<-y, graphical<-width
and
graphical<-height.
It sets the following defaults:
<-displayed @off (will be set by ->display) <-device @nil <-area new area from the arguments <-pen 1 1 <-texture none <-colour @default <-selected @off <-name Classname (box, ellipse, ...) <-handles @nil <-inverted @off <-cursor @nil <-request_compute @nil
Class graphical is a super-class of all graphical classes. Instances of this class can be created, but are probably of little use.
->keyboard_focus.
If @on or
@default and
the receiver passes the test graphical->_wants_keyboard_focus
send window->keyboard_focus.
See also device->advance
network (see graphical<-network)
of connected graphicals (see graphical->connect),
this method tries to give the entire graph of connected graphicals a
sensible layout. Don't expect too much: this algorithm does not know
anything about what your graph is representing.
The algorithm is based on [Eades:84]. It defines a number of springs between connected and non-connected nodes. The algorithm does a Monte-Carlo simulation using these springs.
The parameters are:
<-ideal_length
.0 Force
(outwards) between any pair of non-connected graphicals
<-visible
is often used to confine the graph to the visible part of a window
object.
<-network.
If provided explicitely only the provided graphicals are affected.
Notably providing all nodes in a window and using window<-visible
for the area argument will layout each network and spread the separated
networks over the window.
Connections between objects are found using graphical<-connected,
which may be refined to deal with application specific situations.
Visible annimation can be achieved calling this predicate in a loop
using a low number of iterations each time and force a display update
using
graphical->flush between each iteration.
Defaults: See description
Bugs: # Gets slow if there are many nodes (> 50).
->connect
@see class tree @see graphical<-network
@see topic Connections
->position:
moves the origin of the graphical to the indicated position. Normally
the origin is the top-left corner. Note however that this depends on the graphical<-orientation.
The origin of devices is the origin of the device's coordinate system.
->orientation:
Ensures graphical<-width and graphical<-height
are positive, making the origin the top-left corner of the graphical.
<-width
and graphical<-height.
The origin is the (X,Y) point of the graphical. (X+W, Y+H) refers to the
opposite corner (see graphical<->corner).
This method places the origin at one of the indicated corner. The
graphicals location and size on the screen is not affected by this
method. The values are:
north_west W>=0, H>=0 (->normalise) north_east W < 0, H>=0 south_west W>=0, H < 0 south_east W < 0, H < 0
<-selected
and the graphical<-window
defines one of the following
window<-selection_feedback
values:
.selection_handles
and paint the handles accordingly.
This method should not be called by the application programmer
directly. It is called by graphical->_redraw_area.
->pointer.
Diagnostics: Succeeds without side-effects if the graphical is not displayed.
popup instance variable, it will
fill this variable with the popup
object and assume the class knows how to display it.
popup to the
object and append the recogniser @_popup_gesture, which is a default popup_gesture
object
Bugs: Notably 1) is a dubious assumption.
<-x and graphical<-y
match the x- and y-coordinates of the argument point. Invokes graphical->set.
Synonym for graphical->move.
Recognisers associated this way are store very similar to attributes
associated with object->attribute.
If the graphical is saved to a file using object->save_in_file,
the recognisers will be saved with the graphical
object.
Using class-level programming, recognisers mat be associated with graphical
objects in two ways: by redefining the
graphical->initialise
method, calling graphical->recogniser
to associate event handling or by redefining the class graphical->event
method.
The following example defines movable_box as a class:
:- pce_begin_class(movable_box, box).
:- pce_global(@movable_box_recogniser,
new(move_gesture)).
event(MB, Ev:event) :->
"Associate @movable_box_recogniser"::
( send(MB, send_super, event, Ev)
; send(@movable_box_recogniser, event, Ev)
).
:- pce_end_class.
See also: class gesture,
class event, graphical->prepend_recongiser,
event->post, pce_global/2.
->request_compute.^point^.<-device
of possible associated connections. This method. It is invoked from
class device when the
position of the graphical in the consists-of tree relative to its window
has changed:
->reparent.
@see device->erase
@see device->display
@see device->reparent
-request_compute->set,
which in turn is invoked by all the methods that manipulate the geometry
of graphical
objects. The parameters are the requested X-, Y-, W and H values for the
graphical. Values that need not be changed are passed as @default.
Invokes graphical->geometry
to change the graphical<-area.
The indirection graphical->request_geometry
==> graphical->geometry
may be redefined when a graphical is constrained by some other object.
For example, class window
exploits this mechanism to let windows communicate with their tile
object.
->resize
resizing of collections of objects. This method resizes the graphical
with specified factor relative to the given origin. The X- and
Y-distance of each of the corners to this reference point is multiplies
by resp. the X- and Y-resize factor (1st and 2nd argument).
Defaults: The following defaults apply:
<-x
and graphical<-y of the graphical. @see area->decrease
@see text->resize
@see graphical->set
@see device->resize
<-width and graphical<-height
if the rotation is 90 or 270 degrees.
graphical<-center
is maintained.
Bugs: Someday, xpce should support real rotation and scaling at the level of class device.
->restore_graphics_state.
Each graphical->save_graphics_state
must be closed with a graphical->restore_graphics_state
before graphical->_redraw_area
returns. See also graphical->graphics_state.->toggle_selected
@see graphical-selected->size, graphical->position,
graphical->x,
...).
Note that this method is faster than graphical->size, graphical->position,
etc. when the geometry has to be changed from a set of integer values as
no intermediate object has to be created.
It first determines whether any action is necessary (i.e.
there is a non-default value that is not equal to the current value). If
so, it invoked graphical->geometry
which takes care of the class-dependent geometry-changing action.
Bugs:
graphical->geometry
is invoked using the internal function
simpleSend(), which bypasses possible object-level methods.
Geometry management can only be programmed at thee class level.
->set
with specified W- and H-parameter.
<-graphicals
chain. The background graphicals are at the head of this
chain, wile the foreground graphicals are at the tail.
<-selected
equals @on,
invoke graphical->selected: @off
and visa-versa. Useful for implementing selection handling.
->disconnect
and remove the graphical from it's graphical<-device.->set
with specified X-value to set the width of the graphical.
->set
width specified X- value.
->set
width specified Y- value.
The position relative to the screen may be found using graphical<-display_position.
See also graphical<-position
and graphical<-area.
Defaults: When device is omited, returns the absolute position
relative to the
top-most device. If the graphical is displayed, this is the
window in which the graphical is displayed.
Diagnostics: Fails silently if device is specified and graphical is not displayed on this device.
<-absolute_position.
<-absolute_position.
->alignment
and dialog_item->alignment
for details.<-all_attributes->auto_align
is for the integration with class dialog_item.
It associates an object->attribute
auto_align with the specified value.
graphical<-auto_align
returns the value of this attribute if present. Otherwise @on
if one of the attributes graphical<-above, graphical<-below, graphical<-left,
graphical<-right exists and @off
otherwise.<-y
+ graphical<-height.
With negative height, this is graphical<-y.
<-center_y
@see graphical<-center_x<-center<-centerlocal device that displays
both the receiver and the argument graphical.
graphical<-common_device
is used internally to determine the device on which to draw connections
between graphicals that are not displayed on the same device.
Diagnostics: Fails (silently) if no such device can be found. Note that this method always succeeds if both graphicals are displayed in the same window and always fails if they are displayed in different windows.
->connected
for details. If there are multiple connections matching the search the
first one is returned. See also graphical<-connections.
This method may be redefined to hook the behaviour of graphical->layout.
to: Graphical at other side link: Link that must be in the connection from_kind: Name of the handle at the from-side to_kind: Name of the handle at the to-side.
Fails if there are no connections.
Defaults: Omitted or default arguments imply any value is legal.
|node<-node.
Diagnostics: Fails if graphical<-device
equals @nil.
<-image
on the object. This simplifies manipulation of non-graphical
objects that manipulate a graphical (such as class node).
point(<-x + <-width, <-y + <-height)
<-x + <-width.
<-y + <-height
Diagnostics: Fails (silently) if the graphical is not displayed (indirectly) on a window.
|pixmap<-colour
equals @default,
this is the
graphical<-display_colour
of the graphical's device. Otherwise it is the value of the graphical<-colour
variable.
If the graphical is displayed, this will always return a colour. Otherwise this method fails if neither the graphical, nor its device specify the colour.
get(@event, position, @display, Point)
See event<-position
and @event.
See also graphical<-position
and
graphical<-absolute_position.
Diagnostics: Fails if graphical is not related to a window
Bugs: Yields incorrect results if the window is not actually displayed on the screen.
<-distance
using the areas of both graphicals.
Bugs: Silently assumes that both graphicals are displayed on the same device.
<-distance_x
@see graphical<-distanceFrames are commonly used to represent a tool of an application (or an
entire application). This method provides an easy way to find the
application object
from some deep-down part of it.
Diagnostics: Fails silently if the graphical is not displayed.
<-handles
of the graphical's class. The first matching handle is returned.
Diagnostics: Fails silently when there is no matching handle.
<-device.
Defaults: When no device is specified, the coordinate system of the graphical's device is used.
Diagnostics: Fails if there is no such handle or the graphical is not displayed on the given device.
<-handles
that are within distance pixels from the given point and have the given
kind. Fails if no handles match the criterion.
device
is displayed.
Defaults: When device is @default,
succeeds if graphical and all deviced above it have graphical<-displayed: @on.
<-bottom_sidefrom , toThis method is save on cyclic graphs. See also graphical->layout.
<-image
equal to this graphical if the graphical is controlled by a node of a
tree object. See also graphical<-contained_in.
Diagnostics: Fails silently when the node cannot be found.
Bugs: Only works if the node is attached to a tree and
actually displayed. It checks whether the graphical's device is a tree
and then uses‘node
graphical<-find_node’to find the node
object belonging to this graphical.
->orientation
<-absolute_position<-bottom_side<-height
and graphical<-width of the graphical's
area. Either or both of graphical<-width
and graphical<-height
may be negative. See
graphical<->orientation.
<-bottom_sideDiagnostics: Fails silently when graphical is not displayed on a window.