from PyQt4 import QtGui, QtCore
from decimal import Decimal
# applicationle widgets
SIP_WIDGETS = [QtGui.QLineEdit]
class MyFlatPushButton(QtGui.QPushButton):
def __init__(self, caption, min_size=(50, 50)):
self.MIN_SIZE = min_size
QtGui.QPushButton.__init__(self, caption)
self.setFocusPolicy(QtCore.Qt.NoFocus)
def sizeHint(self):
return QtCore.QSize(self.MIN_SIZE[0], self.MIN_SIZE[1])
class SoftInputWidget(QtGui.QDialog):
def __init__(self, parent_object, keyboard_type='default'):
QtGui.QDialog.__init__(self)
self.setWindowFlags(QtCore.Qt.Tool | QtCore.Qt.WindowStaysOnTopHint | QtCore.Qt.FramelessWindowHint)
self.INPUT_WIDGET = None
self.PARENT_OBJECT = parent_object
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)
self.signalMapper.mapped[int].connect(self.buttonClicked)
def do_layout(self, keyboard_type='default'):
"""
@param keyboard_type:
@return:
"""
gl = QtGui.QVBoxLayout()
self.setFont(self.PARENT_OBJECT.font())
number_widget_list = []
sym_list = range(0, 10)
for sym in sym_list:
button = MyFlatPushButton(str(sym))
button.KEY_CHAR = ord(str(sym))
number_widget_list.append(button)
button = MyFlatPushButton('*')
button.KEY_CHAR = ord('*')
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)
# back space
control_widget_list = []
button = MyFlatPushButton('T')
button.KEY_CHAR = QtCore.Qt.Key_Tab
control_widget_list.append(button)
control_widget_list.append('sep')
# space
button = MyFlatPushButton('SPC')
button.KEY_CHAR = QtCore.Qt.Key_Space
control_widget_list.append(button)
control_widget_list.append('sep')
# close
button = MyFlatPushButton('X')
button.KEY_CHAR = QtCore.Qt.Key_Escape
control_widget_list.append(button)
control_widget_list.append('sep')
# enter
button = MyFlatPushButton('ENT', min_size=(110, 60))
button.KEY_CHAR = QtCore.Qt.Key_Enter
control_widget_list.append(button)
control_widget_list.append('sep')
alist = list()
alist.append((QtCore.Qt.Key_Left, 'L-A'))
alist.append((QtCore.Qt.Key_Right, 'R-A'))
alist.append((QtCore.Qt.Key_Up, 'U-A'))
alist.append((QtCore.Qt.Key_Down, 'D-A'))
for key in alist:
button = MyFlatPushButton(key[1])
button.KEY_CHAR = key[0]
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
elif widget == 'sep':
tlist.append(self.get_vline())
continue
else:
tlist.append(widget)
widget.clicked.connect(self.signalMapper.map)
self.signalMapper.setMapping(widget, widget.KEY_CHAR)
if col == MAX_COL:
col = 0
v = QtGui.QHBoxLayout()
v.addStretch()
v.setSpacing(5)
map(v.addWidget, tlist)
v.addStretch()
gl.addLayout(v)
tlist = []
else:
col += 1
v = QtGui.QHBoxLayout()
v.setSpacing(5)
v.addStretch()
map(v.addWidget, tlist)
v.addStretch()
gl.addLayout(v)
gl.setContentsMargins(0, 0, 0, 0)
gl.setSpacing(5)
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))
# send keypress event to widget
QtGui.QApplication.sendEvent(w, keyPress)
# line edit returnPressed event is triggering twise 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, '')
QtGui.QApplication.sendEvent(w, keyRelease)
# hide on enter or esc button click
if char_ord in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Escape):
self.hide()
def show_input_panel(self, widget):
self.INPUT_WIDGET = widget
self.show()
self.update_panel_position()
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 = QtGui.qApp.desktop().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)
def _get_line(self, vertical=True):
line = QtGui.QFrame()
line.setContentsMargins(0, 0, 0, 0)
if vertical is True:
line.setFrameShape(line.VLine)
else:
line.setFrameShape(line.HLine)
line.setFrameShadow(line.Sunken)
return line
def get_hline(self):
return self._get_line(vertical=False)
def get_vline(self):
return self._get_line()
class TouchInterface(QtCore.QObject):
def __init__(self, PARENT_WIDGET):
QtCore.QObject.__init__(self)
self._PARENT_WIDGET = PARENT_WIDGET
self._input_panel_all = SoftInputWidget(PARENT_WIDGET, 'default')
self._input_panel_numeric = SoftInputWidget(PARENT_WIDGET, '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):
if self._PARENT_WIDGET.focusWidget() == widget and event.type() == QtCore.QEvent.MouseButtonPress:
if hasattr(widget, 'keyboard_type'):
if widget.keyboard_type == 'default':
self._input_panel_all.show_input_panel(widget)
elif widget.keyboard_type == 'numeric':
self._input_panel_numeric.show_input_panel(widget)
return False
class TouchInputWidget(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.touch_interface = TouchInterface(self)
def childEvent(self, event):
self.touch_interface.childEvent(event)
def eventFilter(self, widget, event):
return self.touch_interface.eventFilter(widget, event)
class ExampleWidget(TouchInputWidget):
def __init__(self):
TouchInputWidget.__init__(self)
self.txtNumeric = QtGui.QLineEdit()
# actiate touch input
self.txtNumeric.keyboard_type = 'numeric'
self.txtText = QtGui.QLineEdit()
# activate touch input
self.txtText.keyboard_type = 'default'
gl = QtGui.QVBoxLayout()
gl.addWidget(self.txtNumeric)
gl.addWidget(self.txtText)
self.setWindowTitle('Touch Input Demo')
self.setLayout(gl)
if __name__ == '__main__':
app = QtGui.QApplication([])
ExampleWidget().show()
app.exec_()