:lang: en
:toc:

[[cha:qtvcp:libraries]]
= QtVCP Libraries modules

// Custom lang highlight
// must come after the doc title, to work around a bug in asciidoc 8.6.6
:ini: {basebackend@docbook:'':ini}
:hal: {basebackend@docbook:'':hal}
:ngc: {basebackend@docbook:'':ngc}

Libraries are *prebuilt Python modules that give added features to QtVCP*. +
In this way you can select what features you want - yet don't have to
build common ones yourself. +

== `Status`

*`Status`* is a library that *sends GObject messages based on LinuxCNC's
current state*. +
It is an _extension of GladeVCP's <<cha:gstat,GStat>> object_.

It also has some functions to report status on such things as internal
jog rate.

You _connect a function_ call to the `STATUS` message you are interested
in, and QtVCP will call this function when the message is sent from
`STATUS`.

=== Usage

* *Import `Status` modules* +
  Add this Python code to your import section:
+
[source,python]
----
############################
# **** IMPORT SECTION **** #
############################

from qtvcp.core import Status
----

* *Instantiate `Status` module* +
  Add this Python code to your instantiate section:
+
[source,python]
----
STATUS = Status()
----

* *Connect to `STATUS` messages* +
  Use _GObject syntax_.
//FIXME explicit GObject syntax

=== Example

For example, you can catch machine on and off messages.

[NOTE]
The example below shows the _two common ways of connecting signals_,
one of them _using lambda_. +
*`lambda`* is _used to strip off or manipulate arguments_ from the status
message before calling the function. +
You can see the difference in the called function signature: the one
that uses lambda does not accept the status object - lambda did not pass
it to the function.

* Place these commands into the `[INITIALIZE]` section of the Python
handler file:
+
[source,python]
----
STATUS.connect('state-on', self.on_state_on)
STATUS.connect('state-off', lambda: w, self.on_state_off())
----
+
In this example code, when LinuxCNC is in _"machine on" state_ the
function `self.on_state_on` will be called. +
When LinuxCNC is in _"machine off" state_ the function `self.on_state_off`
will be called. +

* These would call functions that looks like these:
+
[source,python]
----
def on_state_on(self, status_object):
    print('LinuxCNC machine is on')
def on_state_off(self):
    print('LinuxCNC machine is off')
----

== `Info`

*`Info`* is a library to *collect and filter data from the INI file*. +

=== Available data and defaults

//TODO convert to definition list ?
----
LINUXCNC_IS_RUNNING
LINUXCNC_VERSION
INIPATH
INI = linuxcnc.ini(INIPATH)
MDI_HISTORY_PATH = '~/.axis_mdi_history'
QTVCP_LOG_HISTORY_PATH = '~/qtvcp.log'
MACHINE_LOG_HISTORY_PATH = '~/.machine_log_history'
PREFERENCE_PATH = '~/.Preferences'
SUB_PATH = None
SUB_PATH_LIST = []
self.MACRO_PATH = None
MACRO_PATH_LIST = []
INI_MACROS = self.INI.findall("DISPLAY", "MACRO")

IMAGE_PATH = IMAGEDIR
LIB_PATH = os.path.join(HOME, "share","qtvcp")

PROGRAM_FILTERS = None
PARAMETER_FILE = None
MACHINE_IS_LATHE = False
MACHINE_IS_METRIC = False
MACHINE_UNIT_CONVERSION = 1
MACHINE_UNIT_CONVERSION_9 = [1]*9
TRAJ_COORDINATES =
JOINT_COUNT = int(self.INI.find("KINS","JOINTS")or 0)
AVAILABLE_AXES = ['X','Y','Z']
AVAILABLE_JOINTS = [0,1,2]
GET_NAME_FROM_JOINT = {0:'X',1:'Y',2:'Z'}
GET_JOG_FROM_NAME = {'X':0,'Y':1,'Z':2}
NO_HOME_REQUIRED = False
HOME_ALL_FLAG
JOINT_TYPE = self.INI.find(section, "TYPE") or "LINEAR"
JOINT_SEQUENCE_LIST
JOINT_SYNC_LIST

JOG_INCREMENTS = None
ANGULAR_INCREMENTS = None
GRID_INCREMENTS

DEFAULT_LINEAR_JOG_VEL = 15 units per minute
MIN_LINEAR_JOG_VEL = 60 units per minute
MAX_LINEAR_JOG_VEL = 300 units per minute

DEFAULT_ANGULAR_JOG_VEL =
MIN_ANGULAR_JOG_VEL =
MAX_ANGULAR_JOG_VEL =

MAX_FEED_OVERRIDE =
MAX_TRAJ_VELOCITY =

AVAILABLE_SPINDLES = int(self.INI.find("TRAJ", "SPINDLES") or 1)
DEFAULT_SPINDLE_0_SPEED = 200
MAX_SPINDLE_0_SPEED = 2500
MAX_SPINDLE_0_OVERRIDE = 100
MIN_SPINDLE_0_OVERRIDE = 50

MAX_FEED_OVERRIDE = 1.5
MAX_TRAJ_VELOCITY
----

=== User message dialog info

//TODO convert to definition list ?
----
USRMESS_BOLDTEXT = self.INI.findall("DISPLAY", "MESSAGE_BOLDTEXT")
USRMESS_TEXT = self.INI.findall("DISPLAY", "MESSAGE_TEXT")
USRMESS_TYPE = self.INI.findall("DISPLAY", "MESSAGE_TYPE")
USRMESS_PINNAME = self.INI.findall("DISPLAY", "MESSAGE_PINNAME")
USRMESS_DETAILS = self.INI.findall("DISPLAY", "MESSAGE_DETAILS")
USRMESS_ICON = self.INI.findall("DISPLAY", "MESSAGE_ICON")
ZIPPED_USRMESS =

self.GLADEVCP = (self.INI.find("DISPLAY", "GLADEVCP")) or None
----

=== Embedded program info

//TODO convert to definition list ?
----
TAB_NAMES = (self.INI.findall("DISPLAY", "EMBED_TAB_NAME")) or None
TAB_LOCATION = (self.INI.findall("DISPLAY", "EMBED_TAB_LOCATION")) or []
TAB_CMD = (self.INI.findall("DISPLAY", "EMBED_TAB_COMMAND")) or None
ZIPPED_TABS =

MDI_COMMAND_LIST =      (heading: [MDI_COMMAND_LIST], title: MDI_COMMAND")
TOOL_FILE_PATH =        (heading: [EMCIO], title:TOOL_TABLE)
POSTGUI_HALFILE_PATH =  (heading: [HAL], title: POSTGUI_HALFILE)
----

=== Helpers

There are some _helper functions_ - mostly used for widget support:

*`get_error_safe_setting(_self_, _heading_, _detail_, default=_None_)`*::
*`convert_metric_to_machine(_data_)`*::
*`convert_imperial_to_machine(_data_)`*::
*`convert_9_metric_to_machine(_data_)`*::
*`convert_9_imperial_to_machine(_data_)`*::
*`convert_units(_data_)`*::
*`convert_units_9(_data_)`*::
*`get_filter_program(_fname_)`*::
*`get_qt_filter_extensions()`*::
    Get filter extensions in Qt format.

=== Usage

* *Import `Info` module* +
  Add this Python code to your import section:
+
[source,python]
----
############################
# **** IMPORT SECTION **** #
############################

from qtvcp.core import Info
----

* *Instantiate `Info` module* +
  Add this Python code to your instantiate section:
+
[source,python]
----
###########################################
# **** INSTANTIATE LIBRARIES SECTION **** #
###########################################

INFO = Info()
----

* *Access `INFO` data*
  Use this general syntax:
+
[source,python]
----
home_state = INFO.NO_HOME_REQUIRED
if INFO.MACHINE_IS_METRIC is True:
    print('Metric based')
----

== `Action`

*`Action`* library is used to *command LinuxCNC's motion controller*.

It tries to hide incidental details and add convenience methods for
developers.

// TODO Document available actions

=== Helpers

There are some *helper functions*, mostly used for this library's
support:

*`get_jog_info (_num_)`*::
*`jnum_check(_num_)`*::
*`ensure_mode(_modes_)`*::
*`open_filter_program(_filename_, _filter_)`*::
  Open G-code filter program.

=== Usage

* *Import `Action` module* +
  Add this Python code to your import section:
+
[source,python]
----
############################
# **** IMPORT SECTION **** #
############################

from qtvcp.core import Action
----

* *Instantiate `Action` module* +
  Add this Python code to your instantiate section:
+
[source,python]
----
###########################################
# **** INSTANTIATE LIBRARIES SECTION **** #
###########################################

ACTION = Action()
----

* *Access `ACTION` commands* +
  Use general syntax such as these:
+
[source,python]
----
ACTION.SET_ESTOP_STATE(state)
ACTION.SET_MACHINE_STATE(state)

ACTION.SET_MACHINE_HOMING(joint)
ACTION.SET_MACHINE_UNHOMED(joint)

ACTION.SET_LIMITS_OVERRIDE()

ACTION.SET_MDI_MODE()
ACTION.SET_MANUAL_MODE()
ACTION.SET_AUTO_MODE()

ACTION.SET_LIMITS_OVERRIDE()

ACTION.CALL_MDI(code)
ACTION.CALL_MDI_WAIT(code)
ACTION.CALL_INI_MDI(number)

ACTION.CALL_OWORD()

ACTION.OPEN_PROGRAM(filename)
ACTION.SAVE_PROGRAM(text_source, fname):

ACTION.SET_AXIS_ORIGIN(axis,value)
ACTION.SET_TOOL_OFFSET(axis,value,fixture = False)

ACTION.RUN()
ACTION.ABORT()
ACTION.PAUSE()

ACTION.SET_MAX_VELOCITY_RATE(rate)
ACTION.SET_RAPID_RATE(rate)
ACTION.SET_FEED_RATE(rate)
ACTION.SET_SPINDLE_RATE(rate)

ACTION.SET_JOG_RATE(rate)
ACTION.SET_JOG_INCR(incr)
ACTION.SET_JOG_RATE_ANGULAR(rate)
ACTION.SET_JOG_INCR_ANGULAR(incr, text)

ACTION.SET_SPINDLE_ROTATION(direction = 1, rpm = 100, number = 0)
ACTION.SET_SPINDLE_FASTER(number = 0)
ACTION.SET_SPINDLE_SLOWER(number = 0)
ACTION.SET_SPINDLE_STOP(number = 0)

ACTION.SET_USER_SYSTEM(system)

ACTION.ZERO_G92_OFFSET()
ACTION.ZERO_ROTATIONAL_OFFSET()
ACTION.ZERO_G5X_OFFSET(num)

ACTION.RECORD_CURRENT_MODE()
ACTION.RESTORE_RECORDED_MODE()

ACTION.SET_SELECTED_AXIS(jointnum)

ACTION.DO_JOG(jointnum, direction)
ACTION.JOG(jointnum, direction, rate, distance=0)

ACTION.TOGGLE_FLOOD()
ACTION.SET_FLOOD_ON()
ACTION.SET_FLOOD_OFF()

ACTION.TOGGLE_MIST()
ACTION.SET_MIST_ON()
ACTION.SET_MIST_OFF()

ACTION.RELOAD_TOOLTABLE()
ACTION.UPDATE_VAR_FILE()

ACTION.TOGGLE_OPTIONAL_STOP()
ACTION.SET_OPTIONAL_STOP_ON()
ACTION.SET_OPTIONAL_STOP_OFF()

ACTION.TOGGLE_BLOCK_DELETE()
ACTION.SET_BLOCK_DELETE_ON()
ACTION.SET_BLOCK_DELETE_OFF()

ACTION.RELOAD_DISPLAY()
ACTION.SET_GRAPHICS_VIEW(view)

ACTION.UPDATE_MACHINE_LOG(text, option=None):

ACTION.CALL_DIALOG(command):

ACTION.HIDE_POINTER(state):

ACTION.PLAY_SOUND(path):
ACTION.PLAY_ERROR():
ACTION.PLAY_DONE():
ACTION.PLAY_READY():
ACTION.PLAY_ATTENTION():
ACTION.PLAY_LOGIN():
ACTION.PLAY_LOGOUT():
ACTION.SPEAK(speech):

ACTION.BEEP():
ACTION.BEEP_RING():
ACTION.BEEP_START():

ACTION.SET_DISPLAY_MESSAGE(string)
ACTION.SET_ERROR_MESSAGE(string)
----

== `Tool`

This library *handles tool offset file changes*.

//TODO Tool: Usage

WARNING: *LinuxCNC doesn't handle third party manipulation of the tool
  file well.*

//FIXME Tools: Objets properties ?

=== Helpers

*`GET_TOOL_INFO(_toolnumber_)`*::
  This will return a Python *list of information on the requested tool number*. +

*`GET_TOOL_ARRAY()`*::
  This return a single Python *list of Python lists of tool information*.
+
This is a raw list formed _from the system tool file_.

*`ADD_TOOL(_newtool_ = [_-99, 0,'0','0','0','0','0','0','0','0','0','0','0','0', 0,'New Tool'_])`*::
  This will return a Python *tuple of two Python lists of Python lists of
tool information*:
+
* *`[0]`* will be _real tools informations_
* *`[1]`* will be _wear tools informations_ (tool numbers will be over
  10000; Fanuc style tool wear)

+
By default, adds a blanck tool entry with tool number -99. +
You can preload the `newtool` array with tool information.

*`DELETE_TOOLS(_toolnumber_)`*::
  *Delete the numbered tool*.

*`SAVE_TOOLFILE(_toolarray_)`*::
  This will *parse the `toolarray` and save it to the tool file* specified
  in the _INI file_ as the tool path.
+
This tool _array must contain all the available tools information_.
+
This array is expected to use the LinuxCNC _raw tool array_, i.e.
doesn't have tool wear entries.
+
//FIXME Really ??
It will return True if there was an error.

*`CONVERT_TO_WEAR_TYPE(_toolarray_)`*::
  This function *converts a LinuxCNC raw tool array to a QtVCP tool array*.
+
_QtVCP's tool array includes entries for X and Z axis tool wear_.
+
_LinuxCNC supports tool wear_ by adding *tool wear information into tool
entries above 10000*.
+
NOTE: This also *requires remap code to add the wear offsets at tool
  change time*.

*`CONVERT_TO_STANDARD_TYPE(_toolarray_)`*::
  This function *converts QtVCP's tool array into a LinuxCNC raw tool array*.
+
_QtVCP's array includes entries for X and Z axis tool wear_.
+
_LinuxCNC supports tool wear_ by adding *tool wear information into tool
entries above 10000*.
+
NOTE: This also *requires remap code to add the wear offsets t tool
  change time*.

//TODO Tools: Usage

== `Path`

*`Path`* module gives *reference to important files paths*.

=== Referenced Paths

*`PATH.PREFS_FILENAME`*::
  The preference file path.
*`PATH.WORKINGDIR`*::
  The directory QtVCP was launched from.
*`PATH.IS_SCREEN`*::
  Is this a screen or a VCP ?
*`PATH.CONFIGPATH`*::
  Launched configuration folder.
*`PATH.RIPCONFIGDIR`*::
  The Run-in-place config folder for QtVCP screens.
*`PATH.BASEDIR`*::
  Base folder for LinuxCNC.
*`PATH.BASENAME`*::
  The Qt Designer files name (no ending).
*`PATH.IMAGEDIR`*::
  The QtVCP image folder.
*`PATH.SCREENDIR`*::
  The QtVCP builtin Screen folder.
*`PATH.PANELDIR`*::
  The QtVCP builtin VCP folder.
*`PATH.HANDLER`*::
  Handler file Path.
*`PATH.HANDLERDIR`*::
  Directory where the Python handler file was found.
*`PATH.XML`*::
  QtVCP UI file path.
//FIXME PATH.HANDLERDIR for both handler and ui files ?
*`PATH.HANDLERDIR`*::
  Directory where the UI file was found.
*`PATH.QSS`*::
  QtVCP QSS file path.
*`PATH.PYDIR`*::
  LinuxCNC's Python library.
*`PATH.LIBDIR`*::
  The QtVCP library folder.
*`PATH.WIDGET`*::
  The QtVCP widget folder.
*`PATH.PLUGIN`*::
  The QtVCP widget plugin folder.
*`PATH.VISMACHDIR`*::
  Directory where prebuilt Vismach files are found.

Not currently used:

*`PATH.LOCALEDIR`*::
  Locale translation folder.
*`PATH.DOMAIN`*::
  Translation domain.

=== Helpers

There are some helper functions available:

[source,python]
----
file_list = PATH.find_vismach_files()
directory_list = PATH.find_screen_dirs()
directory_list = PATH.find_panel_dirs()
----

=== Usage

* *Import `Path` module* +
  Add this Python code to your import section:
+
[source,python]
----
############################
# **** IMPORT SECTION **** #
############################

from qtvcp.core import Path
----

* *Instantiate `Path` module* +
  Add this Python code to your instantiate section:
+
[source,python]
----
###########################################
# **** INSTANTIATE LIBRARIES SECTION **** #
###########################################

PATH = Path()
----

== `VCPWindow`

*`VCPWindow`* module gives *reference to the `MainWindow` and widgets*.

Typically this would be used for a library (eg, toolbar library uses
it) as the widgets get a reference to the `MainWindow` from the
`_hal_init()` function.

=== Usage

* *Import `VCPWindow` module* +
  Add this Python code to your import section:
+
[source,python]
----
############################
# **** IMPORT SECTION **** #
############################

from qtvcp.qt_makegui import VCPWindow
----

* *Instantiate `VCPWindow` module*+
  Add this Python code to your instantiate section:
+
[source,python]
----
###########################################
# **** INSTANTIATE LIBRARIES SECTION **** #
###########################################

WIDGETS = VCPWindow()
----

== `Aux_program_loader`

//FIXME Aux_program_loader: load (into QtVCP) or launch ?
*`Aux_program_loader`* module allows an easy way to
*load auxiliary programs LinuxCNC often uses*.

=== Helpers

*`load_halmeter()`*::
  _Halmeter_ is used to *display one HAL pin data*. +
  Load a `halmeter` with:
+
[source,python]
----
AUX_PRGM.load_halmeter()
----

*`load_ladder()`*::
  Load _ClassicLadder_ PLC program:
+
[source,python]
----
AUX_PRGM.load_ladder()
----

*`load_status()`*::
  Load LinuxCNC `status` program:
+
[source,python]
----
AUX_PRGM.load_status()
----

*`load_halshow()`*::
  Load _HALshow_, configure display program:
+
[source,python]
----
AUX_PRGM.load_halshow()
----

*`load_halscope()`*::
  Load _HALscope_ program:
+
[source,python]
----
AUX_PRGM.load_halscope()
----

*`load_tooledit()`*::
  Load _Tooledit_ program:
+
[source,python]
----
AUX_PRGM.load_tooledit(<TOOLEFILE_PATH>)
----

*`load_calibration()`*::
  Load _Calibration_ program:
+
[source,python]
----
AUX_PRGM.load_calibration()
----

*`keyboard_onboard()`*::
  Load _onboard/Matchbox keyboard_
+
[source,python]
----
AUX_PRGM.keyboard_onboard(<ARGS>)
----

=== Usage

* *Import `Aux_program_loader` module* +
  Add this Python code to your import section:

[source,python]
----
############################
# **** IMPORT SECTION **** #
############################

from qtvcp.lib.aux_program_loader import Aux_program_loader
----

* *Instantiate `Aux_program_loader` module* +
  Add this Python code to your instantiate section:

[source,python]
----
###########################################
# **** INSTANTIATE LIBRARIES SECTION **** #
###########################################

AUX_PRGM = Aux_program_loader()
----

== `Keylookup`

//FIXME Shouldn't it be Keylookup ?!

*`Keylookup`* module is used to *allow keypresses to control behaviors*
such as jogging.

It's used inside the handler file to facilitate creation of *key bindings*
such as keyboard jogging etc.

=== Usage

.Import `Keylookup` module

To import this modules add this Python code to your import section:

[source,python]
----
############################
# **** IMPORT SECTION **** #
############################

from qtvcp.lib.keybindings import Keylookup
----

.Instantiate `Keylookup` module

To instantiate `Keylookup` module* so you can use it, add this
Python code to your instantiate section:

[source,python]
----
###########################################
# **** INSTANTIATE LIBRARIES SECTION **** #
###########################################

KEYBIND = Keylookup()
----

.Add Key Bindings

NOTE: `Keylookup` requires code under the `processed_key_event` function
  to call `KEYBIND.call()`. +
  Most handler files already have this code.

In the handler file, under the _initialized function_ use this general
syntax to *create keybindings*:

[source,python]
----
KEYBIND.add_call("DEFINED_KEY","FUNCTION TO CALL", USER DATA)
----

Here we add a keybinding for `F10`, `F11` and `F12`:

[source,python]
----
##########################################
# Special Functions called from QtVCP
##########################################

# at this point:
# the widgets are instantiated.
# the HAL pins are built but HAL is not set ready
def initialized__(self):
    KEYBIND.add_call('Key_F10','on_keycall_F10',None)
    KEYBIND.add_call('Key_F11','on_keycall_override',10)
    KEYBIND.add_call('Key_F12','on_keycall_override',20)
----

And then we need to *add the functions that get called*. +
In the handler file, under the `KEY BINDING CALLS` section, add this:

[source,python]
----
#####################
# KEY BINDING CALLS #
#####################

def on_keycall_F12(self,event,state,shift,cntrl,value):
    if state:
        print('F12 pressed')

def on_keycall_override(self,event,state,shift,cntrl,value):
    if state:
        print('value = {}'.format(value))
----

== `Messages`

*`Messages`* module is used to *display pop up dialog messages on the screen*.

These messages are:

* _defined in the INI file under the `[DISPLAY]` heading_, and
* _controlled by HAL pins_.

=== Properties

*`_BOLDTEXT`*::
  Generally is a title.
*`_TEXT`*::
  Text below title, and usually longer.
*`_DETAIL`*::
  Text hidden unless clicked on.
*`_PINNAME`*::
  Basename of the HAL pin(s).
*`_TYPE`*::
  Specifies whether it is a:
  * *Status message* - shown in the _status bar and the notify dialog_. +
    Requires no user intervention.
  * *OK message* - _requiring the user to click OK to close the dialog_. +
    OK messages have _two HAL pins_:
  ** One HAL pin to launch the dialog, and
  ** One to signify it's waiting for response.
  * *Yes/No message* - _requiring the user to select yes or no buttons to close
    the dialog_. +
    Yes/No messages have _three hal pins_:
  ** One to show the dialog,
  ** One for waiting, and
  ** one for the answer.

//FIXME Messages: means STATUS messages will be generated each
//  time a focus_overlay is called or an alert sound emitted ?
By default it will send `STATUS` messages for `focus_overlay` and alert
sound. +

//FIXME Messages: Usage

=== Examples

Here are sample INI message definition code blocks that would be found
under the `[DISPLAY]` heading:

* Status bar and desktop notify pop up message:
+
[source,{ini}]
----
MESSAGE_BOLDTEXT = NONE
MESSAGE_TEXT = This is a statusbar test
MESSAGE_DETAILS = STATUS DETAILS
MESSAGE_TYPE = status
MESSAGE_PINNAME = statustest
----

* Pop up dialog asking a Yes/No question:
+
[source,{ini}]
----
MESSAGE_BOLDTEXT = NONE
MESSAGE_TEXT = This is a yes no dialog test
MESSAGE_DETAILS = Y/N DETAILS
MESSAGE_TYPE = yesnodialog
MESSAGE_PINNAME = yndialogtest
----

* Pop up dialog asking an OK answer + Status bar and desktop notification:
+
[source,{ini}]
----
MESSAGE_BOLDTEXT = This is the short text
MESSAGE_TEXT = This is the longer text of the both type test. It can be longer then the status bar text
MESSAGE_DETAILS = BOTH DETAILS
MESSAGE_TYPE = okdialog status
MESSAGE_PINNAME = bothtest
----

The `ScreenOptions` widget can automatically set up the message system.

== `Notify`

*`Notify`* module is used to *send messages that are integrated into the
desktop*.

It uses the `pynotify` library.

Ubuntu/Mint does not follow the standard so you can't set how long the
message stays up for. +
I suggest fixing this with the `notify-osd` package available from
https://launchpad.net/~leolik/+archive/leolik?field.series_filter=lucid[this PPA]
(DISCONTINUED due to move of Ubuntu to Gnome).

Notify _keeps a list of all the alarm messages since starting_ in
*`self.alarmpage`*. +
If you click _'Show all messages'_ in the notify popup, it will print
them to the terminal.

The `ScreenOptions` widget can automatically set up the notify system.

Typically `STATUS` _messages_ are used to sent notify messages. +

=== Properties

You can set the:

*`title`*::
  Notification message title text.
*`message`*::
  Notification message content text.
*`icon`*::
  Notification message icon.
*`timeout`*::
  How long the message stays up for.

//TODO Notify: Usage

== `Preferences`

*`Preferences`* module allows one to *load and save preference data
permanently to storage media*.

The `ScreenOptions` widget can automatically set up the preference system.

QtVCP searches for the `ScreenOptions` widget first and, if found, calls
*`+_pref_init()+`*. +
This will _create the preferences object_ and return it to QtVCP to pass
to all the widgets and add it to the window object attributes. +
In this case the preferences object would be accessable from the handler
file's `initialized_` method as *`self.w.PREFS_`*.

//FIXME Global or per widget prefs file ?
Also all widgets can have access to a specific preferences file at
initialization time.

// FIXME Preference file vs preference system ?
The `ScreenOptions` widget can automatically set up the preference file.

//FIXME So ScreenOptions set prefs are automatically saved and loaded to/from prefs file(s) ?

//TODO: Preferences: Usage

== `Player`

This module *allows playing sounds using Gstreamer, beep and Espeak*.

It can:

* *play sound/music files* using _Gstreamer_ (non blocking),
* *play sounds* using the `beep` library (currently blocks while beeping),
* *speak words* using the `espeak` library (non blocking while speaking).

There are _default alert sounds_ using Mint or FreeDesktop default sounds.

You can play arbitrary sounds or even songs by specifying the path.

`STATUS` has _messages to control `Player` module_.

The `ScreenOptions` widget can automatically set up the audio system.

=== Sounds

.Alerts
There are default *alerts* to choose from:

* `ERROR`
* `READY`
* `ATTENTION`
* `RING`
* `DONE`
* `LOGIN`
* `LOGOUT`

.Beeps
There are three *beeps*:

* `BEEP_RING`
* `BEEP_START`
* `BEEP`

=== Usage

* *Import `Player` module* +
  Add this Python code to your import section:
+
[source,python]
----
############################
# **** IMPORT SECTION **** #
############################

from qtvcp.lib.audio_player import Player
----

* *Instantiate `Player` module* +
  Add this Python code to your instantiated section:
+
[source,python]
----
###########################################
# **** INSTANTIATE LIBRARIES SECTION **** #
###########################################

SOUND = Player()
SOUND._register_messages()
----
+
The *`_register_messages()`* function connects the audio player to the
`STATUS` library so sounds can be played with the `STATUS` message system.

=== Example

To play sounds upon `STATUS` messages, use these general syntaxes:

[source,python]
----
STATUS.emit('play-alert','LOGOUT')
STATUS.emit('play-alert','BEEP')
STATUS.emit('play-alert','SPEAK This is a test screen for Q t V C P')
STATUS.emit('play-sound', 'PATH TO SOUND')
----

== Virtual Keyboard

This library allows you to *use `STATUS` messages to launch a virtual keyboard*.

It uses https://launchpad.net/onboard[`Onboard`] or
https://git.yoctoproject.org/matchbox-keyboard/[`Matchbox`] libraries
for the keyboard.

//TODO Virtual Keyboard: usage

//TODO Virtual Keyboard: What about other VKB alternatives like:
//  * kvkbd https://github.com/KDE/kvkbd
//  * Florence http://florence.sourceforge.net/
//  * Maliit https://maliit.github.io/, https://github.com/maliit/keyboard
//    which seems to have interesting contextual features.

//TODO VKB: Usage

== Toolbar Actions

This library supplies *prebuilt submenus and actions for toolbar menus
and toolbar buttons*.

Toolbuttons, menu and toolbar menus are:

* _built in Qt Designer_, and
* _assigned actions/submenus in the handler file_.

=== Actions

*`estop`*::
*`power`*::
*`load`*::
*`reload`*::
*`gcode_properties`*::
*`run`*::
*`pause`*::
*`abort`*::
*`block_delete`*::
*`optional_stop`*::
*`touchoffworkplace`*::
*`touchofffixture`*::
*`runfromline`*::
*`load_calibration`*::
*`load_halmeter`*::
*`load_halshow`*::
*`load_status`*::
*`load_halscope`*::
*`about`*::
*`zoom_in`*::
*`zoom_out`*::
*`view_x`*::
*`view_y`*::
*`view_y2`*::
*`view_z`*::
*`view_z2`*::
*`view_p`*::
*`view_clear`*::
*`show_offsets`*::
*`quit`*::
*`system_shutdown`*::
*`tooloffsetdialog`*::
*`originoffsetdialog`*::
*`calculatordialog`*::
*`alphamode`*::
*`inhibit_selection`*::
*`show_dimensions`*::
  Toggles dimensions display.

=== Submenus

*`recent_submenu`*::
*`home_submenu`*::
*`unhome_submenu`*::
*`zero_systems_submenu`*::
*`grid_size_submenu`*::
  Menu to set graphic grid size

=== Usage

Here is the typical code to add to the relevant _handler file_ sections:

[source,python]
----
############################
# **** IMPORT SECTION **** #
############################

from qtvcp.lib.toolbar_actions import ToolBarActions

###########################################
# **** instantiate libraries section **** #
###########################################

TOOLBAR = ToolBarActions()
----

=== Examples

* Assigning Tool Actions To Toolbar Buttons
+
[source,python]
----
##########################################
# Special Functions called from QtVCP
##########################################

# At this point:
#   * the widgets are instantiated,
#   * the HAL pins are built but HAL is not set ready.
def initialized__(self):
    TOOLBAR.configure_submenu(self.w.menuHoming, 'home_submenu')
    TOOLBAR.configure_action(self.w.actionEstop, 'estop')
    TOOLBAR.configure_action(self.w.actionQuit, 'quit', lambda d:self.w.close())
    TOOLBAR.configure_action(self.w.actionEdit, 'edit', self.edit)
    # Add a custom function
    TOOLBAR.configure_action(self.w.actionMyFunction, 'my_Function', self.my_function)
----

* Add a custom toolbar function:
+
[source,python]
----
#####################
# GENERAL FUNCTIONS #
#####################

def my_function(self, widget, state):
    print('My function State = ()'.format(state))
----

== Qt Vismach Machine Graphics library

//FIXME Remove from here and merge in qtvcp-vismach.adoc

*`Qt_vismach`* is a _set of Python functions_ that can be
*used to create and animate models of machines*.

_Vismach_:

* _displays the model_ in a *3D viewport*
* _animates the model parts_ as the values of associated HAL pins change.

This is the _Qt based version_ of the library, there is also a tkinter
version available in LinuxCNC.

The Qt version _allows embedding the simulation in other screens_.

=== Builtin Samples

There are included _sample panels_ in QtVCP for:

* a 3-Axis XYZ mill,
* a 5-Axis gantry mill,
* a 3-Axis mill with an A axis/spindle, and
* a scara mill.

Most of these samples, if loaded after a running LinuxCNC configuration
(including non-QtVCP based screens), will react to machine movement. +
Some require HAL pins to be connected for movement.

From a terminal (pick one):

----
qtvcp vismach_mill_xyz
qtvcp vismach_scara
qtvcp vismach_millturn
qtvcp vismach_5axis_gantry
----

=== Primitives Library

Provides the *basic building blocks of a simulated machine*.

*`Collection`*::
  A `collection` is an *object of individual machine parts*.
+
This holds a *hierarchical list* of primitive shapes or _STL objects_
that operations can be applied to.
//TODO Can't a collection hold other collections ?

*`Translate`*::
  This object will perform an *OpenGL translation* calculation
  _on a Collection object_.
//TODO Not on primitives ?
+
Translation refers to _moving an object in straight line_ to a different
position on screen.

*`Scale`*::
  This object will perform an *OpenGL scale* function _on a collection object_.

*`HalTranslate`*::
  This object will perform an *OpenGL translation* calculation
  _on a Collection object_, *offset by the HAL pin value*.
+
Translation refers to moving an object in straight line to a different
position on screen.
+
You can either:
+
* _read a pin from a component owned by the Vismach object_, or
* _read a HAL system pin directly_ if the component argument is set to
  `None`.

*`Rotate`*::
  This object will perform an *OpenGL rotation* calculation
  _on a Collection object_.

*`HalRotate`*::
  This object will perform an *OpenGL rotation* calculation
  _on a Collection object_, *offset by the HAL pin value*.
+
You can either:
+
* _read a pin from a component owned by the vismach_ object, or
* _read a HAL system pin directly_ if the component argument is set to
  `None`.

*`HalToolCylinder`*::
  This object will build a _CylinderZ object_ that will *change size and
  length based on loaded tool dimensition* (from the tool table) +
+
It reads the `halui.tool.diameter` and `motion.tooloffset.z` _HAL pins_.
+
Example from mill_xyz sample:
+
[source,python]
----
toolshape = CylinderZ(0)
toolshape = Color([1, .5, .5, .5], [toolshape])
tool = Collection([
    Translate([HalTranslate([tooltip], None, "motion.tooloffset.z", 0, 0, -MODEL_SCALING)], 0, 0, 0),
    HalToolCylinder(toolshape)
])
----

*`Track`*::
  *Move and rotate an object to point from one `capture()` 'd coordinate
  system to another*.
+
Base object to _hold coordinates for primitive shapes_.

*`CylinderX`, `CylinderY`, `CylinderZ`*::
  *Build a cylinder on the X, Y or Z axis* by giving _endpoint_ (X, Y, or Z)
  and _radii_ coordinates.

*`Sphere`*::
  *Build a sphere* from _center_ and _radius_ coordinates.

//FIXME Vismach: Triangle: Don't coordinates need 2 axes and not be limited to Z !?
*`TriangleXY`, `TriangleXZ`, `TriangleYZ`*::
  *Build a triangle* in the _specified plane_ by giving the
  _corners Z coordinates_ for each side

//FIXME Vismach: Arc: how to specify ?
*`ArcX`*::
  *Build an arc* by specifying

*`Box`*::
  *Build a box* specified by the _6 vertex coordinates_.

*`BoxCentered`*::
  *Build a box centered on origin* by specifying the _width in X and Y_,
  and the _height in Z_.

*`BoxCenteredXY`*::
  *Build a box centered in X and Y, and running from Z=0*, by specifying
  the _width in X and Y_, and running up or down to the specified
  _height in Z_.

*`Capture`*::
  *Capture current transformation matrix of a collection*.
+
NOTE: This _transforms from the current coordinate system to the
  viewport system_, NOT to the world system.

*`Hud`*::
  *Heads up display* draws a _semi-transparent text box_.
+
Use:
+
* `HUD.strs` for things that must be _updated constantly_,
* `HUD.show("stuff")` for one-shot things like error messages.

//FIXME: Vismach: Color: can it be applied to a primitive ?
*`Color`*::
  *Applies a color* to the _parts of a collection_.

//FIXME: Vismach: Ascii(STL|OJ): is a "part" a collection ?
*`AsciiSTL`, `AsciiOBJ`*::
  *Loads a STL or OBJ data file* as a _Vismach part_.

=== Usage

.Import a simulation
Here is how one might import the XYZ_mill simulation in a QtVCP panel
or screen handler file.

[source,python]
----
############################
# **** IMPORT SECTION **** #
############################

import mill_xyz as MILL
----

.Instantiate and use the simulation widget
Instantiate the simulation widget and add it to the screen's main
layout:

[source,python]
----
    ##########################################
    # Special Functions called from QtVCP
    ##########################################

    # At this point:
    #   * the widgets are instantiated,
    #   * the HAL pins are built but HAL is not set ready.
    def initialized__(self):
        machine = MILL.Window()
        self.w.mainLayout.addWidget(machine)
----

=== More Information

More information on how to build a custom machine simulation in the
<<cha:qtvcp:vismach,Qt Vismach>> chapter.

// vim: set syntax=asciidoc:
