Import LinuxCNC source tree

This commit is contained in:
Wang Dequan
2026-08-20 06:17:39 +08:00
commit 2c814634c3
9429 changed files with 10556013 additions and 0 deletions

View File

@@ -0,0 +1,435 @@
#!/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
from qtpy.QtWidgets import (QWidget, QLabel, QHBoxLayout,
QVBoxLayout, QPushButton, QDialog, QProgressBar)
from qtpy.QtCore import (Qt, QEvent, Slot, Property, QChildEvent,
)
from qtpy.QtGui import QColor, QImage, QResizeEvent, QPainter, QMoveEvent
from qtvcp.widgets.widget_baseclass import _HalWidgetBase
from qtvcp.core import Status
from qtvcp import logger
# Instantiate the libraries with global reference
# STATUS gives us status messages from linuxcnc
# LOG is for running code logging
if __name__ != '__main__':
STATUS = Status()
LOG = logger.getLogger(__name__)
# Set the log level for this module
#LOG.setLevel(logger.DEBUG) # One of DEBUG, INFO, WARNING, ERROR, CRITICAL
################################################################
# Overlay base class
# based on c++ code on net
# Originally designed to allow an overlay on individual widgets
# which might be nice too but for now just the wholw window
################################################################
class OverlayWidget(QWidget):
def __init__(self, parent=None):
self.last = None
self.top_level = parent
super(OverlayWidget, self).__init__(parent)
self.parent = parent
#self.setAttribute(Qt.WA_NoSystemBackground)
#self.setAttribute(Qt.WA_TransparentForMouseEvents)
self.setWindowFlags( self.windowFlags() |Qt.Tool |
Qt.FramelessWindowHint |
Qt.WindowStaysOnTopHint )
if not self.parent is None:
self.parent.installEventFilter(self)
# There seems to be no way to get the very top level widget
# in QT the parent widget can be owned in another parent
# this adds an event filter the widget in 'top_level'
# so we can track what is happening to it.
def newParent(self):
self.parent = self.top_level
self.last = self.parent
if self.last is not None:
LOG.debug('last removed: {} NEW:{}'.format(self.last, self.parent))
self.last.removeEventFilter(self)
if not self.parent: return
self.parent.installEventFilter(self)
#self.raise_()
# Here we track parent window events that we need to react to.
# then we pass then to the original widget
def eventFilter(self, obj, event):
# might be useful events to know
# 103 window blocked by modal dialog
# 99 activation changed
# 25 deactivated
# 24 activated
#print event,'parent',self.parent
#print event,'Event Type',self.event.type()
if obj == self.parent:
#Catches resize and child events from the parent widget
if event.type() == QEvent.Resize:
#print 'resize'
self.resize(QResizeEvent.size(event))
elif event.type() == QEvent.Move:
self.move(QMoveEvent.pos(event))
elif(event.type() == QEvent.ChildAdded):
#print 'CHILD',QChildEvent.child(event)
if not QChildEvent.child(event) is QDialog:
self.raise_()
if event.type() == QEvent.Close:
self.hide()
self.closeEvent(event)
event.accept()
return super(OverlayWidget, self).eventFilter(obj, event)
# Tracks our instanance of focus widget changes
# most importantly the paint event
# if the parent changes it adjusts the event filter
# but in our case the parent won't because we are
# tracking the main window rather the the widget focus is in
def event(self, event):
#print 'overlay:',event
if event.type() == QEvent.ParentAboutToChange:
#print 'REMOVE FILTER'
self.parent.removeEventFilter(self)
return True
if event.type() == QEvent.ParentChange:
#print 'parentEVENT:', self.parentWidget()
self.newParent()
return True
if event.type() == QEvent.Paint:
self.paintEvent(event)
return True
return False
####################################################################
# subclass for using as a fancy dialog focus overlay
####################################################################
class FocusOverlay(OverlayWidget, _HalWidgetBase):
def __init__(self, parent=None):
super(FocusOverlay, self).__init__(parent)
self.setAttribute(Qt.WA_TranslucentBackground)
self.bg_color = QColor(0, 0, 0, 150)
self.text = "Loading..."
self._state = False
self._image_path = '~/emc-dev/linuxcnc-wizard.gif'
self._image = QImage(os.path.expanduser(self._image_path))
self._show_buttons = False
self._show_image = False
self._image_transp = 0.3
self._show_text = True
self.box()
# This is how we plant the very top level parent into
# our overlay widget
# QTVCP_INSTANCE is not accessible from Designer
# it should work with any main window but doesn't.
# this worked so i stopped looking why
# this also sets up following show or hide etc based on
# STATUS messages
# adjust image path name at runtime
def _hal_init(self):
STATUS.connect('focus-overlay-changed', lambda w, data, text, color:
self._status_response(data, text, color))
STATUS.connect('graphics-loading-progress',lambda w,f: self.updateProgress(f))
if self.PREFS_:
self.play_sound = self.PREFS_.getpref('overlay_play_sound', False, bool, 'SCREEN_OPTIONS')
self.sound_type = self.PREFS_.getpref('overlay_sound_type', 'RING', str, 'SCREEN_OPTIONS')
else:
self.play_sound = False
# reparent on top window
self.top_level = self.QTVCP_INSTANCE_
self.newParent()
# adjust size and location to top window
self.setGeometry(self.top_level.geometry())
self.hide()
# look for special path names and change to real path
if 'STD_IMAGE_DIR/' in self._image_path:
t = self._image_path.split('STD_IMAGE_DIR/', )[1]
d = os.path.join(self.PATHS_.IMAGEDIR, t)
elif 'CONFIG_DIR/' in self._image_path:
t = self._image_path.split('CONFIG_DIR/', )[1]
d = os.path.join(self.PATHS_.CONFIGPATH, t)
else:
return
if os.path.exists(d):
self._image = QImage(d)
else:
LOG.debug('Focus Overlay image path runtime error: {}'.format(self._image_path))
def _status_response(self, data, text, color):
if data:
if color:
self.bg_color = color
else:
self.bg_color = QColor(0, 0, 0, 150)
if text:
self.setShowText(True)
self.text = text
else:
self.setShowText(False)
self.show()
self.update()
LOG.debug('Overlay - Show')
if self.play_sound:
STATUS.emit('play-sound', '%s' % self.sound_type)
#os.system("beep -f 555 ")
else:
self.hide()
LOG.debug('Overlay - Hide')
# Ok paint everything
def paintEvent(self, event):
qp = QPainter()
qp.begin(self)
self.colorBackground(qp)
if self._show_image:
self.draw_image(qp)
self.drawText(qp)
qp.end()
#################################################
# Helper functions
#################################################
def updateProgress(self,fraction):
if fraction <0:
self.progressbar.setValue(0)
self.progressbar.setFormat('')
self.progressbar.setVisible(False)
self.hide()
else:
self.progressbar.setVisible(True)
self.progressbar.setValue(fraction)
self.progressbar.setFormat('Loading: {}%'.format(fraction))
# The basic overlay -transparence is set by bg_color
def colorBackground(self, qp):
qp.fillRect(self.rect(), self.bg_color)
# we can add an image like a watermark transparence set by
# _image_transp
def draw_image(self, qp):
#print self._image, self._image_transp
qp.setOpacity(self._image_transp)
qp.drawImage(self.rect(), self._image)
# This sets the text that was built in the box
# old code for text is skipped
def drawText(self, qp):
qp.setOpacity(1.0)
self.mb.setText('<html><head/><body><p><span style=" font-size:30pt; \
font-weight:600;">%s</span></p></body></html>' % self.text)
return
size = self.size()
w = size.width()
h = size.height()
qp.setPen(self.text_color)
qp.setFont(self.font)
qp.drawText(self.rect(), Qt.AlignCenter, self.text)
# build a black label with text and optionally some buttons
# using a label rather then drawing text allows options suck as rich text
# and a solid back ground so text is easy to see
# buttons could be used as a dialog but would require a focus widget per
# dialog action or some fancy coding for responses.
def box(self):
hbox2 = QHBoxLayout()
hbox2.addStretch(1)
self.mb = QLabel('<html><head/><body><p><span style=" font-size:30pt; \
font-weight:600;">%s</span></p></body></html>' % self.text, self)
self.mb.setStyleSheet("background-color: black; color: white")
self.mb.setAlignment(Qt.AlignVCenter | Qt.AlignCenter)
self.mb.setVisible(self._show_text)
hbox2.addWidget(self.mb)
self.progressbar = QProgressBar()
self.progressbar.setStyleSheet("background-color: black; color: white")
self.progressbar.setRange(0,100)
self.progressbar.setVisible(False)
hbox2.addWidget(self.progressbar)
hbox = QHBoxLayout()
hbox.addStretch(1)
self.okButton = QPushButton("OK")
self.okButton.pressed.connect(self.okChecked)
self.cancelButton = QPushButton("Cancel")
self.cancelButton.pressed.connect(self.cancelChecked)
self.okButton.setVisible(self._show_buttons)
self.cancelButton.setVisible(self._show_buttons)
l = QLabel()
hbox.addWidget(l)
hbox.addWidget(self.okButton)
hbox.addWidget(self.cancelButton)
vbox = QVBoxLayout()
vbox.addStretch(1)
vbox.addLayout(hbox2)
vbox.addLayout(hbox)
self.setLayout(vbox)
self.setGeometry(300, 300, 300, 150)
# would need to class patch for something really useful
def okChecked(self):
self.hide()
def cancelChecked(self):
self.hide()
#########################################################################
# 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
########################################################################
@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 getOverayColor(self):
return self.bg_color
@Slot(QColor)
def setOverayColor(self, value):
self.bg_color = value
def resetOverayColor(self, value):
self.bg_color = value
def setShowText(self, value):
self._show_text = value
self.mb.setVisible(value)
def getShowText(self):
return self._show_text
def resetShowText(self, value):
self._show_text = True
self.mb.setVisible(value)
def setshow_image(self, data):
self._show_image = data
#self.hide()
#self.show()
def getshow_image(self):
return self._show_image
def resetshow_image(self):
self._show_image = False
def set_image_transp(self, data):
self._image_transp = data
#self.hide()
#self.show()
def get_image_transp(self):
return self._image_transp
def reset_image_transp(self):
self._image_transp = 0.3
def setshow_buttons(self, data):
self._show_buttons = data
self.okButton.setVisible(data)
self.cancelButton.setVisible(data)
def getshow_buttons(self):
return self._show_buttons
def resetshow_buttons(self):
self._show_buttons = False
def setimage_path(self, data):
self._image_path = str(data)
try:
path = os.path.expanduser(self._image_path)
self._image = QImage(path)
except Exception as e:
LOG.debug('Focus Overlay image path error: {}'.format(self._image_path))
def getimage_path(self):
return self._image_path
def resetimage_path(self):
self._image_path = False
overlay_color = Property(QColor, getOverayColor, setOverayColor, resetOverayColor)
state = Property(bool, getState, setState, resetState)
show_text = Property(bool, getShowText, setShowText, resetShowText)
show_image_option = Property(bool, getshow_image, setshow_image, resetshow_image)
image_transparency = Property(float, get_image_transp, set_image_transp, reset_image_transp)
show_buttons_option = Property(bool, getshow_buttons, setshow_buttons, resetshow_buttons)
image_path = Property(str, getimage_path, setimage_path, resetimage_path)
#################
# Testing
#################
def main():
import sys
from qtpy.QtWidgets import QWidget, QApplication
from qtpy.QtCore import QTimer
app = QApplication(sys.argv)
w = QWidget()
#w.setWindowFlags( Qt.FramelessWindowHint | Qt.Dialog | Qt.WindowStaysOnTopHint )
w.setGeometry(300, 300, 250, 150)
w.setWindowTitle('Test')
l = QLabel('Hello, world!', w)
l.show()
LOG.debug('Label: {}'.format(l))
# class patching is my new thing
# class patch the OK button
def newOk(w):
print('Ok')
w.text = 'OK'
# make update
w.hide()
w.show()
def pop():
print('Pop')
f.show()
f.update()
FocusOverlay.okChecked = newOk
w.show()
# could use f = FocusOverlay( w )
# then dont need to adjust top level
f = FocusOverlay()
f.top_level = w
f.newParent()
# set with qtproperty call
f.setshow_buttons(True)
timer2 = QTimer()
timer2.timeout.connect(lambda:pop() )
timer2.start(1500)
sys.exit(app.exec())
if __name__ == '__main__':
main()