Files
linuxcncAllWasm/lib/python/qtvcp/widgets/dialog_widget.py
2026-08-20 06:17:39 +08:00

2285 lines
82 KiB
Python

#!/usr/bin/env python3
# QTVcp Widget
#
# Copyright (c) 2017 Chris Morley
#
# 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.
import os
import hal
from qtpy.QtWidgets import (QMessageBox, QFileDialog,
QDialog, QDialogButtonBox, QVBoxLayout, QPushButton, QHBoxLayout,
QHBoxLayout, QLineEdit, QPushButton, QDialogButtonBox, QTabWidget,
QTextEdit,QLabel, QApplication)
from qtpy.QtGui import QColor
from qtpy.QtCore import Qt, Slot, Property, QEvent, QUrl, QTimer
from qtpy import uic
from qtvcp.widgets.widget_baseclass import _HalWidgetBase, hal
from qtvcp.widgets.origin_offsetview import OriginOffsetView as OFFVIEW_WIDGET
from qtvcp.widgets.tool_offsetview import ToolOffsetView as TOOLVIEW_WIDGET
from qtvcp.widgets.tool_chooser import ToolChooser as TOOLCHOOSER_WIDGET
from qtvcp.widgets.macro_widget import MacroTab
from qtvcp.widgets.versa_probe import VersaProbe
from qtvcp.widgets.entry_widget import SoftInputWidget
from qtvcp.widgets.virtualkeyboard import VirtualKeyboard
from qtvcp.widgets.calculator import Calculator
from qtvcp.widgets.machine_log import MachineLog
from qtvcp.lib.notify import Notify
from qtvcp.widgets.dialogMixin import GeometryMixin
from qtvcp.core import Status, Action, Info, Tool, Path
from qtvcp import logger
# Instantiate the libraries with global reference
# STATUS gives us status messages from linuxcnc
# ACTION gives commands to linuxcnc
# INFO holds INI file details
# TOOL gives tool file access
# NOTICE is for the desktop notify system
# LOG is for running code logging
STATUS = Status()
ACTION = Action()
INFO = Info()
TOOL = Tool()
PATH = Path()
NOTICE = Notify()
LOG = logger.getLogger(__name__)
# Force the log level for this module
#LOG.setLevel(logger.DEBUG) # One of DEBUG, INFO, WARNING, ERROR, CRITICAL
#########################################
# GeometryMixin helper class
#########################################
# adds geometry class and _HalWidgetBase class into the dialogs.
# the geometry class parses the geometry string and places
# the dialog based on what it finds.
# it needed to be in a separate file do to circular imports
# found when using versaProbe
################################################################################
# Generic messagebox Dialog
################################################################################
class LcncDialog(QMessageBox, GeometryMixin):
OK = 'OK'
NONE = 'NONE'
YESNO = 'YESNO'
OKCANCEL = 'OKCANCEL'
CLOSEPROMPT = 'CLOSEPROMPT'
def __init__(self, parent=None):
super(LcncDialog, self).__init__(parent)
self.setTextFormat(Qt.RichText)
self.setText('<b>Sample Text?</b>')
self.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
self.setIcon(QMessageBox.Critical)
self.setDetailedText('')
self.mtype = 'OK'
self._possibleTypes = ('OK','YESNO','OKCANCEL','CLOSEPROMPT','NONE')
self._state = False
self._color = QColor(0, 0, 0, 150)
self._request_name = 'MESSAGE'
self._nblock = False
self._message = None
self._return_callback = None
self._pinname = None
self._title = 'Message Dialog'
self._forcedFlag = True
self._use_exec = False
self._geoName = 'LncMessage-geometry'
self.set_default_geometry()
self.hide()
self.buttonClicked.connect(self.btn_callback)
self.timer = QTimer()
self.seconds_left = 0
self.timer.timeout.connect(self.update_timer)
def _hal_init(self):
self.read_preference_geometry(self._geoName)
STATUS.connect('dialog-request', self._external_request)
STATUS.connect('dialog-update', self._external_update)
# this processes STATUS called dialog requests
# We check the cmd to see if it was for us
# then we check for a id string
# if all good show the dialog
# and then send back the dialog response via a general message
def _external_request(self, w, message):
self._message = message
if message.get('NAME') == self._request_name:
# request to close dialog early
if message.get('CLOSE'):
self.close()
STATUS.emit('focus-overlay-changed', False, None, None)
return
geo = message.get('GEONAME') or 'LncMessage-geometry'
t = message.get('TITLE')
if not t:
t = 'Message Dialog'
messtext = message.get('MESSAGE') or None
more = message.get('MORE') or None
details = message.get('DETAILS') or None
mtype = message.get('TYPE')
if mtype is None: mtype = 'OK'
icon = message.get('ICON') or 'INFO'
pin = message.get('PINNAME')
ftext = message.get('FOCUSTEXT')
fcolor = message.get('FOCUSCOLOR')
alert = message.get('PLAYALERT')
nblock = message.get('NONBLOCKING')
forceOpen = message.get('NONBLOCKING')
callback = message.get('CALLBACK') # this needs testing
self.showdialog(messtext, more, details, mtype,
icon, pin, ftext, fcolor, alert,
nblock, geoname=geo,title = t, return_callback = callback,
force_open = forceOpen)
# This actually builds and displays the dialog.
# there are three ways to get results:
# - through a return by status message (return_callback = None, use_exec = False)
# - callback return (return_callback = function_name)
# - by direct return statement (use_exec = True)
def showdialog(self, messagetext, more_info=None, details=None, display_type='OK',
icon=QMessageBox.Information, pinname=None, focus_text=None,
focus_color=None, play_alert=None, nblock=False,
return_callback = None, flags = None, setflags = None,
title = None, use_exec = False,geoname=None,
force_open = None, timer=0):
self._pinname = pinname
self._nblock = nblock
self._return_callback = return_callback
self._use_exec = use_exec
# block response to main window?
if nblock:
self.setWindowModality(Qt.NonModal)
else:
self.setWindowModality(Qt.ApplicationModal)
# set standard flags or allow external selection
if flags is None:
if nblock:
self.setWindowFlags(self.windowFlags() | Qt.Tool |
Qt.Dialog | Qt.WindowStaysOnTopHint
| Qt.WindowSystemMenuHint)
if display_type == LcncDialog.NONE:
self.setWindowFlags(self.windowFlags() | Qt.CustomizeWindowHint)
self.setWindowFlag(Qt.WindowCloseButtonHint, False)
else:
self.setWindowFlags(self.windowFlags() | Qt.Tool |
Qt.FramelessWindowHint | Qt.Dialog |
Qt.WindowStaysOnTopHint | Qt.WindowSystemMenuHint)
else:
self.setWindowFlags(self.windowFlags() | flags)
# allow external setting of flags - probably hide close button
if not setflags is None:
for i in setflags:
self.setWindowFlag(i,setflags[i])
self.setWindowTitle(title)
if focus_color is not None:
color = focus_color
else:
color = self._color
# convert text descriptions to actual icons
if icon == 'QUESTION': icon = QMessageBox.Question
elif icon == 'WARNING': icon = QMessageBox.Warning
elif icon == 'CRITICAL': icon = QMessageBox.Critical
elif icon == 'INFO' or isinstance(icon,str): icon = QMessageBox.Information
self.setIcon(icon)
self.setText('<b>%s</b>' % messagetext)
if more_info is not None:
self.setInformativeText(more_info)
else:
self.setInformativeText('')
if details is not None:
self.setDetailedText(details)
else:
self.setDetailedText('')
# convert display type text to buttons layouts
display_type = display_type.upper()
if display_type not in self._possibleTypes:
display_type = LcncDialog.OK
if display_type == LcncDialog.OK:
self.setStandardButtons(QMessageBox.Ok)
elif display_type == LcncDialog.YESNO:
self.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
elif display_type == LcncDialog.OKCANCEL:
self.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
elif display_type == LcncDialog.NONE:
self.setStandardButtons(QMessageBox.NoButton)
if not nblock:
STATUS.emit('focus-overlay-changed', True, focus_text, color)
if play_alert:
STATUS.emit('play-sound', play_alert)
if geoname is None:
geoname = self._geoName
self.read_preference_geometry(geoname)
self.show()
if not force_open is None:
self._forcedFlag = force_open
self.forceDetailsOpen()
if timer:
self.seconds_left = timer
self.timer.start(1000)
if use_exec:
retval = self.exec()
STATUS.emit('focus-overlay-changed', False, None, None)
LOG.debug('Value of pressed button: {}'.format(retval))
return self.qualifiedReturn(retval)
# hack to force details box to present open on first display
def forceDetailsOpen(self):
if self._forcedFlag: return
try:
# force the details box open on first time display
for i in self.buttons():
if self.buttonRole(i) == QMessageBox.ActionRole:
for j in self.children():
for k in j.children():
if isinstance( k, QTextEdit):
#i.hide()
if not k.isVisible():
i.click()
except Exception as e:
print(e)
pass
self._forcedFlag = True
def qualifiedReturn(self, retval):
if retval in(QMessageBox.No, QMessageBox.Cancel):
return False
elif retval in(QMessageBox.Ok, QMessageBox.Yes):
return True
else:
return self.buttonRole(self.clickedButton())
# move dialog when shown
def showEvent(self, event):
self.set_geometry()
super(LcncDialog, self).showEvent(event)
return
if self._nblock:
self.set_geometry()
else:
geom = self.frameGeometry()
geom.moveCenter(QApplication.primaryScreen().availableGeometry().center())
self.setGeometry(geom)
super(LcncDialog, self).showEvent(event)
def update_timer(self):
self.seconds_left -= 1
if self.seconds_left > 0:
title = f"Closing in: {self.seconds_left}s"
self.setText('<b>%s</b>' % title)
else:
self.timer.stop()
self.accept() # Close dialog when done
def btn_callback(self, i):
LOG.debug('Button pressed is: {}'.format(i.text()))
# update the dialog position
self.record_geometry()
if self._use_exec:
return
self.hide()
btn = self.standardButton(self.clickedButton())
result = self.qualifiedReturn(btn)
LOG.debug('Value of {} pressed button: {}'.format(self, result))
self.process_result(result)
def process_result(self, result):
self.timer.stop()
# these directly call a function with btn info
if not self._return_callback is None:
self._return_callback(self, result)
# these return via status messages
elif self._message is not None:
self._message['RETURN'] = result
STATUS.emit('general', self._message)
STATUS.emit('focus-overlay-changed', False, None, None)
self._message = None
# just return result
else:
return result
def setGeometry(self,*args):
#print(args,len(args))
if len(args) == 1:
super().setGeometry(args[0])
width = args[0].width()
else:
super().setGeometry(args[0],args[1],args[2],args[3])
width = args[2]
if isinstance(self,QMessageBox) and width < 200:
if isinstance(self,CloseDialog):
self.findChild(QLabel, "qt_msgbox_label").setFixedWidth(200)
else:
self.findChild(QLabel, "qt_msgbox_label").setFixedWidth(350)
# callback from status 'update-dialog'
def _external_update(self, w, message):
if message.get('NAME') == self._request_name:
if not self.isVisible(): return
response = message.get('response')
if not response is None:
LOG.debug('Response is: {}'.format(response))
# update the dialog position
self.record_geometry()
self.hide()
if response == 0:
self.process_result(False)
elif response == 1:
self.process_result(True)
# **********************
# Designer properties
# **********************
@Slot(bool)
def setState(self, value):
self._state = value
if value:
self.show()
else:
self.hide()
def getState(self):
return self._state
def resetState(self):
self._state = False
def getColor(self):
return self._color
def setColor(self, value):
self._color = value
def resetState(self):
self._color = QColor(0, 0, 0, 150)
def getIdName(self):
return self._request_name
def setIdName(self, name):
self._request_name = name
def resetIdName(self):
self._request_name = 'MESSAGE'
overlay_color = Property(QColor, getColor, setColor)
state = Property(bool, getState, setState, resetState)
launch_id = Property(str, getIdName, setIdName, resetIdName)
################################################################################
# Close Dialog
################################################################################
class CloseDialog(LcncDialog, GeometryMixin):
def __init__(self, parent=None):
super(CloseDialog, self).__init__(parent)
self.shutdown = self.addButton('System\nShutdown',QMessageBox.DestructiveRole)
self._request_name = 'CLOSEPROMPT'
self._title = 'QtVCP'
################################################################################
# Tool Change Dialog
################################################################################
class ToolDialog(LcncDialog, GeometryMixin):
def __init__(self, parent=None):
super(ToolDialog, self).__init__(parent)
self.setText('<b>Manual Tool Change Request</b>')
self.setInformativeText('Please Insert Tool 0')
self.setStandardButtons(QMessageBox.Ok)
self.setDefaultButton(QMessageBox.Ok)
self._useDesktopNotify = False
self._frameless = False
self._curLine = 0
self._actionbutton = self.addButton('Pause For Jogging', QMessageBox.ApplyRole)
self._actionbutton.setEnabled(False)
self._flag = True
self._forcedFlag = False
# We want the tool change HAL pins the same as what's used in AXIS so it is
# easier for users to connect to.
# So we need to trick the HAL component into doing this for these pins,
# but not any other Qt widgets.
# So we record the original base name of the component, make our pins, then
# switch it back
def _hal_init(self):
self.set_default_geometry()
self.read_preference_geometry('ToolChangeDialog-geometry')
if not hal.component_exists('hal_manualtoolchange'):
oldname = self.HAL_GCOMP_.comp.getprefix()
self.HAL_GCOMP_.comp.setprefix('hal_manualtoolchange')
self.hal_pin = self.HAL_GCOMP_.newpin('change', hal.HAL_BIT, hal.HAL_IN)
self.hal_pin.value_changed.connect(self.tool_change)
self.tool_number = self.HAL_GCOMP_.newpin('number', hal.HAL_S32, hal.HAL_IN)
self.changed = self.HAL_GCOMP_.newpin('changed', hal.HAL_BIT, hal.HAL_OUT)
self.ext_ack = self.HAL_GCOMP_.newpin(self.HAL_NAME_ + 'change_button', hal.HAL_BIT, hal.HAL_IN)
self.ext_ack.value_changed.connect(self.external_acknowledge)
self.HAL_GCOMP_.comp.setprefix(oldname)
else:
LOG.warning("""Detected hal_manualtoolchange component already loaded
Qtvcp recommends to allow use of it's own component by not loading the original.
Qtvcp Integrated toolchange dialog will not show until then""")
if self.PREFS_:
self.play_sound = self.PREFS_.getpref('toolDialog_play_sound', True, bool, 'DIALOG_OPTIONS')
self.speak = self.PREFS_.getpref('toolDialog_speak', True, bool, 'DIALOG_OPTIONS')
self.sound_type = self.PREFS_.getpref('toolDialog_sound_type', 'READY', str, 'DIALOG_OPTIONS')
else:
self.play_sound = False
# process callback from 'change' HAL pin
def tool_change(self, change):
if change:
try:
STATUS.stat.poll()
self._curLine = STATUS.stat.motion_line
except:
self._curLine = 0
# enable/disable pause at jog button
if self._curLine > 0:
self._actionbutton.setEnabled(True)
jpause = True
else:
self._actionbutton.setEnabled(False)
jpause = False
MORE = 'Please Insert Tool %d' % self.tool_number.get()
try:
tool_table_line = TOOL.GET_TOOL_INFO(self.tool_number.get())
comment = str(tool_table_line[TOOL.COMMENTS])
except TypeError:
comment = ''
MESS = 'Manual Tool Change Request'
DETAILS = ' Tool Info: %s'% comment
STATUS.emit('focus-overlay-changed', True, MESS, self._color)
if self.PREFS_:
if self.speak:
STATUS.emit('play-sound', 'speak %s' % MORE)
if self.play_sound:
STATUS.emit('play-sound', self.sound_type)
# show desktop notify dialog rather then a qt dialog
if self._useDesktopNotify:
self.deskNotice = NOTICE.show_toolchange_notification(MESS,
MORE +'\n' + comment,
None, 0,
self._processChange,
jogpause = jpause)
else:
# ok show Qt dialog
self.showdialog(MESS, MORE, DETAILS,
frameless = self._frameless)
elif not change:
self.changed.set(False)
# process callback for 'change-button' HAL pin
# hide the message dialog or desktop notify message
def external_acknowledge(self, state):
#print('external acklnowledge: {}'.format(state))
if state:
if self._useDesktopNotify:
self.deskNotice.close()
elif self.isVisible():
self.hide()
self._processChange(True)
# This also is called from DesktopDialog
def _processChange(self,answer):
#print('proces change: {}'.format(answer))
if answer == -1:
self.changed.set(True)
ACTION.ABORT()
STATUS.emit('update-machine-log', 'tool change paused for jogging; launched run-from-line', 'TIME')
STATUS.emit('dialog-request', {'NAME': 'RUNFROMLINE',
'LINE':self._curLine+2, 'NONBLOCKING':True})
elif answer == True:
self.changed.set(True)
elif answer == False:
ACTION.ABORT()
STATUS.emit('update-machine-log', 'tool change Aborted', 'TIME,WARNING')
self.record_geometry()
STATUS.emit('focus-overlay-changed', False, None, None)
###### overridden functions ################
def showdialog(self, message, more_info=None, details=None,
play_alert=None,
frameless = True):
self.setWindowModality(Qt.ApplicationModal)
if frameless:
self.setWindowFlags(self.windowFlags() | Qt.Tool |
Qt.FramelessWindowHint | Qt.Dialog |
Qt.WindowStaysOnTopHint | Qt.WindowSystemMenuHint)
else:
self.setWindowFlags(self.windowFlags() | Qt.Tool |
Qt.Dialog | Qt.WindowStaysOnTopHint
| Qt.WindowSystemMenuHint)
self.setWindowFlags(self.windowFlags() | Qt.CustomizeWindowHint)
self.setWindowFlag(Qt.WindowCloseButtonHint, False)
self.setWindowTitle(self._title)
self.setIcon(QMessageBox.Critical)
self.setText('<b>%s</b>' % message)
self.setInformativeText(more_info)
self.setDetailedText(details)
self.setStandardButtons(QMessageBox.Ok)
self.buttonClicked.connect(self.msgbtn)
# force the details box open on first time display
if self._flag and details != ' Tool Info: ':
for i in self.buttons():
if self.buttonRole(i) == QMessageBox.ActionRole:
if i is self._actionbutton: continue
i.click()
self._flag = False
if play_alert:
STATUS.emit('play-sound', play_alert)
self.show()
# set the geometry when dialog shown
def showEvent(self, event):
self.set_geometry()
super(LcncDialog, self).showEvent(event)
# decode button presses
def msgbtn(self, i):
LOG.debug('Button pressed is: {}'.format(i.text()))
if self.clickedButton() == self._actionbutton:
self._processChange(-1)
elif self.standardButton(self.clickedButton()) == QMessageBox.Ok:
self._processChange(True)
else:
self._processChange(False)
############################################
# **********************
# Designer properties
# **********************
# inherited from lcncDialog
# plus :
def setFrameless(self, value):
self._frameless = value
def getFrameless(self):
return self._frameless
def resetFrameless(self):
self._frameless = False
frameless = Property(bool, getFrameless, setFrameless, resetFrameless)
def setUseDesktopNotify(self, value):
self._useDesktopNotify = value
def getUseDesktopNotify(self):
return self._useDesktopNotify
def resetUseDesktopNotify(self):
self._useDesktopNotify = False
useDesktopNotify = Property(bool, getUseDesktopNotify, setUseDesktopNotify, resetUseDesktopNotify)
################################################################################
# File Open Dialog
################################################################################
class FileDialog(QFileDialog, GeometryMixin):
def __init__(self, parent=None):
super(FileDialog, self).__init__(parent)
self._state = False
self._load_request_name = 'LOAD'
self._save_request_name = 'SAVE'
self._color = QColor(0, 0, 0, 150)
options = QFileDialog.Options()
options |= QFileDialog.DontUseNativeDialog
self.setOptions(options)
self.setWindowModality(Qt.ApplicationModal)
self.setWindowFlags(self.windowFlags() | Qt.Tool |
Qt.Dialog |
Qt.WindowStaysOnTopHint | Qt.WindowSystemMenuHint)
self.INI_exts = INFO.get_qt_filter_extensions()
self.setNameFilter(self.INI_exts)
self.default_path = (os.path.join(os.path.expanduser('~'), 'linuxcnc/nc_files/examples'))
# sidebar links
urls = []
urls.append(QUrl.fromLocalFile(os.path.expanduser('~')))
local = os.path.join(os.path.expanduser('~'),'linuxcnc/nc_files')
if os.path.exists(local):
urls.append(QUrl.fromLocalFile(local))
self.setSidebarUrls(urls)
def _hal_init(self):
self.set_default_geometry()
STATUS.connect('dialog-request', self._external_request)
if self.PREFS_:
self.play_sound = self.PREFS_.getpref('fileDialog_play_sound', True, bool, 'DIALOG_OPTIONS')
self.sound_type = self.PREFS_.getpref('fileDialog_sound_type', 'READY', str, 'DIALOG_OPTIONS')
last_path = self.PREFS_.getpref('last_loaded_directory', self.default_path, str, 'BOOK_KEEPING')
self.setDirectory(last_path)
else:
self.play_sound = False
def _external_request(self, w, message):
name = message.get('NAME')
if name in (self._load_request_name,self._save_request_name):
ext = message.get('EXTENSIONS')
pre = message.get('FILENAME')
dir = message.get('DIRECTORY')
if dir is None:
dir = self.PREFS_.getpref('last_loaded_directory', self.default_path, str, 'BOOK_KEEPING')
geo = message.get('GEONAME') or 'FileDialog-geometry'
self.read_preference_geometry(geo)
if name == self._load_request_name:
# if there is an ID then a file name response is expected
if message.get('ID'):
message['RETURN'] = self.load_dialog(ext, pre, dir, True)
STATUS.emit('general', message)
else:
self.load_dialog(extensions = ext, preselect = pre, directory = dir,)
else:
if message.get('ID'):
message['RETURN'] = self.save_dialog(ext, pre, dir)
STATUS.emit('general', message)
def showdialog(self):
self.load_dialog()
def load_dialog(self, extensions = None, preselect = None, directory = None, return_path=False):
#print(extensions,preselect,os.path.expanduser(directory))
self.setFileMode(QFileDialog.ExistingFile)
self.setAcceptMode(QFileDialog.AcceptOpen)
if extensions:
self.setNameFilter(extensions)
else:
self.setNameFilter(self.INI_exts)
if directory:
self.setDirectory(os.path.expanduser(directory))
else:
self.setDirectory(self.default_path)
if preselect:
self.selectFile(os.path.basename(preselect))
else:
self.selectFile('')
self.setWindowTitle('Open')
STATUS.emit('focus-overlay-changed', True, 'Open Gcode', self._color)
if self.play_sound:
STATUS.emit('play-sound', self.sound_type)
self.set_geometry()
fname = None
if (self.exec()):
fname = self.selectedFiles()[0]
path = self.directory().absolutePath()
self.setDirectory(path)
self.record_geometry()
if fname and not return_path:
if self.PREFS_:
self.PREFS_.putpref('last_loaded_directory', path, str, 'BOOK_KEEPING')
self.PREFS_.putpref('RecentPath_0', fname, str, 'BOOK_KEEPING')
ACTION.OPEN_PROGRAM(fname)
STATUS.emit('update-machine-log', 'Loaded: ' + fname, 'TIME')
# overlay hides it's self after loading
STATUS.emit('focus-overlay-changed', False, None, None)
return fname
def save_dialog(self, extensions = None, preselect = None, directory = None):
self.setFileMode(QFileDialog.AnyFile)
self.setAcceptMode(QFileDialog.AcceptSave)
self.setDefaultSuffix('ngc')
if extensions:
self.setNameFilter(extensions)
else:
self.setNameFilter(self.INI_exts)
if directory:
self.setDirectory(os.path.expanduser(directory))
if preselect:
self.selectFile(os.path.basename(preselect))
# disable filename auto completer
for le in self.findChildren(QLineEdit):
le.setCompleter(None)
self.setWindowTitle('Save')
STATUS.emit('focus-overlay-changed', True, 'Save Gcode', self._color)
if self.play_sound:
STATUS.emit('play-sound', self.sound_type)
self.set_geometry()
fname = None
if (self.exec()):
fname = self.selectedFiles()[0]
path = self.directory().absolutePath()
self.setDirectory(path)
else:
fname = None
STATUS.emit('focus-overlay-changed', False, None, None)
self.record_geometry()
if fname:
if self.PREFS_:
self.PREFS_.putpref('last_saved_directory', path, str, 'BOOK_KEEPING')
self.PREFS_.putpref('RecentSavedPath_0', fname, str, 'BOOK_KEEPING')
return fname
#**********************
# Designer properties
#**********************
@Slot(bool)
def setState(self, value):
self._state = value
if value:
self.show()
else:
self.hide()
def getState(self):
return self._state
def resetState(self):
self._state = False
def getColor(self):
return self._color
def setColor(self, value):
self._color = value
def resetState(self):
self._color = QColor(0, 0, 0, 150)
state = Property(bool, getState, setState, resetState)
overlay_color = Property(QColor, getColor, setColor)
def getLoadIdName(self):
return self._load_request_name
def setLoadIdName(self, name):
self._load_request_name = name
def resetLoadIdName(self):
self._load_request_name = 'LOAD'
def getSaveIdName(self):
return self._save_request_name
def setSaveIdName(self, name):
self._save_request_name = name
def resetSaveIdName(self):
self._save_request_name = 'SAVE'
launch_load_id = Property(str, getLoadIdName, setLoadIdName, resetLoadIdName)
launch_save_id = Property(str, getSaveIdName, setSaveIdName, resetSaveIdName)
################################################################################
# origin Offset Dialog
################################################################################
class OriginOffsetDialog(QDialog, GeometryMixin):
def __init__(self, parent=None):
super(OriginOffsetDialog, self).__init__(parent)
self._color = QColor(0, 0, 0, 150)
self._state = False
self._request_name = 'ORIGINOFFSET'
self.setWindowModality(Qt.ApplicationModal)
self.setWindowFlags(self.windowFlags() | Qt.Tool |
Qt.Dialog |
Qt.WindowStaysOnTopHint | Qt.WindowSystemMenuHint)
self.setMinimumSize(200, 200)
buttonBox = QDialogButtonBox()
buttonBox.setEnabled(False)
STATUS.connect('not-all-homed', lambda w, axis: buttonBox.setEnabled(False))
STATUS.connect('all-homed', lambda w: buttonBox.setEnabled(True))
STATUS.connect('state-estop', lambda w: buttonBox.setEnabled(False))
STATUS.connect('state-estop-reset', lambda w: buttonBox.setEnabled(STATUS.machine_is_on()
and STATUS.is_all_homed()))
STATUS.connect('interp-idle', lambda w: buttonBox.setEnabled(STATUS.machine_is_on()
and STATUS.is_all_homed()))
STATUS.connect('interp-run', lambda w: buttonBox.setEnabled(False))
for i in('X', 'Y', 'Z'):
b = 'button_%s' % i
self[b] = QPushButton('Zero %s' % i)
self[b].clicked.connect(self.zeroPress('%s' % i))
buttonBox.addButton(self[b], QDialogButtonBox.ActionRole)
v = QVBoxLayout()
h = QHBoxLayout()
self._o = OFFVIEW_WIDGET()
self._o.setObjectName('__dialogOffsetViewWidget')
self.setLayout(v)
v.addWidget(self._o)
b = QPushButton('OK')
b.clicked.connect(lambda: self.close())
h.addWidget(b)
h.addWidget(buttonBox)
v.addLayout(h)
self.setModal(True)
def _hal_init(self):
self._o.hal_init()
self.set_default_geometry()
STATUS.connect('dialog-request', self._external_request)
def _external_request(self, w, message):
if message['NAME'] == self._request_name:
geo = message.get('GEONAME') or 'OriginOffsetDialog-geometry'
self.read_preference_geometry(geo)
self.load_dialog()
# This weird code is just so we can get the axis
# letter
# using clicked.connect() apparently can't easily
# add user data
def zeroPress(self, data):
def calluser():
self.zeroAxis(data)
return calluser
def zeroAxis(self, index):
ACTION.SET_AXIS_ORIGIN(index, 0)
def load_dialog(self):
STATUS.emit('focus-overlay-changed', True, 'Set Origin Offsets', self._color)
self.set_geometry()
self.show()
self.exec()
STATUS.emit('focus-overlay-changed', False, None, None)
self.record_geometry()
def _hal_cleanup(self):
self._o._hal_cleanup()
# usual boiler code
# (used so we can use code such as self[SomeDataName]
def __getitem__(self, item):
return getattr(self, item)
def __setitem__(self, item, value):
return setattr(self, item, value)
# **********************
# Designer properties
# **********************
@Slot(bool)
def setState(self, value):
self._state = value
if value:
self.show()
else:
self.hide()
def getState(self):
return self._state
def resetState(self):
self._state = False
def getColor(self):
return self._color
def setColor(self, value):
self._color = value
def resetState(self):
self._color = QColor(0, 0, 0, 150)
def getIdName(self):
return self._request_name
def setIdName(self, name):
self._request_name = name
def resetIdName(self):
self._request_name = 'ORIGINOFFSET'
launch_id = Property(str, getIdName, setIdName, resetIdName)
state = Property(bool, getState, setState, resetState)
overlay_color = Property(QColor, getColor, setColor)
################################################################################
# Tool Offset Dialog
################################################################################
class ToolOffsetDialog(QDialog, GeometryMixin):
def __init__(self, parent=None):
super(ToolOffsetDialog, self).__init__(parent)
self._color = QColor(0, 0, 0, 150)
self._state = False
self._request_name = 'TOOLOFFSET'
self.setWindowModality(Qt.ApplicationModal)
self.setWindowFlags(self.windowFlags() | Qt.Tool |
Qt.Dialog |
Qt.WindowStaysOnTopHint | Qt.WindowSystemMenuHint)
self.setMinimumSize(200, 200)
self._o = TOOLVIEW_WIDGET()
self._o.setObjectName('__dialogToolViewWidget')
self._o._hal_init()
buttonBox = QDialogButtonBox()
buttonBox.setEnabled(False)
STATUS.connect('not-all-homed', lambda w, axis: buttonBox.setEnabled(False))
STATUS.connect('all-homed', lambda w: buttonBox.setEnabled(True))
STATUS.connect('state-estop', lambda w: buttonBox.setEnabled(False))
STATUS.connect('state-estop-reset', lambda w: buttonBox.setEnabled(STATUS.machine_is_on()
and STATUS.is_all_homed()))
STATUS.connect('interp-idle', lambda w: buttonBox.setEnabled(STATUS.machine_is_on()
and STATUS.is_all_homed()))
STATUS.connect('interp-run', lambda w: buttonBox.setEnabled(False))
self.addtool = QPushButton('Add Tool')
self.addtool.clicked.connect(lambda: self.addTool())
buttonBox.addButton(self.addtool, QDialogButtonBox.ActionRole)
self.deletetool = QPushButton('Delete Tool')
self.deletetool.clicked.connect(lambda: self.deleteTool())
buttonBox.addButton(self.deletetool, QDialogButtonBox.ActionRole)
#for i in('X', 'Y', 'Z'):
# b = 'button_%s' % i
# self[b] = QPushButton('Zero %s' % i)
# self[b].clicked.connect(self.zeroPress('%s' % i))
# buttonBox.addButton(self[b], 3)
v = QVBoxLayout()
h = QHBoxLayout()
self.setLayout(v)
v.addWidget(self._o)
b = QPushButton('OK')
b.clicked.connect(lambda: self.close())
h.addWidget(b)
h.addWidget(buttonBox)
v.addLayout(h)
self.setModal(True)
def _hal_init(self):
self.set_default_geometry()
STATUS.connect('dialog-request', self._external_request)
def _external_request(self, w, message):
if message['NAME'] == self._request_name:
geo = message.get('GEONAME') or 'ToolOffsetDialog-geometry'
self.read_preference_geometry(geo)
self.load_dialog()
def addTool(self):
self._o.add_tool()
def deleteTool(self):
self._o.delete_tools()
# This weird code is just so we can get the axis
# letter
# using clicked.connect() apparently can't easily
# add user data
def zeroPress(self, data):
def calluser():
self.zeroAxis(data)
return calluser
def zeroAxis(self, index):
ACTION.SET_AXIS_ORIGIN(index, 0)
def load_dialog(self):
STATUS.emit('focus-overlay-changed', True, 'Set Tool Offsets', self._color)
self.set_geometry()
self.show()
self.exec()
STATUS.emit('focus-overlay-changed', False, None, None)
self.record_geometry()
# usual boiler code
# (used so we can use code such as self[SomeDataName]
def __getitem__(self, item):
return getattr(self, item)
def __setitem__(self, item, value):
return setattr(self, item, value)
# **********************
# Designer properties
# **********************
@Slot(bool)
def setState(self, value):
self._state = value
if value:
self.show()
else:
self.hide()
def getState(self):
return self._state
def resetState(self):
self._state = False
def getColor(self):
return self._color
def setColor(self, value):
self._color = value
def resetState(self):
self._color = QColor(0, 0, 0, 150)
def getIdName(self):
return self._request_name
def setIdName(self, name):
self._request_name = name
def resetIdName(self):
self._request_name = 'TOOLOFFSET'
launch_id = Property(str, getIdName, setIdName, resetIdName)
state = Property(bool, getState, setState, resetState)
overlay_color = Property(QColor, getColor, setColor)
################################################################################
# Tool Chooser Dialog
################################################################################
class ToolChooserDialog(QDialog, GeometryMixin):
def __init__(self, parent=None):
super(ToolChooserDialog, self).__init__(parent)
self._color = QColor(0, 0, 0, 150)
self._state = False
self._request_name = 'TOOLCHOOSER'
self._message = None
self._title = 'Choose Tool'
self.setWindowModality(Qt.ApplicationModal)
self.setWindowFlags(self.windowFlags() | Qt.Tool |
Qt.Dialog |
Qt.WindowStaysOnTopHint | Qt.WindowSystemMenuHint)
self.setMinimumSize(200, 200)
self._o = TOOLCHOOSER_WIDGET()
self._o.setObjectName('__dialogToolChooserWidget')
self._o._hal_init()
buttonBox = QDialogButtonBox()
buttonBox.setEnabled(False)
STATUS.connect('not-all-homed', lambda w, axis: buttonBox.setEnabled(False))
STATUS.connect('all-homed', lambda w: buttonBox.setEnabled(True))
STATUS.connect('state-estop', lambda w: buttonBox.setEnabled(False))
STATUS.connect('state-estop-reset', lambda w: buttonBox.setEnabled(STATUS.machine_is_on()
and STATUS.is_all_homed()))
STATUS.connect('interp-idle', lambda w: buttonBox.setEnabled(STATUS.machine_is_on()
and STATUS.is_all_homed()))
STATUS.connect('interp-run', lambda w: buttonBox.setEnabled(False))
v = QVBoxLayout()
v.addWidget(self._o)
self.bBox = QDialogButtonBox()
self.bBox.addButton('Apply', QDialogButtonBox.AcceptRole)
self.bBox.addButton('Cancel', QDialogButtonBox.RejectRole)
self.bBox.rejected.connect(self.reject)
self.bBox.accepted.connect(self.accept)
self._o.doubleClicked.connect(self.accept)
v.addWidget(self.bBox)
self.setModal(True)
self.setLayout(v)
def accept(self):
self.record_geometry()
try:
num = int(self._o.getSelectedTool())
LOG.debug('Selected tool when accepted: {}'.format(num))
if self._message is not None:
self._message['RETURN'] = num
STATUS.emit('general', self._message)
self._message = None
except Exception as e:
print(e)
self.hide()
def reject(self):
self.record_geometry()
if self._message is not None:
self._message['RETURN'] = None
STATUS.emit('general', self._message)
self._message = None
self.hide()
def _hal_init(self):
self.set_default_geometry()
STATUS.connect('dialog-request', self._external_request)
def _external_request(self, w, message):
self._message = message
if message.get('NAME') == self._request_name:
geo = message.get('GEONAME') or 'ToolChooserDialog-geometry'
self.read_preference_geometry(geo)
self.showdialog(message)
def showdialog(self, message, more_info=None, details=None,
play_alert=None,
frameless = True):
self.setWindowModality(Qt.ApplicationModal)
if frameless:
self.setWindowFlags(self.windowFlags() | Qt.Tool |
Qt.FramelessWindowHint | Qt.Dialog |
Qt.WindowStaysOnTopHint | Qt.WindowSystemMenuHint)
else:
self.setWindowFlags(self.windowFlags() | Qt.Tool |
Qt.Dialog | Qt.WindowStaysOnTopHint
| Qt.WindowSystemMenuHint)
self.setWindowFlags(self.windowFlags() | Qt.CustomizeWindowHint)
self.setWindowFlag(Qt.WindowCloseButtonHint, False)
self.setWindowTitle(self._title)
STATUS.emit('focus-overlay-changed', True, 'Tool Chooser', self._color)
self.set_geometry()
self.show()
self.exec()
STATUS.emit('focus-overlay-changed', False, None, None)
self.record_geometry()
def showEvent(self, event):
self.set_geometry()
super(ToolChooserDialog, self).showEvent(event)
def __getitem__(self, item):
return getattr(self, item)
def __setitem__(self, item, value):
return setattr(self, item, value)
@Slot(bool)
def setState(self, value):
self._state = value
if value:
self.show()
else:
self.hide()
def getState(self):
return self._state
def resetState(self):
self._state = False
def getColor(self):
return self._color
def setColor(self, value):
self._color = value
def resetColor(self):
self._color = QColor(0, 0, 0, 150)
def getIdName(self):
return self._request_name
def setIdName(self, name):
self._request_name = name
def resetIdName(self):
self._request_name = 'TOOLCHOOSER'
launch_id = Property(str, getIdName, setIdName, resetIdName)
state = Property(bool, getState, setState, resetState)
overlay_color = Property(QColor, getColor, setColor)
################################################################################
# CamView Dialog
################################################################################
class CamViewDialog(QDialog, GeometryMixin):
def __init__(self, parent=None):
super(CamViewDialog, self).__init__(parent)
from qtvcp.widgets.camview_widget import CamView
self._color = QColor(0, 0, 0, 150)
self._state = False
self._request_name = 'CAMVIEW'
self.setWindowModality(Qt.ApplicationModal)
self.setWindowFlags(self.windowFlags() | Qt.Tool |
Qt.Dialog |
Qt.WindowStaysOnTopHint | Qt.WindowSystemMenuHint)
self.setMinimumSize(200, 200)
h = QHBoxLayout()
h.addStretch(1)
self.b = QPushButton('Close')
self.b.clicked.connect(lambda: self.close())
h.addWidget(self.b)
l = QVBoxLayout()
self.camV = CamView()
self.setLayout(l)
l.addWidget(self.camV)
l.addLayout(h)
def _hal_init(self):
self.camV.hal_init(HAL_NAME='')
self.set_default_geometry()
STATUS.connect('dialog-request', self._external_request)
def _external_request(self, w, message):
if message['NAME'] == self._request_name:
geo = message.get('GEONAME') or 'CamViewOffsetDialog-geometry'
self.read_preference_geometry(geo)
nblock = message.get('NONBLOCKING')
if nblock:
self.setWindowModality(Qt.NonModal)
self.setWindowFlags(self.windowFlags() | Qt.Tool |
Qt.Dialog | Qt.WindowStaysOnTopHint
| Qt.WindowSystemMenuHint)
self.load_dialog_nonblocking()
else:
self.setWindowModality(Qt.ApplicationModal)
self.setWindowFlags(self.windowFlags() | Qt.Tool |
Qt.FramelessWindowHint | Qt.Dialog |
Qt.WindowStaysOnTopHint | Qt.WindowSystemMenuHint)
self.load_dialog()
def close(self):
self.record_geometry()
super(CamViewDialog, self).close()
def load_dialog_nonblocking(self):
self.set_geometry()
self.show()
def load_dialog(self):
STATUS.emit('focus-overlay-changed', True, 'Cam View Dialog', self._color)
self.set_geometry()
self.show()
self.exec()
STATUS.emit('focus-overlay-changed', False, None, None)
# **********************
# Designer properties
# **********************
@Slot(bool)
def setState(self, value):
self._state = value
if value:
self.show()
else:
self.hide()
def getState(self):
return self._state
def resetState(self):
self._state = False
def getColor(self):
return self._color
def setColor(self, value):
self._color = value
def resetState(self):
self._color = QColor(0, 0, 0, 150)
def getIdName(self):
return self._request_name
def setIdName(self, name):
self._request_name = name
def resetIdName(self):
self._request_name = 'CAMVIEW'
launch_id = Property(str, getIdName, setIdName, resetIdName)
state = Property(bool, getState, setState, resetState)
overlay_color = Property(QColor, getColor, setColor)
################################################################################
# MacroTab Dialog
################################################################################
class MacroTabDialog(QDialog, GeometryMixin):
def __init__(self, parent=None):
super(MacroTabDialog, self).__init__(parent)
self.setWindowTitle('Qtvcp Macro Menu')
self._color = QColor(0, 0, 0, 150)
self._state = False
self._request_name = 'MACROTAB'
self.setWindowModality(Qt.ApplicationModal)
self.setWindowFlags(self.windowFlags() | Qt.Tool |
Qt.Dialog |
Qt.WindowStaysOnTopHint | Qt.WindowSystemMenuHint)
self.setMinimumSize(00, 200)
self.resize(600, 400)
# patch class to call our button methods rather then the
# original methods (Gotta do before instantiation)
MacroTab.closeChecked = self._close
MacroTab.runChecked = self._run
MacroTab.setTitle = self._setTitle
# ok now instantiate patched class
self.tab = MacroTab()
self.tab.setObjectName('macroTabInternal_')
l = QVBoxLayout()
self.setLayout(l)
l.addWidget(self.tab)
#we need the close button
self.tab.closeButton.setVisible(True)
def _hal_init(self):
self.set_default_geometry()
# gotta call this since we instantiated this out of qtvcp's knowledge
self.tab._hal_init()
STATUS.connect('dialog-request', self._external_request)
def _external_request(self, w, message):
if message['NAME'] == self._request_name:
geo = message.get('GEONAME') or 'MacroTabDialog-geometry'
self.read_preference_geometry(geo)
self.load_dialog()
# This method is called instead of MacroTab's closeChecked method
# we do this so we can use it's buttons to hide our dialog
# rather then close the MacroTab widget
def _close(self):
self.close()
# This method is called instead of MacroTab's runChecked() method
# we do this so we can use it's buttons to hide our dialog
# rather then close the MacroTab widget
def _run(self):
self.tab.runMacro()
self.close()
def _setTitle(self, string):
self.setWindowTitle(string)
def load_dialog(self):
STATUS.emit('focus-overlay-changed', True, 'Lathe Macro Dialog', self._color)
self.tab.stack.setCurrentIndex(0)
self.set_geometry()
self.show()
self.exec()
STATUS.emit('focus-overlay-changed', False, None, None)
self.record_geometry()
# **********************
# Designer properties
# **********************
@Slot(bool)
def setState(self, value):
self._state = value
if value:
self.show()
else:
self.hide()
def getState(self):
return self._state
def resetState(self):
self._state = False
def getColor(self):
return self._color
def setColor(self, value):
self._color = value
def resetState(self):
self._color = QColor(0, 0, 0, 150)
def getIdName(self):
return self._request_name
def setIdName(self, name):
self._request_name = name
def resetIdName(self):
self._request_name = 'MACROTAB'
launch_id = Property(str, getIdName, setIdName, resetIdName)
state = Property(bool, getState, setState, resetState)
overlay_color = Property(QColor, getColor, setColor)
################################################################################
# Versaprobe Dialog
################################################################################
class VersaProbeDialog(QDialog, GeometryMixin):
def __init__(self, parent=None):
super(VersaProbeDialog, self).__init__(parent)
self._color = QColor(0, 0, 0, 150)
self._state = False
self._request_name = 'VERSAPROBE'
self.setWindowModality(Qt.ApplicationModal)
self.setWindowFlags(self.windowFlags() | Qt.Tool |
Qt.Dialog |
Qt.WindowStaysOnTopHint | Qt.WindowSystemMenuHint)
self.setMinimumSize(200, 200)
buttonBox = QDialogButtonBox(QDialogButtonBox.Ok)
b = buttonBox.button(QDialogButtonBox.Ok)
b.clicked.connect(lambda: self.close())
l = QVBoxLayout()
self._o = VersaProbe()
self.setLayout(l)
l.addWidget(self._o)
l.addWidget(buttonBox)
def _hal_init(self):
self._o.hal_init()
self.set_default_geometry()
STATUS.connect('dialog-request', self._external_request)
def _hal_cleanup(self):
self._o._hal_cleanup()
def _external_request(self, w, message):
if message['NAME'] == self._request_name:
geo = message.get('GEONAME') or 'VersaProbeDialog-geometry'
self.read_preference_geometry(geo)
self.load_dialog()
def showdialog(self):
self.load_dialog()
def load_dialog(self):
STATUS.emit('focus-overlay-changed', True, 'VersaProbe Dialog', self._color)
self.set_geometry()
self.show()
self.exec()
STATUS.emit('focus-overlay-changed', False, None, None)
self.record_geometry()
# **********************
# Designer properties
# **********************
@Slot(bool)
def setState(self, value):
self._state = value
if value:
self.show()
else:
self.hide()
def getState(self):
return self._state
def resetState(self):
self._state = False
def getColor(self):
return self._color
def setColor(self, value):
self._color = value
def resetState(self):
self._color = QColor(0, 0, 0, 150)
def getIdName(self):
return self._request_name
def setIdName(self, name):
self._request_name = name
def resetIdName(self):
self._request_name = 'VERSAPROBE'
launch_id = Property(str, getIdName, setIdName, resetIdName)
state = Property(bool, getState, setState, resetState)
overlay_color = Property(QColor, getColor, setColor)
############################################
# Entry Dialog
############################################
class EntryDialog(QDialog, GeometryMixin):
def __init__(self, parent=None):
super(EntryDialog, self).__init__(parent)
self._color = QColor(0, 0, 0, 150)
self.play_sound = False
self._request_name = 'ENTRY'
self._title = 'Numerical Entry'
self.setWindowFlags(self.windowFlags() | Qt.Tool |
Qt.Dialog | Qt.WindowStaysOnTopHint |
Qt.WindowSystemMenuHint)
self._softKey = SoftInputWidget(self, 'numeric')
self.Num = QLineEdit()
self.Num.installEventFilter(self)
self.Num.keyboard_type = 'numeric'
# actiate touch input
self.Num.keyboard_enable = True
self.Num.returnPressed.connect(lambda: self.accept())
gl = QVBoxLayout()
gl.addWidget(self.Num)
self.bBox = QDialogButtonBox()
self.bBox.addButton('Apply', QDialogButtonBox.AcceptRole)
self.bBox.addButton('Cancel', QDialogButtonBox.RejectRole)
self.bBox.rejected.connect(self.reject)
self.bBox.accepted.connect(self.accept)
gl.addWidget(self.bBox)
self.setLayout(gl)
def _hal_init(self):
self.set_default_geometry()
if self.PREFS_:
self.play_sound = self.PREFS_.getpref('EntryDialog_play_sound', True, bool, 'DIALOG_OPTIONS')
self.sound_type = self.PREFS_.getpref('EntryDialog_sound_type', 'READY', str, 'DIALOG_OPTIONS')
else:
self.play_sound = False
STATUS.connect('dialog-request', self._external_request)
def eventFilter(self, widget, event):
if self.Num.focusWidget() == widget and event.type() == QEvent.MouseButtonPress:
if self.Num.keyboard_enable:
self._softKey.show_input_panel(widget)
return False
# this processes STATUS called dialog requests
# We check the cmd to see if it was for us
# then we check for a id string
# if all good show the dialog
# and then send back the dialog response via a general message
def _external_request(self, w, message):
if message.get('NAME') == self._request_name:
geo = message.get('GEONAME') or 'EntryDialog-geometry'
self.read_preference_geometry(geo)
t = message.get('TITLE')
if t:
self._title = t
else:
self._title = 'Numerical Entry'
overlay = message.get('OVERLAY')
if overlay is None :
overlay = True
preload = message.get('PRELOAD')
num = self.showdialog(preload=preload, overlay=overlay)
message['RETURN'] = num
STATUS.emit('general', message)
def showdialog(self, preload=None,overlay=True):
conversion = {'x':0, 'y':1, "z":2, 'a':3, "b":4, "c":5, 'u':6, 'v':7, 'w':8}
if overlay:
STATUS.emit('focus-overlay-changed', True, '', self._color)
self.setWindowTitle(self._title)
if self.play_sound:
STATUS.emit('play-sound', self.sound_type)
self.set_geometry()
if preload is not None:
self.Num.setText(str(preload))
flag = False
while flag == False:
self.Num.setFocus()
retval = self.exec()
if retval:
try:
answer = float(self.Num.text())
flag = True
except Exception as e:
try:
p,relp,dtg = STATUS.get_position()
otext = text = self.Num.text().lower()
for let in INFO.AVAILABLE_AXES:
let = let.lower()
if let in text:
Pos = relp[conversion[let]]
text = text.replace('%s'%let,'%s'%Pos)
process = eval(text)
answer = float(process)
STATUS.emit('update-machine-log', 'Convert Entry from: {} to {}'.format(otext,text), 'TIME,SUCCESS')
flag = True
except Exception as e:
self.setWindowTitle('%s'%e)
else:
flag = True
answer = None
if overlay:
STATUS.emit('focus-overlay-changed', False, None, None)
self.record_geometry()
LOG.debug('Value of pressed button: {}'.format(retval))
if answer is None:
return None
return answer
def getColor(self):
return self._color
def setColor(self, value):
self._color = value
def resetState(self):
self._color = QColor(0, 0, 0, 150)
def getIdName(self):
return self._request_name
def setIdName(self, name):
self._request_name = name
def resetIdName(self):
self._request_name = 'ENTRY'
def set_soft_keyboard(self, data):
self.Num.keyboard_enable = data
def get_soft_keyboard(self):
return self.Num.keyboard_enable
def reset_soft_keyboard(self):
self.Num.keyboard_enable = True
# designer will show these properties in this order:
launch_id = Property(str, getIdName, setIdName, resetIdName)
overlay_color = Property(QColor, getColor, setColor)
soft_keyboard_option = Property(bool, get_soft_keyboard, set_soft_keyboard, reset_soft_keyboard)
############################################
# Keyboard Dialog
############################################
class KeyboardDialog(QDialog, GeometryMixin):
def __init__(self, parent=None):
super(KeyboardDialog, self).__init__(parent)
self._color = QColor(0, 0, 0, 150)
self.play_sound = False
self._request_name = 'KEYBOARD'
self._title = 'Keyboard Entry'
self.setWindowFlags(self.windowFlags() | Qt.Tool |
Qt.Dialog | Qt.WindowStaysOnTopHint |
Qt.WindowSystemMenuHint)
self.keybrd = VirtualKeyboard()
self.edit = QLineEdit()
gl = QVBoxLayout()
gl.addWidget(self.edit)
gl.addWidget(self.keybrd)
self.bBox = QDialogButtonBox()
self.bBox.addButton('Apply', QDialogButtonBox.AcceptRole)
self.bBox.addButton('Cancel', QDialogButtonBox.RejectRole)
self.bBox.rejected.connect(self.reject)
self.bBox.accepted.connect(self.accept)
gl.addWidget(self.bBox)
self.setLayout(gl)
def _hal_init(self):
self.set_default_geometry()
if self.PREFS_:
self.play_sound = self.PREFS_.getpref('KeyboardDialog_play_sound', True, bool, 'DIALOG_OPTIONS')
self.sound_type = self.PREFS_.getpref('KeyboardDialog_sound_type', 'READY', str, 'DIALOG_OPTIONS')
else:
self.play_sound = False
STATUS.connect('dialog-request', self._external_request)
# this processes STATUS called dialog requests
# We check the cmd to see if it was for us
# then we check for a id string
# if all good show the dialog
# and then send back the dialog response via a general message
def _external_request(self, w, message):
if message.get('NAME') == self._request_name:
geo = message.get('GEONAME') or 'KeyboardDialog-geometry'
self.read_preference_geometry(geo)
t = message.get('TITLE')
if t:
self._title = t
else:
self._title = 'Keyboard Entry'
preload = message.get('PRELOAD')
overlay = message.get('OVERLAY')
if overlay is None :
overlay = True
text = self.showdialog(preload)
message['RETURN'] = text
STATUS.emit('general', message)
def showdialog(self, preload=None, overlay=True):
if overlay:
STATUS.emit('focus-overlay-changed', True, '', self._color)
self.setWindowTitle(self._title)
if self.play_sound:
STATUS.emit('play-sound', self.sound_type)
self.set_geometry()
self.edit.setFocus()
if preload is not None:
self.edit.setFocus()
self.edit.setText(str(preload))
self.edit.deselect()
retval = self.exec()
answer = self.edit.text()
if retval:
STATUS.emit('update-machine-log', 'keyboard Entry {}'.format(answer), 'TIME,DEBUG')
else:
answer = None
if overlay:
STATUS.emit('focus-overlay-changed', False, None, None)
self.record_geometry()
LOG.debug('Value of pressed button: {}'.format(retval))
if answer is None:
return None
return answer
def getColor(self):
return self._color
def setColor(self, value):
self._color = value
def resetState(self):
self._color = QColor(0, 0, 0, 150)
def getIdName(self):
return self._request_name
def setIdName(self, name):
self._request_name = name
def resetIdName(self):
self._request_name = 'ENTRY'
def set_soft_keyboard(self, data):
self.Num.keyboard_enable = data
def get_soft_keyboard(self):
return self.Num.keyboard_enable
def reset_soft_keyboard(self):
self.Num.keyboard_enable = True
# designer will show these properties in this order:
launch_id = Property(str, getIdName, setIdName, resetIdName)
overlay_color = Property(QColor, getColor, setColor)
soft_keyboard_option = Property(bool, get_soft_keyboard, set_soft_keyboard, reset_soft_keyboard)
############################################
# Calculator Dialog
############################################
class CalculatorDialog(Calculator, GeometryMixin):
def __init__(self, parent=None):
super(CalculatorDialog, self).__init__(parent)
self._color = QColor(0, 0, 0, 150)
self.play_sound = False
self._request_name = 'CALCULATOR'
self._title = 'Calculator Entry'
self._nblock = False
self._message = None
self._overlay = None
self._flag = False
self._result = False
self.setWindowFlags(self.windowFlags() | Qt.Tool |
Qt.Dialog | Qt.WindowStaysOnTopHint |
Qt.WindowSystemMenuHint)
def _hal_init(self):
self.set_default_geometry()
if self.PREFS_:
self.play_sound = self.PREFS_.getpref('CalculatorDialog_play_sound', True, bool, 'DIALOG_OPTIONS')
self.sound_type = self.PREFS_.getpref('CalculatorDialog_sound_type', 'READY', str, 'DIALOG_OPTIONS')
else:
self.play_sound = False
STATUS.connect('dialog-request', self._external_request)
STATUS.connect('dialog-update', self._external_update)
# this processes STATUS called dialog requests
# We check the cmd to see if it was for us
# then we check for a id string
# if all good show the dialog
# and then send back the dialog response via a general message
def _external_request(self, w, message):
self._message = message
if message.get('NAME') == self._request_name:
geo = message.get('GEONAME') or 'CalculatorDialog-geometry'
self.read_preference_geometry(geo)
t = message.get('TITLE')
if t:
self._title = t
else:
self._title = 'Calculator Entry'
preload = message.get('PRELOAD')
axis = message.get('AXIS')
next = message.get('NEXT', False)
cycle = message.get('WIDGETCYCLE', False)
if axis in ('X','Y','Z','A','B','C','U','V','W'):
self.axisTriggered(axis)
self._nblock = message.get('NONBLOCKING')
overlay = message.get('OVERLAY')
if overlay is None:
overlay = True
self._overlay = overlay
if next:
self.updatedialog(preload=preload)
message['NEXT'] = False
else:
num = self.showdialog(preload=preload, overlay=overlay, cycle=cycle)
# callback from status 'update-dialog'
def _external_update(self, w, message):
if message.get('NAME') == self._request_name:
if not self.isVisible(): return
response = message.get('response')
if not response is None:
if response == 0:
self.reject()
elif response == 1:
self.accept()
def updatedialog(self, preload=None):
self.setWindowTitle(self._title)
if self.play_sound:
STATUS.emit('play-sound', self.sound_type)
if self._overlay:
STATUS.emit('focus-overlay-changed', True, '', self._color)
if preload is not None:
self.display.setText(str(preload))
def getValue(self, title=''):
self._title = title
self._nblock = True
geo = 'CalculatorDialog-geometry'
self.read_preference_geometry(geo)
return self.showdialog(preload=None, overlay=False, cycle=False, wait=True)
def showdialog(self, preload=None, overlay=True, cycle=False, wait=False):
self.setWindowTitle(self._title)
if self.play_sound:
STATUS.emit('play-sound', self.sound_type)
self.set_geometry()
if preload is not None:
self.display.setText(str(preload))
# show/hide the widget cycling buttons
self.applyNextButton.setVisible(cycle)
self.nextButton.setVisible(cycle)
self.backButton.setVisible(cycle)
if self._nblock:
self.show()
if wait:
self._flag = True
while self._flag:
QApplication.processEvents()
return (self.display.text(), self._result)
else:
if overlay:
STATUS.emit('focus-overlay-changed', True, '', self._color)
retval = self.exec()
if overlay:
STATUS.emit('focus-overlay-changed', False, None, None)
def accept(self):
self.record_geometry()
super(CalculatorDialog, self).accept()
self._result = True
self._flag = False
try:
num = float(self.display.text())
LOG.debug('Displayed value when accepted: {}'.format(num))
if self._message is not None:
self._message['RETURN'] = num
self._message['NEXT'] = False
STATUS.emit('general', self._message)
self._message = None
except Exception as e:
print(e)
def reject(self):
self.record_geometry()
super(CalculatorDialog, self).reject()
self._result = False
self._flag = False
if self._message is not None:
self._message['RETURN'] = None
self._message['NEXT'] = False
STATUS.emit('general', self._message)
self._message = None
# used for cycling between different widgets.
# the actual cycling is done in the calling code
def backAction(self):
try:
if self._message is not None:
self._message['RETURN'] = None
self._message['NEXT'] = False
self._message['BACK'] = True
STATUS.emit('general', self._message)
except Exception as e:
print(e)
# used for cycling between different widgets.
# the actual cycling is done in the calling code
def nextAction(self):
try:
if self._message is not None:
self._message['RETURN'] = None
self._message['NEXT'] = True
self._message['BACK'] = False
STATUS.emit('general', self._message)
except Exception as e:
print(e)
# used to apply and then cycle to the next widget.
# the actual cycling is done in the calling code
def applyAction(self):
self._flag = False
try:
num = float(self.display.text())
if self._message is not None:
self._message['RETURN'] = num
self._message['NEXT'] = True
self._message['BACK'] = False
STATUS.emit('general', self._message)
except Exception as e:
print(e)
def getColor(self):
return self._color
def setColor(self, value):
self._color = value
def resetState(self):
self._color = QColor(0, 0, 0, 150)
def getIdName(self):
return self._request_name
def setIdName(self, name):
self._request_name = name
def resetIdName(self):
self._request_name = 'CALCULATOR'
launch_id = Property(str, getIdName, setIdName, resetIdName)
overlay_color = Property(QColor, getColor, setColor)
############################################
# machine Log Dialog
############################################
class MachineLogDialog(QDialog, GeometryMixin):
def __init__(self, parent=None):
super(MachineLogDialog, self).__init__(parent)
self._color = QColor(0, 0, 0, 150)
self.play_sound = False
self._request_name = 'MACHINELOG'
self._title = 'Machine Log'
self.setWindowFlags(self.windowFlags() | Qt.Tool |
Qt.Dialog | Qt.WindowStaysOnTopHint |
Qt.WindowSystemMenuHint)
def _hal_init(self):
self.buildWidget()
self.set_default_geometry()
if self.PREFS_:
self.play_sound = self.PREFS_.getpref('MachineLogDialog_play_sound', True, bool, 'DIALOG_OPTIONS')
self.sound_type = self.PREFS_.getpref('MachineLogDialog_sound_type', 'READY', str, 'DIALOG_OPTIONS')
else:
self.play_sound = False
STATUS.connect('dialog-request', self._external_request)
def buildWidget(self):
# add a vertical layout to dialog
l = QVBoxLayout()
self.setLayout(l)
# build tab widget
tabs = QTabWidget()
# build tabs
tab_mlog = MachineLog()
tab_mlog._hal_init()
tab_ilog = MachineLog()
tab_ilog.set_integrator_log(True)
tab_ilog._hal_init()
# add tabs to tab widget
tabs.addTab(tab_mlog,'Machine Log')
tabs.addTab(tab_ilog,'Integrator Log')
# add tab to layout
l.addWidget(tabs)
# build dialog buttons
self.bBox = QDialogButtonBox()
self.bBox.addButton('Ok', QDialogButtonBox.AcceptRole)
self.bBox.accepted.connect(self.accept)
# add buttons to layout
l.addWidget(self.bBox)
# this processes STATUS called dialog requests
# We check the cmd to see if it was for us
# then we check for a id string
# if all good show the dialog
# and then send back the dialog response via a general message
def _external_request(self, w, message):
if message.get('NAME') == self._request_name:
geo = message.get('GEONAME') or 'MachineLogDialog-geometry'
self.read_preference_geometry(geo)
t = message.get('TITLE')
if t:
self._title = t
else:
self._title = 'Machine Log'
nonblock = message.get('NONBLOCKING')
num = self.showdialog(nonblock)
message['RETURN'] = num
STATUS.emit('general', message)
def showdialog(self, nonblock=None):
if nonblock is not None:
STATUS.emit('focus-overlay-changed', True, 'Machine Log', self._color)
self.setWindowTitle(self._title)
if self.play_sound:
STATUS.emit('play-sound', self.sound_type)
self.set_geometry()
if nonblock is not None:
self.exec()
STATUS.emit('focus-overlay-changed', False, None, None)
self.record_geometry()
return False
else:
self.show()
def accept(self):
self.record_geometry()
super(MachineLogDialog, self).accept()
def reject(self):
self.record_geometry()
super(MachineLogDialog, self).reject()
def getColor(self):
return self._color
def setColor(self, value):
self._color = value
def resetState(self):
self._color = QColor(0, 0, 0, 150)
def getIdName(self):
return self._request_name
def setIdName(self, name):
self._request_name = name
def resetIdName(self):
self._request_name = 'MACHINELOG'
# designer will show these properties in this order:
launch_id = Property(str, getIdName, setIdName, resetIdName)
overlay_color = Property(QColor, getColor, setColor)
############################################
# Run from line prestart Dialog
############################################
class RunFromLineDialog(QDialog, GeometryMixin):
def __init__(self, parent=None):
super(RunFromLineDialog, self).__init__(parent)
# Load the widgets UI file:
self.filename = os.path.join(PATH.SHAREDIR,'widgets_ui', 'runFromLine_dialog.ui')
try:
self.instance = uic.loadUi(self.filename, self)
except AttributeError as e:
LOG.critical(e)
self.start_line = None
self._color = QColor(0, 0, 0, 150)
self.play_sound = False
self._request_name = 'RUNFROMLINE'
self._title = 'Run from line preset Dialog'
self.setWindowFlags(self.windowFlags() | Qt.Tool |
Qt.Dialog | Qt.WindowStaysOnTopHint |
Qt.WindowSystemMenuHint)
self.buttonBox.clicked.connect(self.Clicked)
def _hal_init(self):
self.set_default_geometry()
if self.PREFS_:
self.play_sound = self.PREFS_.getpref('RunFromLineDialog_play_sound', True, bool, 'DIALOG_OPTIONS')
self.sound_type = self.PREFS_.getpref('RunFromLineDialog_sound_type', 'READY', str, 'DIALOG_OPTIONS')
else:
self.play_sound = False
STATUS.connect('dialog-request', self._external_request)
def homed_on_test():
return (STATUS.machine_is_on()
and (STATUS.is_all_homed() or INFO.NO_HOME_REQUIRED))
STATUS.connect('state-off', lambda w: self.setEnabled(False))
STATUS.connect('state-estop', lambda w: self.setEnabled(False))
STATUS.connect('interp-idle', lambda w: self.setEnabled(homed_on_test()))
STATUS.connect('all-homed', lambda w: self.setEnabled(homed_on_test()))
# this processes STATUS called dialog requests
# We check the cmd to see if it was for us
# then we check for a id string
# if all good show the dialog
# and then send back the dialog response via a general message
def _external_request(self, w, message):
if message.get('NAME') == self._request_name:
geo = message.get('GEONAME') or 'RunFromLineDialog-geometry'
self.read_preference_geometry(geo)
l = message.get('LINE')
t = message.get('TITLE')
if t:
self._title = t
else:
self._title = 'Run From Line: {}'.format(l)
nblock = message.get('NONBLOCKING')
mess = message.get('MESSAGE')
num = self.showdialog(line = l, message=mess, nonblock = nblock)
message['RETURN'] = num
STATUS.emit('general', message)
def showdialog(self, line = 1, message=None, nonblock=None):
self.start_line = int(line)
if message is not None:
self.label_line.setText(message)
if not nonblock:
STATUS.emit('focus-overlay-changed', True, 'Machine Log', self._color)
self.setWindowTitle(self._title)
if self.play_sound:
STATUS.emit('play-sound', self.sound_type)
self.set_geometry()
if not nonblock:
self.exec()
STATUS.emit('focus-overlay-changed', False, None, None)
self.record_geometry()
return False
else:
self.show()
# accept button applies presets and if line number given starts linuxcnc
def accept(self):
self.preset()
if self.start_line:
ACTION.RUN(self.start_line)
super(RunFromLineDialog, self).accept()
#apply button only applies presets
def Clicked(self, button):
if self.buttonBox.buttonRole(button) == QDialogButtonBox.ApplyRole:
self.preset()
# preset spindle before running
def preset(self):
if self.radioButton_cw.isChecked():
direction = 'M3'
else:
direction = 'M4'
speed = self.spinBox_rpm.value()
ACTION.CALL_MDI_WAIT('s{} {}'.format(speed,direction), mode_return=True)
################################################################################
# About Dialog
################################################################################
class AboutDialog(QDialog, GeometryMixin):
def __init__(self, parent=None):
super(AboutDialog, self).__init__(parent)
self._geometry_string = 'half'
self._color = QColor(0, 0, 0, 150)
self._request_name = 'ABOUT'
self._title = 'QtVCP About'
self.play_sound = False
self.text = QTextEdit('This is an ABOUT dialog')
self.text.setReadOnly(True)
self.setWindowFlags(self.windowFlags() | Qt.Tool |
Qt.Dialog | Qt.WindowStaysOnTopHint |
Qt.WindowSystemMenuHint)
def _hal_init(self):
self.buildWidget()
self.set_default_geometry()
self.read_preference_geometry('AboutDialog-geometry')
STATUS.connect('dialog-request', self._external_request)
def buildWidget(self):
# add a vertical layout to dialog
l = QVBoxLayout()
self.setLayout(l)
l.addWidget(self.text)
# build dialog buttons
self.bBox = QDialogButtonBox()
self.bBox.addButton('Ok', QDialogButtonBox.AcceptRole)
self.bBox.accepted.connect(self.accept)
# add buttons to layout
l.addWidget(self.bBox)
def setText(self, txt):
self.text.setText(txt)
self.adjustSize()
# this processes STATUS called dialog requests
# We check the cmd to see if it was for us
# then we check for a id string
# if all good show the dialog
# and then send back the dialog response via a general message
def _external_request(self, w, message):
if message.get('NAME') == self._request_name:
geo = message.get('GEONAME') or 'AboutDialog-geometry'
self.read_preference_geometry(geo)
t = message.get('TITLE')
if t:
self._title = t
else:
self._title = 'About'
nonblock = message.get('NONBLOCKING')
num = self.showdialog(nonblock)
message['RETURN'] = num
STATUS.emit('general', message)
def showdialog(self, nonblock=None):
if nonblock is not None:
STATUS.emit('focus-overlay-changed', True, 'Machine Log', self._color)
self.setWindowTitle(self._title)
if self.play_sound:
STATUS.emit('play-sound', self.sound_type)
self.set_geometry()
if nonblock is not None:
self.exec()
STATUS.emit('focus-overlay-changed', False, None, None)
self.record_geometry()
return False
else:
self.show()
def accept(self):
self.record_geometry()
super(AboutDialog, self).accept()
def getColor(self):
return self._color
def setColor(self, value):
self._color = value
def resetState(self):
self._color = QColor(0, 0, 0, 150)
def getIdName(self):
return self._request_name
def setIdName(self, name):
self._request_name = name
def resetIdName(self):
self._request_name = 'ABOUT'
# designer will show these properties in this order:
launch_id = Property(str, getIdName, setIdName, resetIdName)
overlay_color = Property(QColor, getColor, setColor)
################################
# for testing without editor:
################################
def main():
import sys
from qtpy.QtWidgets import QApplication
app = QApplication(sys.argv)
#widget = AboutDialog()
#widget.setText('</b>This is new text<\b>')
#widget = KeyboardDialog()
#widget = CalculatorDialog()
#widget = RunFromLineDialog()
#widget = MachineLogDialog()
#widget = EntryDialog()
#widget = CamViewDialog()
#widget = VersaProbeDialog()
#widget = MacroTabDialog()
#widget = CamViewDialog()
#widget = ToolOffsetDialog()
#widget = OriginOffsetDialog()
#widget = FileDialog()
#widget = ToolDialog()
widget = ToolChooserDialog(
)
widget.HAL_NAME_ = 'test'
widget.PREFS_ = None
widget._hal_init()
t = widget.showdialog(1)
print (t)
sys.exit()
if __name__ == '__main__':
main()