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

453 lines
16 KiB
Python

#!/usr/bin/env python3
# qtvcp
#
# Copyright (c) 2018 Chris Morley <chrisinnanaimo@hotmail.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.
#
#################################################################################
import os
from qtpy.QtCore import Signal, Property
from qtpy.QtWidgets import QLabel
from qtpy.QtGui import QPixmap
from qtvcp.widgets.widget_baseclass import _HalWidgetBase
from qtvcp.core import Status, Info, Tool, Path
from qtvcp import logger
# Instantiate the libraries with global reference
# STATUS gives us status messages from linuxcnc
# LOG is for running code logging
STATUS = Status()
INFO = Info()
TOOL = Tool()
PATH = Path()
LOG = logger.getLogger(__name__)
# Force the log level for this module
# LOG.setLevel(logger.INFO) # One of DEBUG, INFO, WARNING, ERROR, CRITICAL
if PATH.IMAGEDIR is not None:
DEFAULTIMAGE = (os.path.join(PATH.IMAGEDIR,'applet-critical.png')) or ''
else:
DEFAULTIMAGE = ''
class ImageSwitcher(QLabel, _HalWidgetBase):
widgetChanged = Signal(int)
def __init__(self, parent=None):
super(ImageSwitcher, self).__init__(parent)
self._defaultImage = DEFAULTIMAGE
self._imagePath = [DEFAULTIMAGE]
self._current_number = 0
#self.setScaledContents(True)
self.defaultPixmap = QPixmap()
def _hal_init(self):
self.show_image_by_number(self._current_number)
def _designerInit(self):
self._hal_init()
# Show the widgets based on a reference number
def show_image_by_number(self, number):
#print (self.objectName(),len(self._imagePath),number)
try:
# outside limits or list
if number <0 or number > len(self._imagePath)-1:
LOG.debug('Path reference number out of range: {}'.format(number))
self.setPixmap(self.defaultPixmap)
return
# specifically ask for None
if self._imagePath[number].upper() == 'NONE':
self.setPixmap(QPixmap())
return
# specifically ask for Default image
if self._imagePath[number].upper() == 'DEFAULT':
self.setPixmap(self.defaultPixmap)
return
# resources file images.
if ':/' in self._imagePath[number]:
path = self._imagePath[number]
pixmap = QPixmap(path)
# no valid image
if pixmap.isNull():
self.setPixmap(self.defaultPixmap)
return
self.setPixmap(pixmap)
return
else:
path = os.path.expanduser(self._imagePath[number])
except Exception as e:
LOG.error('Path reference number: {}'.format(e))
self.setPixmap(self.defaultPixmap)
return
path = os.path.expanduser(self.defaultPixmap)
#print 'requested:',number,self._imagePath[number]
# if path doesn't exist try referencing
# from the built in image folder
if not os.path.exists(path):
path = os.path.join(PATH.IMAGEDIR, path)
if not os.path.exists(path):
LOG.debug('No Path: {}'.format(path))
pixmap = QPixmap(path)
self.setPixmap(pixmap)
def set_default_image(self, path):
self._defaultImage = path
def set_image_number(self, data):
self._current_number = data
self.show_image_by_number(data)
def get_image_number(self):
return self._current_number
def reset_image_number(self):
self._current_number = 0
image_number = Property(int, get_image_number, set_image_number, reset_image_number)
def set_image_l(self, data):
self._imagePath = data
def get_image_l(self):
return self._imagePath
def reset_image_l(self):
self._imagePath = [self._defaultImage]
image_list = Property('QStringList', get_image_l, set_image_l, reset_image_l)
def setImagePath(self, data):
self.defaultPixmap = data
def getImagePath(self):
return self.defaultPixmap
def resetImagePath(self):
self.defaultPixmap
image_default = Property(QPixmap, getImagePath, setImagePath, resetImagePath)
##############################
# required class boiler code #
##############################
def __getitem__(self, item):
return getattr(self, item)
def __setitem__(self, item, value):
return setattr(self, item, value)
class StatusImageSwitcher(ImageSwitcher):
def __init__(self, parent=None):
super(StatusImageSwitcher, self).__init__(parent)
self._imagePath = [os.path.join(PATH.IMAGEDIR,'applet-critical.png'),
os.path.join(PATH.IMAGEDIR,'spindle_ccw.gif'),
os.path.join(PATH.IMAGEDIR,'spindle_cw.gif')]
self.spindle = True
self.all_homed = False
self.axis_homed = False
self.hard_limits = False
self.machine_state = False
self.command_state = False
self.feedmode_state = False
self.spindlemode_state = False
self.toolnumber_state = False
self.tool_orientation_state = False
self._last_limit = []
self.axis = 'X'
for i in range(0,len(INFO.AVAILABLE_JOINTS)):
self._last_limit.append([0,0])
def _hal_init(self):
if self.spindle:
STATUS.connect('spindle-control-changed', lambda w, a, b, c, d: self.switch_on_spindle(b,c,d))
elif self.all_homed:
STATUS.connect('not-all-homed', lambda w, data: self.switch_on_homed(0))
STATUS.connect('all-homed', lambda w: self.switch_on_homed(1))
elif self.axis_homed:
STATUS.connect('unhomed', lambda w, data: self.switch_on_axis_unhomed(data))
STATUS.connect('homed', lambda w, data: self.switch_on_axis_homed(data))
elif self.hard_limits:
STATUS.connect('hard-limits-tripped', lambda w, data, group: self.switch_on_hard_limits(data, group))
elif self.machine_state:
STATUS.connect('state-estop', lambda w: self.switch_on_machine_state(0))
STATUS.connect('interp-run', lambda w: self.switch_on_machine_state(1))
STATUS.connect('interp-idle', lambda w: self.switch_on_machine_state(2))
STATUS.connect('interp-paused', lambda w: self.switch_on_machine_state(3))
STATUS.connect('interp-waiting', lambda w: self.switch_on_machine_state(4))
STATUS.connect('interp-reading', lambda w: self.switch_on_machine_state(5))
elif self.command_state:
STATUS.connect('command-running', lambda w: self.switch_on_command_state(0))
STATUS.connect('command-stopped', lambda w: self.switch_on_command_state(1))
STATUS.connect('command-error', lambda w: self.switch_on_command_state(2))
elif self.feedmode_state:
STATUS.connect('fpm-mode', lambda w, d: self.switch_on_feedmode_state(0, d))
STATUS.connect('fpr-mode', lambda w, d: self.switch_on_feedmode_state(1,d))
STATUS.connect('itime-mode', lambda w, d: self.switch_on_feedmode_state(2,d))
elif self.spindlemode_state:
STATUS.connect('rpm-mode', lambda w, d: self.switch_on_spindlemode_state(0, d))
STATUS.connect('css-mode', lambda w, d: self.switch_on_spindlemode_state(1, d))
elif self.toolnumber_state:
STATUS.connect('tool-in-spindle-changed', lambda w, d:self.switch_on_toolnumber_state(d))
elif self.tool_orientation_state:
STATUS.connect('tool-info-changed', lambda w, d:self.switch_on_toolinfo_state(d, TOOL.ORIENTATION))
def _designerInit(self):
self.show_image_by_number(0)
# M5 disabled = image 0
# M3 at speed = image 1
# M4 at speed = image 2
# M3 accelerating = image 3
# M4 accelerating = image 4
def switch_on_spindle(self, on, speed, up):
if not on:
num = 0
elif not up:
if speed<0: num = 4
else: num = 3
elif speed <0: num = 2
else: num = 1
self.set_image_number(num)
def switch_on_homed(self, data):
if not data <0:
self.set_image_number(data)
def switch_on_axis_homed(self, num):
data = INFO.GET_NAME_FROM_JOINT[int(num)]
if data == self.axis:
self.set_image_number(1)
def switch_on_axis_unhomed(self, num):
data = INFO.GET_NAME_FROM_JOINT[int(num)]
if data == self.axis:
self.set_image_number(0)
def switch_on_hard_limits(self, data, group):
if not data:
# tripped
self.set_image_number(0)
elif (len(self._imagePath)) == 2:
#print 'bool images'
self.set_image_number(1)
elif (len(self._imagePath)-1) == (len(INFO.AVAILABLE_JOINTS)):
#print 'per joint limits images', self._last_limit, group
for i in range(0,len(INFO.AVAILABLE_JOINTS)):
if group[i] == self._last_limit[i]:
pass
elif group[i] == [0,0]:
pass
else:
self.set_image_number(i+1)
break
elif (len(self._imagePath)-1) == (len(INFO.AVAILABLE_JOINTS) * 2):
pass
#print 'per joint and per end limits images'
self._last_limit = group
def switch_on_machine_state(self, state):
self.set_image_number(state)
def switch_on_command_state(self, state):
self.set_image_number(state)
def switch_on_feedmode_state(self, mode, state):
if state:
self.set_image_number(mode)
def switch_on_spindlemode_state(self, mode, state):
if state:
self.set_image_number(mode)
def switch_on_toolnumber_state(self, number):
self.set_image_number(number)
def switch_on_toolinfo_state(self, tool_entry, index):
toolnum = tool_entry[0]
tool_table_line = TOOL.GET_TOOL_INFO(toolnum)
try:
self.set_image_number(tool_table_line[index])
except Exception as e:
LOG.warning('Problem with tool file info')
#########################################################################
# This is how designer can interact with our widget properties.
# designer will show the Property properties in the editor
# it will use the get set and reset calls to do those actions
#
# _toggle_properties makes it so we can only select one option
########################################################################
def _toggle_properties(self, picked):
data = ('spindle','all_homed', 'axis_homed','hard_limits',
'machine_state', 'command_state', 'feedmode_state',
'spindlemode_state', 'tool_orientation_state',
'toolnumber_state' )
for i in data:
if not i == picked:
self['watch_'+i] = False
# property getter/setters
# machine_spindle status
def set_spindle(self, data):
self.spindle = data
if data:
self._toggle_properties('spindle')
def get_spindle(self):
return self.spindle
def reset_spindle(self):
self.spindle = False
watch_spindle = Property(bool, get_spindle, set_spindle, reset_spindle)
# machine_homed status
def set_homed(self, data):
self.all_homed = data
if data:
self._toggle_properties('all_homed')
def get_homed(self):
return self.all_homed
def reset_homed(self):
self.all_homed = False
watch_all_homed = Property(bool, get_homed, set_homed, reset_homed)
# machine_axis_homed status
def set_axis_homed(self, data):
self.axis_homed = data
if data:
self._toggle_properties('axis_homed')
def get_axis_homed(self):
return self.axis_homed
def reset_axis_homed(self):
self.axis_homed = False
watch_axis_homed = Property(bool, get_axis_homed, set_axis_homed, reset_axis_homed)
# machine_limits status
def set_limits(self, data):
self.hard_limits = data
if data:
self._toggle_properties('hard_limits')
def get_limits(self):
return self.hard_limits
def reset_limits(self):
self.hard_limits = False
watch_hard_limits = Property(bool, get_limits, set_limits, reset_limits)
# machine_state status
def set_machine_state(self, data):
self.machine_state = data
if data:
self._toggle_properties('machine_state')
def get_machine_state(self):
return self.machine_state
def reset_machine_state(self):
self.machine_state = False
watch_machine_state = Property(bool, get_machine_state, set_machine_state,
reset_machine_state)
# command_state status
def set_command_state(self, data):
self.command_state = data
if data:
self._toggle_properties('command_state')
def get_command_state(self):
return self.command_state
def reset_command_state(self):
self.command_state = False
watch_command_state = Property(bool, get_command_state, set_command_state,
reset_command_state)
# feedmode_state status
def set_feedmode_state(self, data):
self.feedmode_state = data
if data:
self._toggle_properties('feedmode_state')
def get_feedmode_state(self):
return self.feedmode_state
def reset_feedmode_state(self):
self.feedmode_state = False
watch_feedmode_state = Property(bool, get_feedmode_state, set_feedmode_state,
reset_feedmode_state)
# spindlemode_state status
def set_spindlemode_state(self, data):
self.spindlemode_state = data
if data:
self._toggle_properties('spindlemode_state')
def get_spindlemode_state(self):
return self.spindlemode_state
def reset_spindlemode_state(self):
self.spindlemode_state = False
watch_spindlemode_state = Property(bool, get_spindlemode_state, set_spindlemode_state,
reset_spindlemode_state)
# toolnumber_state status
def set_toolnumber_state(self, data):
self.toolnumber_state = data
if data:
self._toggle_properties('toolnumber_state')
def get_toolnumber_state(self):
return self.toolnumber_state
def reset_toolnumber_state(self):
self.toolnumber_state = False
watch_toolnumber_state = Property(bool, get_toolnumber_state, set_toolnumber_state,
reset_toolnumber_state)
# tool_orientation_state status
def set_tool_orientation_state(self, data):
self.tool_orientation_state = data
if data:
self._toggle_properties('tool_orientation_state')
def get_tool_orientation_state(self):
return self.tool_orientation_state
def reset_tool_orientation_state(self):
self.tool_orientation_state = False
watch_tool_orientation_state = Property(bool, get_tool_orientation_state, set_tool_orientation_state,
reset_tool_orientation_state)
def set_axis(self, data):
if data.upper() in('X','Y','Z','A','B','C','U','V','W'):
self.axis = data.upper()
def get_axis(self):
return self.axis
def reset_axis(self):
self.axis = 'X'
axis_letter = Property(str, get_axis, set_axis, reset_axis)
##############################
# required class boiler code #
##############################
def __getitem__(self, item):
return getattr(self, item)
def __setitem__(self, item, value):
return setattr(self, item, value)
# for testing without editor:
def main():
import sys
from qtpy.QtWidgets import QApplication
app = QApplication(sys.argv)
widget = ImageSwitcher()
widget._hal_init()
widget.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()