297 lines
10 KiB
Python
Executable file
297 lines
10 KiB
Python
Executable file
#!/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.
|
|
#------------------------------------------------------------------------------
|
|
|
|
"""
|
|
# popup keyboard for use with touchscreen applications
|
|
# used by pyngcgui.py
|
|
# based on work of John Thornton's Buglump
|
|
|
|
Optional __init__() args:
|
|
glade_file (default = 'popupkeyboard.ui')
|
|
dialog_name (default = 'dialog') Main window
|
|
num_entry_name (default = 'num_entry') Entry for display
|
|
coord_buttons_name (default = 'coords') Box for coord buttons
|
|
use_coord_buttons (default = True) Enable coord buttons
|
|
|
|
Required objects for glade_file:
|
|
Gtk.Window (Main window)
|
|
Gtk.Entry (Entry for display)
|
|
|
|
Optional objects for galde_file:
|
|
Gtk.*Box: (Box for coord buttons)
|
|
|
|
All buttons use a single handler named 'on_click'.
|
|
The PopupKeyboard class recognizes buttons by their LABEL.
|
|
|
|
Required button LABELS (case insensitive):
|
|
0,1,2,3,4,5,6,7,8,9,. (numbers and decimal point)
|
|
+/- (toggle sign)
|
|
clear (clear)
|
|
bs (back space)
|
|
save (save)
|
|
cancel (cancel)
|
|
Optional button LABELS (case insensitive):
|
|
x,y,z,a,b,c,u,v,w,d (9 axes plus d for diameter)
|
|
|
|
"""
|
|
|
|
import linuxcnc
|
|
import sys
|
|
import os
|
|
import gi
|
|
gi.require_version('Pango', '1.0')
|
|
from gi.repository import Pango
|
|
gi.require_version('Gtk', '3.0')
|
|
from gi.repository import Gtk
|
|
from gi.repository import GObject
|
|
g_ui_dir = linuxcnc.SHARE + "/linuxcnc"
|
|
|
|
class PopupKeyboard:
|
|
def __init__(self
|
|
,glade_file=None
|
|
,dialog_name='dialog'
|
|
,num_entry_name='num_entry'
|
|
,coord_buttons_name='coords'
|
|
,use_coord_buttons=True
|
|
,theme_name=None
|
|
):
|
|
|
|
if (glade_file is None):
|
|
glade_file = os.path.join(g_ui_dir,'popupkeyboard.ui')
|
|
|
|
fontname ='sans 12 bold'
|
|
self.use_coord_buttons = use_coord_buttons
|
|
|
|
self.builder = Gtk.Builder()
|
|
self.builder.add_from_file(glade_file)
|
|
self.builder.connect_signals(self)
|
|
|
|
self.dialog = self.builder.get_object(dialog_name)
|
|
self.set_theme(theme_name)
|
|
self.num_entry = self.builder.get_object(num_entry_name)
|
|
try:
|
|
self.coord_buttons = self.builder.get_object(coord_buttons_name)
|
|
except:
|
|
self.coord_buttons = None
|
|
|
|
self.result = None
|
|
self.location = None
|
|
|
|
self.top = self.dialog.get_toplevel()
|
|
self.top.set_title(glade_file)
|
|
self.top.set_keep_above(True)
|
|
|
|
if (not self.use_coord_buttons):
|
|
if self.coord_buttons:
|
|
self.coord_buttons.hide()
|
|
|
|
# prevent closing of dialog by window manager, escape key , etc
|
|
# http://faq.pygtk.org/index.py?file=faq10.013.htp&req=show
|
|
self.top.connect("response", self.on_response) #reqd
|
|
self.top.connect("delete-event",self.on_delete) #reqd
|
|
self.top.connect("close", self.on_close) #probably not reqd
|
|
|
|
# find buttons with labels XYZABCUVW or D
|
|
# and show iff corresponding axis is in axis_mask
|
|
self.label_to_btn = {}
|
|
for btn in self.builder.get_objects():
|
|
if type(btn) is not Gtk.Button:
|
|
continue
|
|
self.label_to_btn[btn.get_label().upper()] = btn
|
|
|
|
#not working gtk3:
|
|
#if isinstance(btn.child, Gtk.Label):
|
|
# lbl = btn.child
|
|
# lbl.modify_font(Pango.FontDescription(fontname))
|
|
|
|
if use_coord_buttons: self.support_coord_buttons()
|
|
|
|
# making it insensitive clears the initial selection region
|
|
#deprecated in gtk3:
|
|
#self.num_entry.set_state(Gtk.StateType.INSENSITIVE)
|
|
#self.num_entry.modify_text(Gtk.StateType.INSENSITIVE
|
|
# ,Gtk.gdk.color_parse('black'))
|
|
#self.num_entry.modify_font(Pango.FontDescription(fontname))
|
|
|
|
def support_coord_buttons(self):
|
|
try:
|
|
self.stat = linuxcnc.stat()
|
|
self.stat.poll()
|
|
has_x = False
|
|
for axno in range(0,9):
|
|
axname = 'XYZABCUVW'[axno]
|
|
if axname in self.label_to_btn:
|
|
b = self.label_to_btn[axname]
|
|
if bool(self.stat.axis_mask & (1 << axno)):
|
|
b.show()
|
|
if axno == 0: has_x = True
|
|
else:
|
|
b.hide()
|
|
bdiam = None
|
|
if 'D' in self.label_to_btn:
|
|
bdiam = self.label_to_btn['D']
|
|
if bdiam and has_x:
|
|
bdiam.show()
|
|
elif bdiam:
|
|
bdiam.hide()
|
|
except linuxcnc.error as msg:
|
|
self.stat = None
|
|
if self.coord_buttons is not None:
|
|
self.coord_buttons.hide()
|
|
print("linuxcnc must be running to use coordinate keys")
|
|
# continue without buttons for testing when linuxnc not running
|
|
except Exception as err:
|
|
print('Exception:',Exception)
|
|
print(sys.exc_info())
|
|
sys.exit(1)
|
|
|
|
|
|
def set_theme(self,tname=None):
|
|
if tname is None:
|
|
return
|
|
screen = self.dialog.get_screen()
|
|
#wip: gtk3 notworking
|
|
#settings = Gtk.settings_get_for_screen(screen)
|
|
#settings.set_string_property('gtk-theme-name',tname,"")
|
|
#theme = settings.get_property('gtk-theme-name')
|
|
|
|
# prevent closing of dialog by window manager, escape key , etc
|
|
def on_response(self,widget,response):
|
|
if response < 0:
|
|
GObject.signal_stop_emission_by_name(widget,'response')
|
|
return True
|
|
|
|
def on_delete(self,widget,event): return True
|
|
|
|
def on_close(self,widget,event): return True
|
|
|
|
def run(self,initial_value='',title=None):
|
|
if title is not None:
|
|
self.top.set_title(title)
|
|
self.num_entry.set_text(str(initial_value))
|
|
if self.location:
|
|
#Gtk3:deprecated:
|
|
#self.dialog.parse_geometry('+%d+%d'
|
|
# % (self.location[0],self.location[1]))
|
|
pass
|
|
self.num_entry.set_position(0)
|
|
self.dialog.run()
|
|
if self.result is None:
|
|
return False #user canceled
|
|
else:
|
|
return True
|
|
|
|
def on_click(self, widget, data=None):
|
|
l = widget.get_label()
|
|
e = self.num_entry
|
|
self.label_to_method(l)(e,l)
|
|
|
|
def label_to_method(self,l):
|
|
ll = l.lower()
|
|
if ll.find('clear') >= 0: return self.do_clear
|
|
if ll.find('save') >= 0: return self.do_save
|
|
if ll.find('cancel')>= 0: return self.do_cancel
|
|
if ll.find('+/-') >= 0: return self.do_sign
|
|
if ll.find('bs') >= 0: return self.do_backspace
|
|
if ll in ('.0123456789'): return self.do_number
|
|
if ll in ('xyzabcuvwd'): return self.do_axis_letter
|
|
return self.do_unknown_label
|
|
|
|
def do_unknown_label(self,e,l):
|
|
print('PopupKeyboard:do_unknown_label: <%s>' % l)
|
|
|
|
def do_number(self,e,l):
|
|
# not needed if INSENSITIVE:
|
|
# if e.get_selection_bounds(): e.delete_selection()
|
|
e.set_text(e.get_text() + l)
|
|
|
|
def do_backspace(self,e,l):
|
|
e.set_text(e.get_text()[:-1])
|
|
|
|
def do_sign(self,e,l):
|
|
current = e.get_text()
|
|
if current == '':
|
|
current = '-'
|
|
elif current[0] == '-':
|
|
current = current[1:]
|
|
else:
|
|
current = '-' + current
|
|
e.set_text(current)
|
|
|
|
def do_save(self,e,l):
|
|
self.result = e.get_text()
|
|
e.set_text('')
|
|
self.location = self.dialog.get_position()
|
|
self.dialog.hide() # tells dialog.run() to finish
|
|
|
|
def do_cancel(self,e,l):
|
|
self.result = None # None means canceled
|
|
self.dialog.hide() # tells dialog.run() to finish
|
|
|
|
def do_clear(self,e,l):
|
|
e.set_text('')
|
|
|
|
def do_axis_letter(self,e,l):
|
|
if self.stat:
|
|
self.stat.poll()
|
|
e.set_text("%.6g" % self.coord_value(l))
|
|
else:
|
|
print("linuxcnc must be running to use <%s> key" % l)
|
|
|
|
def get_result(self):
|
|
return(self.result)
|
|
|
|
def coord_value(self,char):
|
|
# offset calc copied from emc_interface.py
|
|
# char = 'x' | 'y' | ...
|
|
# 'd' is for diameter
|
|
s = self.stat
|
|
s.poll()
|
|
p = s.position # tuple=(xvalue, yvalue, ...
|
|
return {
|
|
'x': p[0] - s.g5x_offset[0] - s.tool_offset[0],
|
|
'y': p[1] - s.g5x_offset[1] - s.tool_offset[1],
|
|
'z': p[2] - s.g5x_offset[2] - s.tool_offset[2],
|
|
'a': p[3] - s.g5x_offset[3] - s.tool_offset[3],
|
|
'b': p[4] - s.g5x_offset[4] - s.tool_offset[4],
|
|
'c': p[5] - s.g5x_offset[5] - s.tool_offset[5],
|
|
'u': p[6] - s.g5x_offset[6] - s.tool_offset[6],
|
|
'v': p[7] - s.g5x_offset[7] - s.tool_offset[7],
|
|
'w': p[8] - s.g5x_offset[8] - s.tool_offset[8],
|
|
'd':(p[0] - s.g5x_offset[0] - s.tool_offset[0])* 2,#2*R
|
|
}[char.lower()]
|
|
|
|
|
|
if __name__ == "__main__":
|
|
m = PopupKeyboard()
|
|
print("\nClear and Save to end test\n")
|
|
ct = 100
|
|
while True:
|
|
m.run(initial_value=''
|
|
,title=str(ct)
|
|
)
|
|
result = m.get_result()
|
|
print('result = <%s>' % result)
|
|
if result=='':
|
|
sys.exit(0)
|
|
ct += 1
|
|
Gtk.main()
|
|
# vim: sts=4 sw=4 et
|