1. Preference File Loading/Saving
Here is how to load and save preferences at launch and closing time.
-
Preference file option must be set in the
ScreenOptions
widget. -
Preference file path must be set in the
INI
configuration.
Under the def initialized__(self):
function add:
if self.w.PREFS_: # variable name (entry name, default value, type, section name) self.int_value = self.w.PREFS_.getpref('Integer_value', 75, int, 'CUSTOM_FORM_ENTRIES') self.string_value = self.w.PREFS_.getpref('String_value', 'on', str, 'CUSTOM_FORM_ENTRIES')
In the closing_cleanup__()
function, add:
if self.w.PREFS_: # variable name (entry name, variable name, type, section name) self.w.PREFS_.putpref('Integer_value', self.integer_value, int, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('String_value', self.string_value, str, 'CUSTOM_FORM_ENTRIES')
2. Use QSettings
To Read/Save Variables
Here is how to load and save variables using PyQt’s QSettings
functions:
-
Use
Group
to keep names organized and unique. -
Account for
none
value returned when reading a setting which has no entry. -
Set defaults to cover the first time it is run using the
or _<default_value>_
syntax.
Note
|
The file is actually saved in ~/.config/QtVcp |
In this example:
-
We add
or 20
andor 2.5
as defaults. -
The names
MyGroupName
,int_value
,float_value
,myInteger
, andmyFloat
are user defined. -
Under the
def initialized__(self):
function add:# set recorded columns sort settings self.SETTINGS_.beginGroup("MyGroupName") self.int_value = self.SETTINGS_.value('myInteger', type = int) or 20 self.float_value = self.SETTINGS_.value('myFloat', type = float) or 2.5 self.SETTINGS_.endGroup()
-
Under the
def closing_cleanup__(self):
function add:# save values with QSettings self.SETTINGS_.beginGroup("MyGroupName") self.SETTINGS_.setValue('myInteger', self.int_value) self.SETTINGS_.setValue('myFloat', self.float_value) self.SETTINGS_.endGroup()
3. Add A Basic Style Editor
Being able to edit a style on a running screen is convenient.
StyleSheetEditor
module in the IMPORT SECTION
:from qtvcp.widgets.stylesheeteditor import StyleSheetEditor as SSE
StyleSheetEditor
module in the INSTANTIATE SECTION
:STYLEEDITOR = SSE()
keybinding
in the INITIALIZE SECTION
:Under the +__init__.(self, halcomp, widgets, paths):+
function add:
KEYBIND.add_call('Key_F12','on_keycall_F12')
KEYBINDING SECTION
:def on_keycall_F12(self,event,state,shift,cntrl): if state: STYLEEDITOR.load_dialog()
4. Request Dialog Entry
QtVCP uses STATUS
messages to pop up and return information from dialogs.
Prebuilt dialogs keep track of their last position and include options for focus shading and sound.
To get information back from the dialog requires using a STATUS
general
message.
Status
module in the IMPORT SECTION
from qtvcp.core import Status STATUS = Status()
This loads and initializes the Status
library.
STATUS general
messages in the INITIALIZE SECTIONUnder the +__init__.(self, halcomp, widgets, paths)+
function:
STATUS.connect('general',self.return_value)
This registers STATUS
to call the function self.return_value
when a
general message is sent.
GENERAL FUNCTIONS
sectiondef request_number(self): mess = {'NAME':'ENTRY','ID':'FORM__NUMBER', 'TITLE':'Set Tool Offset'} STATUS.emit('dialog-request', mess)
The function
-
creates a Python
dict
with:-
NAME
- needs to be set to the dialogs unique launch name.
NAME
sets which dialog to request.
ENTRY
orCALCULATOR
allows entering numbers. -
ID
- needs to be set to a unique name that the function supplies.ID
should be a unique key. -
TITLE
sets the dialog title. -
Arbitrary data can be added to the
dict
. The dialog will ignore them but send them back to the return code.
-
-
Sends the
dict
as adialog-request
STATUS
message
CALLBACKS FROM STATUS
section.# Process the STATUS return message from set-tool-offset def return_value(self, w, message): num = message.get('RETURN') id_code = bool(message.get('ID') == 'FORM__NUMBER') name = bool(message.get('NAME') == 'ENTRY') if id_code and name and num is not None: print('The {} number from {} was: {}'.format(name, id_code, num))
This catches all general messages so it must check the dialog type and id code to confirm it’s our dialog.
In this case we had requested an ENTRY
dialog and our unique id was FORM_NUMBER
, so now we know the message is for us.
ENTRY
or CALCULATOR
dialogs return a float number.
5. Speak a Startup Greeting
This requires the espeak
library installed on the system.
Status
in the IMPORT
sectionfrom qtvcp.core import Status STATUS = Status()
INITIALIZE SECTION
Under the init.(self, halcomp, widgets, paths)
function:
STATUS.emit('play-alert','SPEAK Please remember to oil the ways.')
SPEAK
is a keyword: everything after it will be pronounced.
6. ToolBar Functions
Toolbar buttons and submenus are added in Qt Designer but the code to make them do something is added in the handler file. To add a submenus in Qt Designer:
-
Add a
Qaction
by typing in the toolbar column then clicking the + icon on the right. -
This will add a sub column that you need to type a name into.
-
Now the original
Qaction
will be aQmenu
instead. -
Now erase the
Qaction
you added to thatQmenu
, the menu will stay as a menu.
In this example we assume you added a toolbar with one submenu and three actions. These actions will be configured to create:
-
a recent file selection menu,
-
an about pop up dialog action,
-
a quit program action, and
-
a user defined function action.
The objectName
of the toolbar button is used to identify the button when configuring it - descriptive names help.
Using the action editor menu, right click and select edit.
Edit the object name, text, and button type for an appropriate action.
In this example the:
-
submenu name must be
menuRecent
, -
actions names must be
actionAbout
,actionQuit
,actionMyFunction
toolbar_actions
library in the IMPORT SECTION
from qtvcp.lib.toolbar_actions import ToolBarActions
ToolBarActions
module in the INSTANTIATE LIBRARY SECTION
TOOLBAR = ToolBarActions()
SPECIAL FUNCTIONS SECTION
Under the def initialized__(self)
function add:
TOOLBAR.configure_submenu(self.w.menuRecent, 'recent_submenu') TOOLBAR.configure_action(self.w.actionAbout, 'about') TOOLBAR.configure_action(self.w.actionQuit, 'Quit', lambda d:self.w.close()) TOOLBAR.configure_action(self.w.actionMyFunction, 'My Function', self.my_function)
GENERAL FUNCTIONS SECTION
def my_function(self, widget, state): print('My function State = ()'.format(state))
The function to be called if the action "My Function" button is pressed.
7. Add HAL Pins That Call Functions
In this way you don’t need to poll the state of input pins.
Qhal
library in the IMPORT SECTION
from qtvcp.core import Qhal
This is to allow access to QtVCP’s HAL component.
Qhal
in the INSTANTIATE LIBRARY SECTION
QHAL = Qhal()
Under the initialised__
function, make sure there is an entry similar to this:
########################################## # 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): self.pin_cycle_start_in = QHAL.newpin('cycle-start-in',QHAL.HAL_BIT, QHAL.HAL_IN) self.pin_cycle_start_in.value_changed.connect(lambda s: self.cycleStart(s))
GENERAL FUNCTIONS SECTION
##################### # general functions # ##################### def cycleStart(self, state): if state: tab = self.w.mainTab.currentWidget() if tab in( self.w.tab_auto, self.w.tab_graphics): ACTION.RUN(line=0) elif tab == self.w.tab_files: self.w.filemanager.load() elif tab == self.w.tab_mdi: self.w.mditouchy.run_command()
This function assumes there is a Tab widget, named mainTab
,
that has tabs with the names tab_auto
, tab_graphics
, tab_filemanager
and tab_mdi
.
In this way the cycle start button works differently depending on what tab is shown.
This is simplified - checking state and error trapping might be helpful.
8. Add A Special Max Velocity Slider Based On Percent
Some times you want to build a widget to do something not built in. The built in Max velocity slider acts on units per minute, here we show how to do on percent.
The STATUS
command makes sure the slider adjusts if LinuxCNC changes the current max velocity.
valueChanged.connect()
calls a function when the slider is moved.
In Qt Designer add a QSlider
widget called mvPercent
, then add the following code to the handler file:
############################# # SPECIAL FUNCTIONS SECTION # ############################# def initialized__(self): self.w.mvPercent.setMaximum(100) STATUS.connect('max-velocity-override-changed', \ lambda w, data: self.w.mvPercent.setValue( \ (data / INFO.MAX_TRAJ_VELOCITY)*100 \ ) ) self.w.mvPercent.valueChanged.connect(self.setMVPercentValue) ##################### # GENERAL FUNCTIONS # ##################### def setMVPercentValue(self, value): ACTION.SET_MAX_VELOCITY_RATE(INFO.MAX_TRAJ_VELOCITY * (value/100.0))
9. Toggle Continuous Jog On and Off
Generally selecting continuous jogging is a momentary button, that requires you to select the previous jog increment after.
We will build a button that toggles between continuous jog and whatever increment that was already selected.
In Qt Designer:
-
Add an
ActionButton
with no action -
Call it
btn_toggle_continuous
. -
Set the
AbstractButton
propertycheckable
toTrue
. -
Set the
ActionButton
propertiesincr_imperial_number
andincr_mm_number
to0
. -
Use Qt Designer’s slot editor to use the button signal
clicked(bool)
to call form’s handler functiontoggle_continuous_clicked()
.
See Using Qt Designer To Add Slots section for more information.
Then add this code snippets to the handler file under the initialized__
function:
# at this point: # the widgets are instantiated. # the HAL pins are built but HAL is not set ready def initialized__(self): STATUS.connect('jogincrement-changed', \ lambda w, d, t: self.record_jog_incr(d,t) \ ) # set a default increment to toggle back to self.L_incr = 0.01 self.L_text = "0.01in"
In the GENERAL FUNCTIONS SECTION
add:
##################### # GENERAL FUNCTIONS # ##################### # if it isn't continuous, record the latest jog increment # and untoggle the continuous button def record_jog_incr(self,d, t): if d != 0: self.L_incr = d self.L_text = t self.w.btn_toggle_continuous.safecheck(False)
In the CALLBACKS FROM FORM SECTION
add:
####################### # CALLBACKS FROM FORM # ####################### def toggle_continuous_clicked(self, state): if state: # set continuous (call the actionbutton's function) self.w.btn_toggle_continuous.incr_action() else: # reset previously recorded increment ACTION.SET_JOG_INCR(self.L_incr, self.L_text)
10. Class Patch The File Manager Widget
Note
|
Class patching (monkey patching) is a little like black magic - so use it only if needed. |
The File manager widget is designed to load a selected program in LinuxCNC. But maybe you want to print the file name first.
We can "class patch" the library to redirect the function call.
In the IMPORT SECTION
add:
from qtvcp.widgets.file_manager import FileManager as FM
Here we are going to:
-
Keep a reference to the original function (1) so we can still call it
-
Redirect the class to call our custom function (2) in the handler file instead.
########################################## # Special Functions called from QtVCP # ########################################## # For changing functions in widgets we can 'class patch'. # class patching must be done before the class is instantiated. def class_patch__(self): self.old_load = FM.load # keep a reference of the old function <1> FM.load = self.our_load # redirect function to our handle file function <2>
-
Write a custom function to replace the original:
This function must have the same signature as the original function.
In this example we are still going to call the original function by using the reference to it we recorded earlier.
It requires the first argument to be the widget instance, which in this case isself.w.filemanager
(the name given in the Qt Designer editor).##################### # GENERAL FUNCTIONS # ##################### def our_load(self,fname): print(fname) self.old_load(self.w.filemanager,fname)
Now our custom function will print the file path to the terminal before loading the file. Obviously boring but shows the principle.
Note
|
There is another slightly different way to do this that can have advantages:
you can store the reference to the original function in the original class. ########################################## # Special Functions called from QtVCP ########################################## # For changing functions in widgets we can 'class patch'. # class patching must be done before the class is instantiated. def class_patch__(self): FM.super__load = FM.load # keep a reference of the old function in the original class FM.load = self.our_load # redirect function to our handle file function ##################### # GENERAL FUNCTIONS # ##################### def our_load(self,fname): print(fname) self.w.filemanager.super__load(fname) |
11. Adding Widgets Programmatically
In some situation it is only possible to add widgets with Python code rather then using the Qt Designer editor.
When adding QtVCP widgets programmatically, sometimes there are extra steps to be taken.
Here we are going to add a spindle speed indicator bar and up-to-speed LED to a tab widget corner. Qt Designer does not support adding corner widgets to tabs but PyQt does.
This is a cut down example from QtAxis screen’s handler file.
First we must import the libraries we need, if they’re not already imported in the handler file:
-
QtWidgets
gives us access to theQProgressBar
, -
QColor
is for the LED color, -
StateLED
is the QtVCP library used to create the spindle-at-speed LED, -
Status
is used to catch LinuxCNC status information, -
Info
gives us information about the machine configuration.
############################ # **** IMPORT SECTION **** # ############################ from PyQt5 import QtWidgets from PyQt5.QtGui import QColor from qtvcp.widgets.state_led import StateLED as LED from qtvcp.core import Status, Info
Status
and Info
channelsSTATUS
and INFO
are initialized outside the handler class so as to be global references (no self. in front):
########################################## # **** instantiate libraries section **** # ########################################### STATUS = Status() INFO = Info()
STATUS
monitoring functionFor the spindle speed indicator we need to know the current spindle speed.
For this we register with STATUS
to:
-
Catch the
actual-spindle-speed-changed
signal -
Call the
self.update_spindle()
function
######################## # **** INITIALIZE **** # ######################## # Widgets allow access to widgets from the QtVCP files. # At this point the widgets and HAL pins are not instantiated. def __init__(self,halcomp,widgets,paths): self.hal = halcomp self.w = widgets self.PATHS = paths STATUS.connect('actual-spindle-speed-changed', \ lambda w,speed: self.update_spindle(speed))
We need to make sure the Qt Designer widgets are already built before we try to add to them.
For this, we add a call to self.make_corner_widgets()
function to build our extra widgets at the right time, i.e. under the initialized__()
function:
########################################## # Special Functions called from QtScreen # ########################################## # at this point: # the widgets are instantiated. # the HAL pins are built but HAL is not set ready def initialized__(self): self.make_corner_widgets()
Ok let’s code the function to build the widgets and add them in the tab widget. We are assuming there is a tab widget built with Designer called rightTab.
We are assuming there is a tab widget built with Qt Designer called rightTab
.
##################### # general functions # ##################### def make_corner_widgets(self): # make a spindle-at-speed green LED self.w.led = LED() # <1> self.w.led.setProperty('is_spindle_at_speed_status',True) # <2> self.w.led.setProperty('color',QColor(0,255,0,255)) # <3> self.w.led.hal_init(HAL_NAME = 'spindle_is_at_speed') # <4> # make a spindle speed bar self.w.rpm_bar = QtWidgets.QProgressBar() # <5> self.w.rpm_bar.setRange(0, INFO.MAX_SPINDLE_SPEED) # <6> # container w = QtWidgets.QWidget() # <7> w.setContentsMargins(0,0,0,6) w.setMinimumHeight(40) # layout hbox = QtWidgets.QHBoxLayout() # <8> hbox.addWidget(self.w.rpm_bar) # <9> hbox.addWidget(self.w.led) # <9> w.setLayout(hbox) # add the container to the corner of the right tab widget self.w.rightTab.setCornerWidget(w) # <10>
-
This initializes the basic StateLed widget and uses
self.w.led
as the reference from then on. -
Since the state LED can be used for many indications, we must set the property that designates it as a spindle-at-speed LED.
-
This sets it as green when on.
-
This is the extra function call required with some QtVCP widgets.
IfHAL_NAME
is omitted it will use the widget’sobjectName
if there is one.
It gives the special widgets reference to:-
self.HAL_GCOMP
-
the HAL component instance
-
self.HAL_NAME
-
This widget’s name as a string
-
self.QT_OBJECT_
-
This widget’s PyQt object instance
-
self.QTVCP_INSTANCE_
-
The very top level parent of the screen
-
self.PATHS_
-
The instance of QtVCP’s path library
-
self.PREFS_
-
the instance of an optional preference file
-
self.SETTINGS_
-
the
Qsettings
object
-
-
Initializes a PyQt5
QProgressBar
. -
Sets the max range of the progress bar to the max specified in the
INI
. -
We create a QWidget
Since you can only add one widget to the tab corner and we want two there, we must add both into a container. -
add a QHBoxLayout to the QWidget.
-
Then we add our QProgress bar and LED to the layout.
-
Finally we add the QWidget (with our QProgress bar and LED in it) to the tab widget’s corner.
STATUS
monitoring functionNow we build the function to actually update out the QProgressBar
when STATUS
updates the spindle speed:
######################## # callbacks from STATUS # ######################## def update_spindle(self, data): self.w.rpm_bar.setInvertedAppearance(bool(data<0)) # <1> self.w.rpm_bar.setFormat('{0:d} RPM'.format(int(data))) # <2> self.w.rpm_bar.setValue(abs(data)) # <3>
-
In this case we chose to display left-to-right or right-to-left, depending if we are turning clockwise or anticlockwise.
-
This formats the writing in the bar.
-
This sets the length of the colored bar.
12. Update/Read Objects Periodically
Sometimes you need to update a widget or read a value regularly that isn’t covered by normal libraries.
Here we update an LED based on a watched HAL pin every 100 ms.
We assume there is an LED named led
in the Qt Designer UI file.
Qhal
library for access to QtVCP’s HAL componentIn the IMPORT SECTION
add:
from qtvcp.core import Qhal
Qhal
In the INSTANTIATE LIBRARY SECTION
add:
QHAL = Qhal()
Now add/modify these sections to include code that is similar to this:
CYCLE_TIME
periodThis is usually every 100 ms.
######################## # **** INITIALIZE **** # ######################## # widgets allows access to widgets from the QtVCP files # at this point the widgets and hal pins are not instantiated def __init__(self,halcomp,widgets,paths): self.hal = halcomp self.w = widgets self.PATHS = paths # register a function to be called at CYCLE_TIME period (usually every 100 ms) STATUS.connect('periodic', lambda w: self.update_periodic())
##################### # general functions # ##################### def update_periodic(self): data = QHAL.getvalue('spindle.0.is-oriented') self.w.led.setState(data)
13. External Control With ZMQ
QtVCP can automatically set up ZMQ messaging to send and/or receive remote messages from external programs.
It uses ZMQ’s publish/subscribe messaging pattern.
As always, consider security before letting programs interface though messaging.
13.1. ZMQ Messages Reading
Sometimes you want to control the screen with a separate program.
In the ScreenOptions
widget, you can select the property use_receive_zmq_option
.
You can also set this property directly in the handler file, as in this sample.
We assume the ScreenOptions
widget is called screen_options
in Qt Designer:
######################## # **** INITIALIZE **** # ######################## # widgets allows access to widgets from the QtVCP files # at this point the widgets and hal pins are not instantiated def __init__(self,halcomp,widgets,paths): # directly select ZMQ message receiving self.w.screen_options.setProperty('use_receive_zmq_option',True)
This allows an external program to call functions in the handler file.
Let’s add a specific function for testing. You will need to run LinuxCNC from a terminal to see the printed text.
##################### # general functions # ##################### def test_zmq_function(self, arg1, arg2): print('zmq_test_function called: ', arg1, arg2)
Here is a sample external program to call a function. It alternates between two data sets every second. Run this in a separate terminal from LinuxCNC to see the sent messages.
#!/usr/bin/env python3 from time import sleep import zmq import json context = zmq.Context() socket = context.socket(zmq.PUB) socket.bind("tcp://127.0.0.1:5690") topic = b'QtVCP' # prebuilt message 1 # makes a dict of function to call plus any arguments x = { # <1> "FUNCTION": "test_zmq_function", "ARGS": [True,200] } # convert to JSON object m1 = json.dumps(x) # prebuild message 2 x = { # <1> "FUNCTION": "test_zmq_function", "ARGS": [False,0], } # convert to JSON object m2 = json.dumps(x) if __name__ == '__main__': while True: print('send message 1') socket.send_multipart([topic, bytes((m1).encode('utf-8'))]) sleep(ms(1000)) print('send message 2') socket.send_multipart([topic, bytes((m2).encode('utf-8'))]) sleep(ms(1000))
-
Set the function to call and the arguments to send to that function.
You will need to know the signature of the function you wish to call.
Also note that the message is converted to a JSON object.
This is because ZMQ sends byte messages not Python objects.
json
converts Python objects to bytes and will be converted back when received.
13.2. ZMQ Messages Writing
You may also want to communicate with an external program from the screen.
In the ScreenOptions
widget, you can select the property use_send_zmq_message
.
You can also set this property directly in the handler file, as in this sample.
We assume the ScreenOptions
widget is called screen_options
in Qt Designer:
######################## # **** INITIALIZE **** # ######################## # widgets allows access to widgets from the QtVCP files # at this point the widgets and hal pins are not instantiated def __init__(self, halcomp,widgets,paths): # directly select ZMQ message sending self.w.screen_options.setProperty('use_send_zmq_option',True)
This allows sending messages to a separate program.
The message sent will depend on what the external program is expecting.
Let’s add a specific function for testing.
You will need to run LinuxCNC from a terminal to see the printed text.
Also, something needs to be added to call this function, such as a button click.
##################### # general functions # ##################### def send_zmq_message(self): # This could be any Python object JSON can convert message = {"name": "John", "age": 30} self.w.screen_options.send_zmq_message(message)
Here is a sample program that will receive the message and print it to the terminal:
import zmq import json # ZeroMQ Context context = zmq.Context() # Define the socket using the "Context" sock = context.socket(zmq.SUB) # Define subscription and messages with topic to accept. topic = "" # all topics sock.setsockopt(zmq.SUBSCRIBE, topic) sock.connect("tcp://127.0.0.1:5690") while True: topic, message = sock.recv_multipart() print('{} sent message:{}'.format(topic,json.loads(message)))
14. Sending Messages To Status Bar Or Desktop Notify Dialogs
There are several ways to report information to the user.
A status bar is used for short information to show the user.
Note
|
Not all screens have a status bar. |
self.w.statusbar.showMessage(message, timeout * 1000)
timeout
is in seconds and we assume statusbar
is the Qt Designer set name of the widget.
You can also use the Status
library to send a message to the notify
library if it is enabled (usually set in ScreenOptions
widget):
This will send the message to the statusbar and the desktop notify dialog.
The messages are also recorded until the user erases them using controls. The users can recall any recorded messages.
There are several options:
-
STATUS.TEMPORARY_MESSAGE
-
Show the message for a short time only.
-
STATUS.OPERATOR_ERROR
-
STATUS.OPERATOR_TEXT
-
STATUS.NML_ERROR
-
STATUS.NML_TEXT
-
STATUS.emit('error', STATUS.OPERATOR_ERROR, 'message')
You can send messages thru LinuxCNC’s operator message functions. These are usually caught by the notify system, so are equal to above. They would be printed to the terminal as well.
ACTION.SET_DISPLAY_MESSAGE('MESSAGE') ACTION.SET_ERROR_MESSAGE('MESSAGE')
15. Catch Focus Changes
Focus is used to direct user action such as keyboard entry to the proper widget.
fwidget = QtWidgets.QApplication.focusWidget() if fwidget is not None: print("focus widget class: {} name: {} ".format(fwidget, fwidget.objectName()))
# at this point: # the widgets are instantiated. # the HAL pins are built but HAL is not set ready def initialized__(self): QtWidgets.QApplication.instance().event_filter.focusIn.connect(self.focusInChanged) ##################### # general functions # ##################### def focusInChanged(self, widget): if isinstance(widget.parent(),type(self.w.gcode_editor.editor)): print('G-code Editor') elif isinstance(widget,type(self.w.gcodegraphics)): print('G-code Display') elif isinstance(widget.parent(),type(self.w.mdihistory) ): print('MDI History')
Notice we sometimes compare to widget
, sometimes to widget.parent()
.
This is because some QtVCP widgets are built from multiple sub-widgets and the latter actually get the focus; so we need to check the parent of those sub-widgets.
Other times the main widget is what gets the focus, e.g., the G-code display widget can be set to accept the focus.
In that case there are no sub-widgets in it, so comparing to the widget.parent()
would get you the container that holds the G-code widget.
16. Read Command Line Load Time Options
Some panels need information at load time for setup/options. QtVCP covers this requirement with -o options.
The -o argument is good for a few, relatively short options, that can be added to the loading command line.
For more involved information, reading an INI or preference file is probably a better idea.
Multiple -o options can be used on the command line so you must decode them.
self.w.USEROPTIONS_ will hold any found -o options as a list of strings.
You must parse and define what is accepted and what to do with it.
def initialized__(self): # set a default camera number number = 0 # check if there are any -o options at all if self.w.USEROPTIONS_ is not None: # if in debug mode print the options to the terminal LOG.debug('cam_align user options: {}'.format(self.w.USEROPTIONS_)) # go through the found options one by one for num, i in enumerate(self.w.USEROPTIONS_): # if the -o option has 'size=' in it, assume it's width and height of window # override the default width and height of the window if 'size=' in self.w.USEROPTIONS_[num]: try: strg = self.w.USEROPTIONS_[num].strip('size=') arg = strg.split(',') self.w.resize(int(arg[0]),int(arg[1])) except Exception as e: print('Error with cam_align size setting:',self.w.USEROPTIONS_[num]) # # if the -o option has 'camnumber=' in it, assume it's the camera number to use elif 'camnumber=' in self.w.USEROPTIONS_[num]: try: number = int(self.w.USEROPTIONS_[num].strip('camnumber=')) except Exception as e: print('Error with cam_align camera selection - not a number - using 0') # set the camera number either as default or if -o option changed the 'number' variable, to that number. self.w.camview._camNum = number