linuxcnc/lib/python/gremlin_view.py
Steffen Moeller 2dc8a0d348 src: s/ini/INI/
s/ini/INI/ - smallish correction

Update lib/python/gladevcp/hal_gremlin.py

Co-authored-by: Jérémie Tarot <silopolis@gmail.com>

Update lib/python/gremlin_view.py

Co-authored-by: Jérémie Tarot <silopolis@gmail.com>

Update lib/python/qtvcp/widgets/adjustment_bar.py

Co-authored-by: Jérémie Tarot <silopolis@gmail.com>

Update src/hal/user_comps/vfs11_vfd/vfs11_vfd.c

Co-authored-by: Jérémie Tarot <silopolis@gmail.com>

Update lib/python/qtvcp/widgets/adjustment_bar.py

Co-authored-by: Jérémie Tarot <silopolis@gmail.com>

More 'ini' -> 'INI' in src

ini -> INI to sync tests

Update lib/python/gremlin_view.py

Co-authored-by: Jérémie Tarot <silopolis@gmail.com>

Update lib/python/qtvcp/widgets/offset_tool_button.py

Co-authored-by: Jérémie Tarot <silopolis@gmail.com>

s/modbus/Modbus/

Update lib/python/qtvcp/widgets/offset_tool_button.py

Co-authored-by: Jérémie Tarot <silopolis@gmail.com>

Update scripts/linuxcnc.in

Co-authored-by: Jérémie Tarot <silopolis@gmail.com>

Update src/emc/rs274ngc/interp_namedparams.cc

Co-authored-by: Jérémie Tarot <silopolis@gmail.com>

Update src/emc/usr_intf/emcsh.cc

Co-authored-by: Jérémie Tarot <silopolis@gmail.com>

docs: inifile -> INI file, etc
2022-10-11 19:04:38 +02:00

566 lines
18 KiB
Python

#!/usr/bin/env python3
#------------------------------------------------------------------------------
# Copyright: 2013
# Author: Dewey Garrett <dgarrett@panix.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#------------------------------------------------------------------------------
"""gremlin_view
Provide class: GremlinView for gremlin with buttons for simpler embedding
Standalone functionality if linuxcnc running
A default ui file (gremlin_view.ui) is provided for a default
button arrangement but a user may provide their own by supplying
the glade_file argument.
The following objects are mandatory:
'gremlin_view_window' toplevel window
'gremlin_view_hal_gremlin' hal_gremlin
'gremlin_view_box' HBox or VBox' containing hal_gremlin
Optional radiobutton group names:
'select_p_view'
'select_x_view'
'select_y_view'
'select_z_view'
'select_z2_view'
Optional Checkbuttons names:
'enable_dro'
'show_machine_speed
'show_distance_to_go'
'show_limits'
'show_extents'
'show_tool'
'show_metric'
Callbacks are provided for the following buttons actions
on_clear_live_plotter_clicked
on_enable_dro_clicked
on_zoomin_pressed
on_zoomout_pressed
on_pan_x_minus_pressed
on_pan_x_plus_pressed
on_pan_y_minus_pressed
on_pan_y_plus_pressed
on_show_tool_clicked
on_show_metric_clicked
on_show_extents_clicked
on_select_p_view_clicked
on_select_x_view_clicked
on_select_y_view_clicked
on_select_z_view_clicked
on_select_z2_view_clicked
on_show_distance_to_go_clicked
on_show_machine_speed_clicked
on_show_limits_clicked
"""
import gi
gi.require_version("Gtk","3.0")
from gi.repository import Gtk
from gi.repository import GObject
from gi.repository import GLib
import os
import sys
import gladevcp.hal_actions # reqd for Builder
import linuxcnc
import time
import subprocess
import gettext
import datetime
import glib # for glib.GError
g_ui_dir = linuxcnc.SHARE + "/linuxcnc"
g_periodic_secs = 1 # integer
g_delta_pixels = 10
g_move_delay_secs = 0.2
g_progname = os.path.basename(sys.argv[0])
g_verbose = False
LOCALEDIR = linuxcnc.SHARE + "/locale"
gettext.install("linuxcnc", localedir=LOCALEDIR)
def ini_check ():
"""set environmental variable and change directory"""
# Note:
# hal_gremlin gets INI file from os.environ (only)
# hal_gremlin expects cwd to be same as INI file
ini_filename = get_linuxcnc_ini_file()
if ini_filename is not None:
os.putenv('INI_FILE_NAME',ini_filename) # ineffective
os.environ['INI_FILE_NAME'] = ini_filename # need for hal_gremlin
os.chdir(os.path.dirname(ini_filename))
if g_verbose:
print(('ini_check: INI_FILENAME= %s' % ini_filename))
print(('ini_check: curdir= %s' % os.path.curdir))
return True # success
print((_('%s: LinuxCNC INI file not available') % g_progname))
return False # exit here crashes glade-gtk2
def get_linuxcnc_ini_file():
"""find LinuxCNC INI file with pgrep"""
ps = subprocess.Popen('ps -C linuxcncsvr --no-header -o args'.split(),
stdout=subprocess.PIPE
)
p,e = ps.communicate()
if p is not None: p=p.decode()
if e is not None: e=e.decode()
if ps.returncode:
print(_('get_linuxcnc_ini_file: stdout= %s') % p)
print(_('get_linuxcnc_ini_file: stderr= %s') % e)
return None
ans = p.split()[p.split().index('-ini')+1]
return ans
class GremlinView():
"""Implement a standalone gremlin with some buttons
and provide means to embed using a glade ui file"""
def __init__(self
,glade_file=None #None: use default ui
,parent=None
,width=None
,height=None
,alive=True
,gtk_theme_name="Follow System Theme"
):
self.alive = alive
linuxcnc_running = False
if ini_check():
linuxcnc_running = True
if not linuxcnc_running:
print("gremlin_view: LinuxCNC must be running")
sys.exit(1)
if (glade_file == None):
glade_file = os.path.join(g_ui_dir,'gremlin_view.ui')
print("gremlin_view glade_file=",glade_file)
bldr = Gtk.Builder()
try:
bldr.add_from_file(glade_file)
except glib.GError as detail:
print('\nGremlinView:%s\n' % detail)
raise glib.GError(detail) # re-raise
# required objects:
self.topwindow = bldr.get_object('gremlin_view_window')
self.gbox = bldr.get_object('gremlin_view_box')
self.halg = bldr.get_object('gremlin_view_hal_gremlin')
#self.halg.show_lathe_radius = 1 # for test, hal_gremlin default is Dia
if not linuxcnc_running:
# blanks display area:
self.halg.set_has_window(False)
# radiobuttons for selecting view: (expect at least one)
select_view_letters = ['p','x','y','z','z2']
for vletter in select_view_letters:
try:
obj = bldr.get_object('select_' + vletter + '_view')
self.my_view = vletter # first letter found is initial view
break
except:
continue
check_button_objects = ['enable_dro'
,'show_machine_speed'
,'show_distance_to_go'
,'show_limits'
,'show_extents'
,'show_tool'
,'show_metric'
]
for objname in check_button_objects:
obj = bldr.get_object(objname)
if obj is not None:
setattr(self,'objname',obj)
obj.set_active(True)
else:
if g_verbose:
print('%s: Optional object omitted <%s>'
% (__file__,objname))
# show_metric: use INI file
#FIXME show_metric,lunits s/b mandatory?
try:
objname = 'show_metric'
self.show_metric = bldr.get_object('show_metric')
lunits = self.halg.inifile.find('TRAJ','LINEAR_UNITS')
except AttributeError:
if g_verbose:
print('%s: Problem for <%s>' % (__file__,objname))
if linuxcnc_running:
if lunits == 'inch':
self.halg.metric_units = False
elif lunits == 'mm':
self.halg.metric_units = True
else:
raise AttributeError('%s: unknown [TRAJ]LINEAR_UNITS] <%s>'
% (__file__,lunits))
if self.halg.get_show_metric():
self.show_metric.set_active(True)
else:
self.show_metric.set_active(False)
if alive:
bldr.connect_signals(self)
# todo: to remove other signals on halg:
# bldr.disconnect(integer_handle_id)
# bldr.disconnect_by_func('func_name')
# bldr.handler_disconnect()
minwidth = 300 # smallest size
minheight = 300 # smallest size
if (width is None):
width = minwidth
else:
width = int(width)
if (height is None):
height = minheight
else:
height = int(height)
if width < minwidth:
width = minwidth
if height < minheight:
height = minheight
# err from gremlin if omit this
self.halg.width = width
self.halg.height = height
self.halg.set_size_request(width,height)
# self.x,self.y used in conjunction with pan buttons
# but using mouse may change internal values in gremlin
# resulting in unexpected movement if both mouse and
# pan buttons are used
self.x = 0
self.y = 0
# prevent flashing topwindow
self.topwindow.iconify()
self.topwindow.show_all()
self.topwindow.hide()
self.preview_file(None)
if linuxcnc_running:
try:
self.preview_file(None)
except linuxcnc.error as detail:
print('linuxcnc.error')
print(' detail=',detail)
try:
self.last_file = self.halg._current_file
except AttributeError:
self.last_file = None
self.last_file_mtime = None
self.parent = parent
if self.parent is None:
# topwindow (standalone) application
# print "TOP:",gtk_theme_name
screen = self.topwindow.get_screen()
else:
# print "REPARENT:",gtk_theme_name
screen = self.halg.get_screen()
# not valid for Gtk3:
# settings = Gtk.settings_get_for_screen(screen)
# systname = settings.get_property("gtk-theme-name")
# if ( (gtk_theme_name is None)
# or (gtk_theme_name == "")
# or (gtk_theme_name == "Follow System Theme")):
# gtk_theme_name = systname
# settings.set_string_property('gtk-theme-name',gtk_theme_name,"")
self.topwindow.connect('destroy',self._topwindowquit)
self.topwindow.show_all()
self.running = True
if self.last_file is not None:
self.topwindow.set_title(g_progname
+ ': ' + os.path.basename(self.last_file))
self.last_file_mtime = datetime.datetime.fromtimestamp(
os.path.getmtime(self.last_file))
self.ct = 0
if self.parent is None: self.topwindow.deiconify()
self._periodic('BEGIN')
GLib.timeout_add_seconds(g_periodic_secs,self._periodic,'Continue')
# or use GLib.timeout_add() interval units in mS
def _periodic(self,arg):
# print "_periodic:",self.ct,arg
self.ct +=1
self.halg.poll()
if (self.parent is not None) and (self.ct) == 2:
# not sure why delay is needed for reparenting
# but without, the display of the (rgb) axes
# and the cone to not appear in gremlin
# print "REPARENT:",self.gbox, self.parent
#-----------------------------------------------------------------------------
# determine if glade interface designer is running
# to avoid assertion error:
# gtk_widget_reparent_fixup_child: assertion failed: (client_data != NULL)
is_glade = False
if 'glade' in sys.argv[0] and 'gladevcp' not in sys.argv[0]:
for d in os.environ['PATH'].split(':'):
f = os.path.join(d,sys.argv[0])
if ( os.path.isfile(f)
and os.access(f, os.X_OK)):
is_glade = True
break
#-----------------------------------------------------------------------------
if (not is_glade):
self.gbox.reparent(self.parent)
self.gbox.show_all()
self.gbox.connect('destroy',self._gboxquit)
return True
try:
current_file = self.halg._current_file
except AttributeError:
current_file = None
if current_file is None:
return True # keep trying _periodic()
current_file_mtime = datetime.datetime.fromtimestamp(
os.path.getmtime(current_file))
if ( current_file != self.last_file
or current_file_mtime != self.last_file_mtime):
# print('old,new',self.last_file_mtime,current_file_mtime)
self.last_file = current_file
self.last_file_mtime = current_file_mtime
self.halg.hide()
self.halg.load()
getattr(self.halg,'set_view_%s' % self.my_view)()
self.halg.show()
if self.topwindow is not None:
self.topwindow.set_title(g_progname
+ ': ' + os.path.basename(self.last_file))
return True # repeat _periodic()
def preview_file(self,filename):
self.halg.hide()
# handle exception in case glade is running
try:
self.halg.load(filename or None)
except Exception as detail:
if self.alive:
print("file load fail:",Exception,detail)
pass
getattr(self.halg,'set_view_%s' % self.my_view)()
self.halg.show()
def _gboxquit(self,w):
self.running = False # stop periodic checks
def _topwindowquit(self,w):
self.running = False # stop periodic checks
Gtk.main_quit()
def expose(self):
self.halg.expose()
def on_zoomin_pressed(self,w):
while w.get_state() == Gtk.StateType.ACTIVE:
self.halg.zoomin()
time.sleep(g_move_delay_secs)
Gtk.main_iteration_do(blocking=0)
def on_zoomout_pressed(self,w):
while w.get_state() == Gtk.StateType.ACTIVE:
self.halg.zoomout()
time.sleep(g_move_delay_secs)
Gtk.main_iteration_do(blocking=0)
def on_pan_x_minus_pressed(self,w):
while w.get_state() == Gtk.StateType.ACTIVE:
self.x -= g_delta_pixels
self.halg.translate(self.x,self.y)
time.sleep(g_move_delay_secs)
Gtk.main_iteration_do(blocking=0)
def on_pan_x_plus_pressed(self,w):
while w.get_state() == Gtk.StateType.ACTIVE:
self.x += g_delta_pixels
self.halg.translate(self.x,self.y)
time.sleep(g_move_delay_secs)
Gtk.main_iteration_do(blocking=0)
def on_pan_y_minus_pressed(self,w):
while w.get_state() == Gtk.StateType.ACTIVE:
self.y += g_delta_pixels
self.halg.translate(self.x,self.y)
time.sleep(g_move_delay_secs)
Gtk.main_iteration_do(blocking=0)
def on_pan_y_plus_pressed(self,w):
while w.get_state() == Gtk.StateType.ACTIVE:
self.y -= g_delta_pixels
self.halg.translate(self.x,self.y)
time.sleep(g_move_delay_secs)
Gtk.main_iteration_do(blocking=0)
def on_clear_live_plotter_clicked(self,w):
self.halg.clear_live_plotter()
def on_enable_dro_clicked(self,w):
if w.get_active():
self.halg.enable_dro = True
else:
self.halg.enable_dro = False
self.expose()
def on_show_machine_speed_clicked(self,w):
if w.get_active():
self.halg.show_velocity = True
else:
self.halg.show_velocity = False
self.expose()
def on_show_distance_to_go_clicked(self,w):
if w.get_active():
self.halg.show_dtg = True
else:
self.halg.show_dtg = False
self.expose()
def on_show_limits_clicked(self,w):
if w.get_active():
self.halg.show_limits = True
else:
self.halg.show_limits = False
self.expose()
def on_show_extents_clicked(self,w):
if w.get_active():
self.halg.show_extents_option = True
else:
self.halg.show_extents_option = False
self.expose()
def on_show_tool_clicked(self,w):
if w.get_active():
self.halg.show_tool = True
else:
self.halg.show_tool = False
self.expose()
def on_show_metric_clicked(self,w):
if w.get_active():
self.halg.metric_units = True
else:
self.halg.metric_units = False
self.expose()
def on_select_p_view_clicked(self,w):
self.set_view_per_w(w,'p')
def on_select_x_view_clicked(self,w):
self.set_view_per_w(w,'x')
def on_select_y_view_clicked(self,w):
self.set_view_per_w(w,'y')
def on_select_z_view_clicked(self,w):
self.set_view_per_w(w,'z')
def on_select_z2_view_clicked(self,w):
self.set_view_per_w(w,'z2')
def set_view_per_w(self,w,vletter):
self.my_view = vletter
getattr(self.halg,'set_view_%s' % vletter)()
#-----------------------------------------------------------------------------
# Standalone (and demo) usage:
def standalone_gremlin_view():
import getopt
#---------------------------------------
def usage(msg=None):
print(("""\n
Usage: %s [options]\n
Options: [-h | --help]
[-v | --verbose]
[-W | --width] width
[-H | --height] height
[-f | --file] glade_file
Note: linuxcnc must be running on same machine
""") % g_progname)
if msg:
print('\n%s' % msg)
#---------------------------------------
glade_file = None
width = None
height = None
vbose = False
try:
options,remainder = getopt.getopt(sys.argv[1:]
, 'f:hH:vW:'
, ['file='
,'help'
,'width='
,'height='
]
)
except getopt.GetoptError as msg:
usage()
print('GetoptError: %s' % msg)
sys.exit(1)
for opt,arg in options:
if opt in ('-h','--help'):
usage(),sys.exit(0)
if opt in ('-v','--verbose'):
g_verbose = True
continue
if opt in ('-W','--width' ): width=arg
if opt in ('-H','--height'): height=arg
if opt in ('-f','--file'): glade_file=arg
if remainder:
usage('unknown argument:%s' % remainder)
sys.exit(1)
try:
g = GremlinView(glade_file=glade_file
,width=width
,height=height
)
Gtk.main()
except linuxcnc.error as detail:
Gtk.main()
print('linuxcnc.error:',detail)
usage()
# vim: sts=4 sw=4 et