286 lines
10 KiB
Python
286 lines
10 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.
|
|
#
|
|
# base code Found on Code Cookbook
|
|
#
|
|
# This widget pops up an onscreen keyboard for entries
|
|
# Used in the Macro and MDI line widget
|
|
|
|
from qtpy import QtWidgets, QtCore, QtGui
|
|
#from decimal import Decimal
|
|
|
|
# applicationle widgets
|
|
SIP_WIDGETS = [QtWidgets.QLineEdit]
|
|
|
|
|
|
class MyFlatPushButton(QtWidgets.QPushButton):
|
|
def __init__(self, caption, min_size=(50, 50)):
|
|
self.MIN_SIZE = min_size
|
|
super(MyFlatPushButton, self).__init__(caption)
|
|
self.setFocusPolicy(QtCore.Qt.NoFocus)
|
|
|
|
def sizeHint(self):
|
|
return QtCore.QSize(self.MIN_SIZE[0], self.MIN_SIZE[1])
|
|
|
|
class SoftInputWidget(QtWidgets.QDialog):
|
|
def __init__(self, parent=None, keyboard_type='default'):
|
|
super(SoftInputWidget, self).__init__(parent)
|
|
self.setWindowModality(QtCore.Qt.ApplicationModal)
|
|
self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint |
|
|
QtCore.Qt.WindowStaysOnTopHint)
|
|
self.INPUT_WIDGET = None
|
|
self.PARENT_OBJECT = parent
|
|
self.signalMapper = QtCore.QSignalMapper(self)
|
|
|
|
self.NO_ORD_KEY_LIST = list()
|
|
self.NO_ORD_KEY_LIST.append(QtCore.Qt.Key_Left)
|
|
self.NO_ORD_KEY_LIST.append(QtCore.Qt.Key_Up)
|
|
self.NO_ORD_KEY_LIST.append(QtCore.Qt.Key_Right)
|
|
self.NO_ORD_KEY_LIST.append(QtCore.Qt.Key_Down)
|
|
self.NO_ORD_KEY_LIST.append(QtCore.Qt.Key_Backspace)
|
|
self.NO_ORD_KEY_LIST.append(QtCore.Qt.Key_Enter)
|
|
self.NO_ORD_KEY_LIST.append(QtCore.Qt.Key_Tab)
|
|
self.NO_ORD_KEY_LIST.append(QtCore.Qt.Key_Escape)
|
|
|
|
self.do_layout(keyboard_type)
|
|
|
|
mapped = getattr(self.signalMapper, 'mappedInt', None) or self.signalMapper.mapped[int]
|
|
mapped.connect(self.buttonClicked)
|
|
|
|
def do_layout(self, keyboard_type='default'):
|
|
"""
|
|
@param keyboard_type:
|
|
@return:
|
|
"""
|
|
gl = QtWidgets.QVBoxLayout()
|
|
self.setFont(self.PARENT_OBJECT.font())
|
|
number_widget_list = []
|
|
sym_list = list(range(0, 10))
|
|
for sym in sym_list:
|
|
button = MyFlatPushButton(str(sym))
|
|
button.KEY_CHAR = ord(str(sym))
|
|
number_widget_list.append(button)
|
|
|
|
# alphabets
|
|
alpha_widget_list = []
|
|
sym_list = ['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P',
|
|
'new_row',
|
|
'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L',
|
|
'new_row',
|
|
'Z', 'X', 'C', 'V', 'B', 'N', 'M']
|
|
for sym in sym_list:
|
|
if sym == 'new_row':
|
|
alpha_widget_list.append('new_row')
|
|
else:
|
|
button = MyFlatPushButton(sym)
|
|
button.KEY_CHAR = ord(sym)
|
|
alpha_widget_list.append(button)
|
|
|
|
button = MyFlatPushButton('.')
|
|
button.KEY_CHAR = ord('.')
|
|
alpha_widget_list.append(button)
|
|
button = MyFlatPushButton('-')
|
|
button.KEY_CHAR = ord('-')
|
|
alpha_widget_list.append(button)
|
|
|
|
control_widget_list = []
|
|
|
|
button = MyFlatPushButton('LINE\nUP')
|
|
button.setToolTip('Cursor Up')
|
|
button.KEY_CHAR = QtCore.Qt.Key_Up
|
|
control_widget_list.append(button)
|
|
|
|
button = MyFlatPushButton('LINE\nDOWN')
|
|
button.setToolTip('Cursor Down')
|
|
button.KEY_CHAR = QtCore.Qt.Key_Down
|
|
control_widget_list.append(button)
|
|
|
|
# space
|
|
button = MyFlatPushButton('SPACE', min_size=(160, 50))
|
|
button.KEY_CHAR = QtCore.Qt.Key_Space
|
|
control_widget_list.append(button)
|
|
|
|
# back space
|
|
button = MyFlatPushButton('BACK')
|
|
button.setToolTip('Backspace')
|
|
button.KEY_CHAR = QtCore.Qt.Key_Backspace
|
|
control_widget_list.append(button)
|
|
|
|
# close
|
|
button = MyFlatPushButton('CLOSE')
|
|
button.setToolTip('Close Keyboard')
|
|
button.KEY_CHAR = QtCore.Qt.Key_Escape
|
|
control_widget_list.append(button)
|
|
|
|
# enter
|
|
button = MyFlatPushButton('ENTER', min_size=(105, 50))
|
|
button.setToolTip('Enter Command')
|
|
button.KEY_CHAR = QtCore.Qt.Key_Enter
|
|
control_widget_list.append(button)
|
|
|
|
MAX_COL = 10
|
|
col = 0
|
|
tlist = list()
|
|
if keyboard_type == 'numeric':
|
|
widget_list = number_widget_list
|
|
elif keyboard_type == 'alpha':
|
|
widget_list = alpha_widget_list
|
|
else:
|
|
widget_list = list()
|
|
widget_list.extend(number_widget_list)
|
|
widget_list.append('new_row')
|
|
widget_list.extend(alpha_widget_list)
|
|
|
|
widget_list.append('new_row')
|
|
widget_list.extend(control_widget_list)
|
|
|
|
for widget in widget_list:
|
|
if widget == 'new_row':
|
|
col = MAX_COL
|
|
else:
|
|
tlist.append(widget)
|
|
widget.clicked.connect(self.signalMapper.map)
|
|
self.signalMapper.setMapping(widget, widget.KEY_CHAR)
|
|
|
|
if col == MAX_COL:
|
|
col = 0
|
|
v = QtWidgets.QHBoxLayout()
|
|
v.addStretch()
|
|
v.setSpacing(5)
|
|
list(map(v.addWidget, tlist))
|
|
v.addStretch()
|
|
gl.addLayout(v)
|
|
tlist = []
|
|
else:
|
|
col += 1
|
|
|
|
v = QtWidgets.QHBoxLayout()
|
|
v.setSpacing(5)
|
|
v.addStretch()
|
|
list(map(v.addWidget, tlist))
|
|
v.addStretch()
|
|
gl.addLayout(v)
|
|
gl.setContentsMargins(4, 4, 4, 4)
|
|
gl.setSpacing(4)
|
|
gl.setSizeConstraint(gl.SetFixedSize)
|
|
|
|
self.setLayout(gl)
|
|
|
|
def reject(self):
|
|
self.buttonClicked(QtCore.Qt.Key_Escape)
|
|
|
|
def buttonClicked(self, char_ord):
|
|
w = self.INPUT_WIDGET
|
|
if char_ord in self.NO_ORD_KEY_LIST:
|
|
keyPress = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, char_ord, QtCore.Qt.NoModifier, '')
|
|
else:
|
|
keyPress = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, char_ord, QtCore.Qt.NoModifier, chr(char_ord))
|
|
# hide on enter or esc button click
|
|
if char_ord == QtCore.Qt.Key_Escape:
|
|
self.hide()
|
|
else:
|
|
# send keypress event to widget
|
|
QtWidgets.QApplication.sendEvent(w, keyPress)
|
|
if char_ord == QtCore.Qt.Key_Enter:
|
|
self.hide()
|
|
|
|
# line edit returnPressed event is triggering twice for press and release both
|
|
# that is why do not send release event for special key
|
|
if char_ord not in self.NO_ORD_KEY_LIST:
|
|
keyRelease = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, char_ord, QtCore.Qt.NoModifier, '')
|
|
QtWidgets.QApplication.sendEvent(w, keyRelease)
|
|
|
|
def show_input_panel(self, widget):
|
|
self.INPUT_WIDGET = widget
|
|
self.show()
|
|
self.update_panel_position()
|
|
self.setFocus()
|
|
self.raise_()
|
|
|
|
def update_panel_position(self):
|
|
widget = self.INPUT_WIDGET
|
|
if not widget: return
|
|
|
|
widget_rect = widget.rect()
|
|
widget_bottom = widget.mapToGlobal(QtCore.QPoint(widget.frameGeometry().x(),
|
|
widget.frameGeometry().y())).y()
|
|
screen_height = QtWidgets.QApplication.primaryScreen().availableGeometry().height()
|
|
input_panel_height = self.geometry().height() + 5
|
|
|
|
if (screen_height - widget_bottom) > input_panel_height:
|
|
# display input panel at bottom of widget
|
|
panelPos = QtCore.QPoint(widget_rect.left(), widget_rect.bottom() + 2)
|
|
else:
|
|
# display input panel at top of widget
|
|
panelPos = QtCore.QPoint(widget_rect.left(), widget_rect.top() - input_panel_height)
|
|
|
|
panelPos = widget.mapToGlobal(panelPos)
|
|
self.move(panelPos)
|
|
|
|
|
|
# a widget that calls our keyboard dialog
|
|
class TouchInterface(QtWidgets.QWidget):
|
|
def __init__(self, parent):
|
|
super(TouchInterface, self).__init__(parent)
|
|
self._PARENT_WIDGET = parent
|
|
self._input_panel_alpha = SoftInputWidget(parent, 'alpha')
|
|
self._input_panel_numeric = SoftInputWidget(parent, 'numeric')
|
|
self._input_panel_full = SoftInputWidget(parent, 'default')
|
|
self.keyboard_enable = True
|
|
self.keyboard_type = 'numeric'
|
|
|
|
def childEvent(self, event):
|
|
if event.type() == QtCore.QEvent.ChildAdded:
|
|
if isinstance(event.child(), *SIP_WIDGETS):
|
|
event.child().installEventFilter(self)
|
|
|
|
def eventFilter(self, widget, event):
|
|
try:
|
|
if self._PARENT_WIDGET.focusWidget() == widget and event.type() == QtCore.QEvent.MouseButtonPress:
|
|
if self.keyboard_enable is False:
|
|
return False
|
|
self.callDialog(widget, self.keyboard_type.lower())
|
|
return False
|
|
except:
|
|
return False
|
|
|
|
# can be class patched to call other entries - like qtvcp dialogs
|
|
def callDialog(self, widget, ktype):
|
|
if ktype == 'alpha':
|
|
self._input_panel_alpha.show_input_panel(widget)
|
|
elif ktype == 'numeric':
|
|
self._input_panel_numeric.show_input_panel(widget)
|
|
else:
|
|
self._input_panel_full.show_input_panel(widget)
|
|
|
|
## testing ##
|
|
if __name__ == '__main__':
|
|
import sys
|
|
from qtpy.QtWidgets import QWidget
|
|
app = QtWidgets.QApplication([])
|
|
w = QWidget()
|
|
w.setGeometry(100, 100, 200, 100)
|
|
w.setWindowTitle('Entry Widget')
|
|
line = QtWidgets.QLineEdit()
|
|
layout = QtWidgets.QVBoxLayout()
|
|
layout.addWidget(line)
|
|
test = TouchInterface(line)
|
|
# test.callDialog(line, 'numeric')
|
|
# test.callDialog(line, 'alpha')
|
|
test.callDialog(line, 'default')
|
|
w.setLayout(layout)
|
|
w.show()
|
|
sys.exit(app.exec())
|