Traditional machine control panels are large sheets of steel with push buttons, knobs, lights and sometimes meters mounted on them. They have many advantages - the buttons are far more rugged than a computer keyboard, and large enough that you can usually operate the correct one by feel while looking elsewhere, for example at the tool. However, they also have disadvantages. The occupy a lot of panel space, they are expensive, and wiring them into the PC can use up a lot of I/O pins. That is where Virtual Control Panels come in.
A Virtual Control Panel (VCP) is a window on the computer screen with buttons, meters, switches, etc. When you click on a VCP button, it changes the state of a HAL pin, exactly as if you had pressed a physical button wired to an input pin on an I/O card. Likewise, a VCP LED lights up when a HAL pin goes true, just like a physical indicator lamp wired to an output pin on an I/O card. Virtual control panels are not intended to replace physical panels - sometimes there is just no substitute for a big rugged oil-tight push button. But virtual panels can be used for testing or monitoring things that don't require physical buttons and lights, to temporarily replace real I/O devices while debugging ladder logic, or perhaps to simulate a physical panel before you build it and wire it to an I/O board.
The layout of a pyVCP panel is specified with an XML file that contains widget tags between <pyvcp> and </pyvcp>. For example:
<pyvcp>
<label text="This is a LED indicator"/>
<led/>
</pyvcp>
If you place this text in a file called tiny.xml, and run
halrun -I loadusr pyvcp -c mypanel tiny.xml
pyVCP will create the panel for you, which includes two widgets, a Label with the text “This is a LED indicator”, and a LED, used for displaying the state of a HAL BIT signal. It will also create a HAL component named “mypanel” (all widgets in this panel are connected to pins that start with “mypanel.”). Since no <halpin> tag was present inside the <led> tag, pyVCP will automatically name the HAL pin for the LED widget mypanel.led.0
For a list of widgets and their tags and options, see the widget reference below.
Once you have created your panel, connecting HAL signals to and from the pyVCP pins is done with:
halcmd linksp
If you are new to HAL, the HAL Tutorial[->] is recommended.
Parts of pyVCP files are evaluated as Python code, and can take any action available to Python programs. Only use pyVCP .xml files from a source that you trust.
Since AXIS uses the same GUI toolkit (Tkinter) as pyVCP, it is possible to include a pyVCP panel on the right side of the normal AXIS user interface. A typical example is explained below.
Place your pyVCP XML file describing the panel in the same directory where your .ini file is. Say we we want to display the current spindle speed using a Bar widget. Place the following in a file called spindle.xml:
<pyvcp>
<label>
<text>"Spindle speed:"</text>
</label>
<bar>
<halpin>"spindle-speed"</halpin>
<max_>5000</max_>
</bar>
</pyvcp>
Here we've made a panel with a Label and a Bar widget, specified that the HAL pin connected to the Bar should be named “spindle-speed”, and set the maximum value of the bar to 5000 (see widget reference below for all options). To make AXIS aware of this file, and call it at start up, we need to specify the following in the [DISPLAY] section of the .ini file:
PYVCP = spindle.xml
To make our widget actually display the spindle-speed it needs to be hooked up to the appropriate HAL signal. A .hal file that will be run once AXIS and pyVCP have started can be specified in the [HAL] section of the .ini file:
POSTGUI_HALFILE = spindle_to_pyvcp.hal
This change will run the HAL commands specified in “spindle_to_pyvcp.hal”. In our example the contents could look like this:
linksp spindle-rpm-filtered pyvcp.spindle-speed
assuming that a signal called “spindle-rpm-filtered” already exists. Note that when running together with AXIS, all pyVCP widget HAL pins have names that start with “pyvcp.”.
This is what the newly created pyVCP panel should look like in AXIS. The sim/lathe configuration is already configured this way.
HAL signals come in two variants, BIT and FLOAT. pyVCP can either display the value of the signal with an indicator widget, or modify the signal value with a control widget. Thus there are four classes of pyVCP widgets that you can connect to a HAL signal. A fifth class of helper widgets allow you to organize and label your panel.
Each widget is described briefly, followed by the markup used, and a screen shot. All tags inside the main widget tag are optional.
At the present time, both a tag-based and an attribute-based syntax are supported. For instance, the following XML fragments are treated identically:
<led halpin="my-led"/>
and
<led><halpin>"my-led"</halpin></led>
When the attribute-based syntax is used, the following rules are used to turn the attributes value into a Python value:
When the tag-based syntax is used, the text within the tag is always evaluated as a Python expression.
The examples below show a mix of formats.
A LED is used to indicate the status of a BIT signal. The LED color will be on_color when the BIT signal is true, and off_color otherwise.
<led>
<halpin>"my-led"</halpin>
<size>50</size>
<on_color>"blue"</on_color>
<off_color>"black"</off_color>
</led>
<halpin> sets the name of the pin, default is “led.n”, where n is an integer
<size> sets the size of the led, default is 20
<on_color> sets the color of the LED when the pin is true. default is “green”
<off_color> sets the color of the LED when the pin is false. default is “ref”
A button is used to control a BIT pin. The pin will be set True when the button is pressed and held down, and will be set False when the button is released.
<button>
<halpin>"my-button"</halpin>
<text>"ON"</text>
</button>
A checkbutton controls a BIT pin. The pin will be set True when the button is checked, and false when the button is unchecked.
<checkbutton>
<halpin>"my-checkbutton"</halpin>
</checkbutton>
An unchecked checkbutton: , and a checked one:
A radiobutton will set one of a number of BIT pins true. The other pins are set false.
<radiobutton>
<choices>["one","two","three"]</choices>
<halpin>"my-radio"</halpin>
</radiobutton>
Note that the HAL pins in the example above will me named my-radio.one, my-radio.two, and my-radio.three. In the image above, "three" is the selected value.
The number widget displays the value of a FLOAT signal.
<number>
<halpin>"my-number"</halpin>
<font>("Helvetica",50)</font>
<format>"+4.3f"</format>
</number>
<font> is a Tkinter font type and size specification. Note that on Ubuntu 6.06 "Helvetica" is not available in sizes above ca 40 or 50. One font that will show up to at least size 200 is "courier 10 pitch", so for a really big Number widget you could specify:
<font>("courier 10 pitch",100)</font>
<format> is a "C-style" format specified that determines how the number is displayed.
A bar widget displays the value of a FLOAT signal both graphically using a bar display and numerically.
<bar>
<halpin>"my-bar"</halpin>
<min_>0</min_>
<max_>123</max_>
<bgcolor>"grey"</bgcolor>
<fillcolor>"red"</fillcolor>
</bar>
Meter displays the value of a FLOAT signal using a traditional dial indicator.
<meter>
<halpin>"my-meter"</halpin>
<text>"Voltage"</text>
<size>300</size>
<min_>-12</min_>
<max_>33</max_>
</meter>
Spinbox controls a FLOAT pin. You increase or decrease the value of the pin by either pressing on the arrows, or pointing at the spinbox and rolling your mouse-wheel.
<spinbox>
<halpin>"my-spinbox"</halpin>
<min_>-12</min_>
<max_>33</max_>
<resolution>0.1</resolution>
<format>"2.3f"</format>
<font>("Arial",30)</font>
</spinbox>
Scale controls a FLOAT pin. You increase or decrease the value of the pin be either dragging the slider, or pointing at the scale and rolling your mouse-wheel.
<scale>
<halpin>"my-scale"</halpin>
<resolution>0.1</resolution>
<orient>HORIZONTAL</orient>
<min_>-33</min_>
<max_>26</max_>
</scale>
Jogwheel mimics a real jogwheel by outputting a FLOAT pin which counts up or down as the wheel is turned, either by dragging in a circular motion, or by rolling the mouse-wheel.
<jogwheel>
<halpin>"my-wheel"</halpin>
<cpr>45</cpr>
<size>250</size>
</jogwheel>
Containers are widgets that contain other widgets.
Use a Hbox when you want to stack widgets horizontally next to each other.
<hbox>
<label><text>"a vbox:"</text></label>
<led></led>
<number></number>
<bar></bar>
</hbox>
Inside a Hbox, you can use the <boxfill fill=""/>, <boxanchor anchor=""/>, and <boxexpand expand=""/> tags to choose how items in the box behave when the window is re-sized. For details of how fill, anchor, and expand behave, refer to the Tk pack manual page, pack(3tk). By default, fill="y", anchor="center", expand="yes".
Use a Vbox when you want to stack widgets vertically on top of each other.
<vbox>
<label><text>"a vbox:"</text></label>
<led></led>
<number></number>
<bar></bar>
</vbox>
Inside a Hbox, you can use the <boxfill fill=""/>, <boxanchor anchor=""/>, and <boxexpand expand=""/> tags to choose how items in the box behave when the window is re-sized. For details of how fill, anchor, and expand behave, refer to the Tk pack manual page, pack(3tk). By default, fill="x", anchor="center", expand="yes".
A label is a piece of text on your panel.
<label>
<text>"This is a Label:"</text>
<font>("Helvetica",20)</font>
</label>
A labelframe is a frame with a groove and a label at the upper-left corner.
<labelframe text="Group Title">
<hbox>
<led/> <led/>
</hbox>
</labelframe>
A table is a container that allows layout in a grid of rows and columns. Each row is started by a <tablerow/> tag. A contained widget may span rows or columns through the use of the <tablespan rows= cols=/> tag. The sides of the cells to which the contained widgets “stick” may be set through the use of the <tablesticky sticky=/> tag. A table expands on its flexible rows and columns.
Example:
<table flexible_rows="[2]" flexible_columns="[1,4]">
<tablesticky sticky="new"/>
<tablerow/>
<label text="A (cell 1,1)"/>
<label text="B (cell 1,2)"/>
<tablespan columns="2"/><label text="C, D (cells 1,3 and 1,4)">
<tablerow/>
<label text="E (cell 2,1)"/>
<tablesticky sticky="nsew"/><tablespan rows="2"/>
<label text="'spans\n2 rows'"/>
<tablesticky sticky="new"/><label text="G (cell 2,3)"/>
<label text="H (cell 2,4)"/>
<tablerow/>
<label text="J (cell 3,1)"/>
<label text="K (cell 3,2)"/>
<label text="M (cell 3,4)"/>
</table>