f51cbb71b0c83d4746fdb9ced1b51deec53883c7
[ejpi] / src / libraries / qtpieboard.py
1 #!/usr/bin/env python
2
3
4 from __future__ import division
5
6 import os
7 import warnings
8
9 from PyQt4 import QtGui
10 from PyQt4 import QtCore
11
12 import qtpie
13
14
15 class PieKeyboard(object):
16
17         SLICE_CENTER = -1
18         SLICE_NORTH = 0
19         SLICE_NORTH_WEST = 1
20         SLICE_WEST = 2
21         SLICE_SOUTH_WEST = 3
22         SLICE_SOUTH = 4
23         SLICE_SOUTH_EAST = 5
24         SLICE_EAST = 6
25         SLICE_NORTH_EAST = 7
26
27         MAX_ANGULAR_SLICES = 8
28
29         SLICE_DIRECTIONS = [
30                 SLICE_CENTER,
31                 SLICE_NORTH,
32                 SLICE_NORTH_WEST,
33                 SLICE_WEST,
34                 SLICE_SOUTH_WEST,
35                 SLICE_SOUTH,
36                 SLICE_SOUTH_EAST,
37                 SLICE_EAST,
38                 SLICE_NORTH_EAST,
39         ]
40
41         SLICE_DIRECTION_NAMES = [
42                 "CENTER",
43                 "NORTH",
44                 "NORTH_WEST",
45                 "WEST",
46                 "SOUTH_WEST",
47                 "SOUTH",
48                 "SOUTH_EAST",
49                 "EAST",
50                 "NORTH_EAST",
51         ]
52
53         def __init__(self, rows, columns):
54                 self._layout = QtGui.QGridLayout()
55
56                 self.__cells = {}
57
58         @property
59         def toplevel(self):
60                 return self._layout
61
62         def add_pie(self, row, column, pieButton):
63                 assert len(pieButton) == 8
64                 self._layout.addWidget(pieButton, row, column)
65                 self.__cells[(row, column)] = pieButton
66
67         def get_pie(self, row, column):
68                 return self.__cells[(row, column)]
69
70
71 class KeyboardModifier(object):
72
73         def __init__(self, name):
74                 self.name = name
75                 self.lock = False
76                 self.once = False
77
78         @property
79         def isActive(self):
80                 return self.lock or self.once
81
82         def on_toggle_lock(self, *args, **kwds):
83                 self.lock = not self.lock
84
85         def on_toggle_once(self, *args, **kwds):
86                 self.once = not self.once
87
88         def reset_once(self):
89                 self.once = False
90
91
92 def parse_keyboard_data(text):
93         return eval(text)
94
95
96 def _enumerate_pie_slices(pieData, iconPaths):
97         for direction, directionName in zip(
98                 PieKeyboard.SLICE_DIRECTIONS, PieKeyboard.SLICE_DIRECTION_NAMES
99         ):
100                 if directionName in pieData:
101                         sliceData = pieData[directionName]
102
103                         action = QtGui.QAction(None)
104                         try:
105                                 action.setText(sliceData["text"])
106                         except KeyError:
107                                 pass
108                         try:
109                                 relativeIconPath = sliceData["path"]
110                         except KeyError:
111                                 pass
112                         else:
113                                 for iconPath in iconPaths:
114                                         absIconPath = os.path.join(iconPath, relativeIconPath)
115                                         if os.path.exists(absIconPath):
116                                                 action.setIcon(QtGui.QIcon(absIconPath))
117                                                 break
118                         pieItem = qtpie.QActionPieItem(action)
119                         actionToken = sliceData["action"]
120                 else:
121                         pieItem = qtpie.PieFiling.NULL_CENTER
122                         actionToken = ""
123                 yield direction, pieItem, actionToken
124
125
126 def load_keyboard(keyboardName, dataTree, keyboard, keyboardHandler, iconPaths):
127         for (row, column), pieData in dataTree.iteritems():
128                 pieItems = list(_enumerate_pie_slices(pieData, iconPaths))
129                 assert pieItems[0][0] == PieKeyboard.SLICE_CENTER, pieItems[0]
130                 _, center, centerAction = pieItems.pop(0)
131
132                 pieButton = qtpie.QPieButton(center)
133                 pieButton.set_center(center)
134                 keyboardHandler.map_slice_action(center, centerAction)
135                 for direction, pieItem, action in pieItems:
136                         pieButton.insertItem(pieItem)
137                         keyboardHandler.map_slice_action(pieItem, action)
138                 keyboard.add_pie(row, column, pieButton)
139
140
141 class KeyboardHandler(object):
142
143         def __init__(self, keyhandler):
144                 self.__keyhandler = keyhandler
145                 self.__commandHandlers = {}
146                 self.__modifiers = {}
147                 self.__sliceActions = {}
148
149                 self.register_modifier("Shift")
150                 self.register_modifier("Super")
151                 self.register_modifier("Control")
152                 self.register_modifier("Alt")
153
154         def register_command_handler(self, command, handler):
155                 # @todo Look into hooking these up directly to the pie actions
156                 self.__commandHandlers["[%s]" % command] = handler
157
158         def unregister_command_handler(self, command):
159                 # @todo Look into hooking these up directly to the pie actions
160                 del self.__commandHandlers["[%s]" % command]
161
162         def register_modifier(self, modifierName):
163                 mod = KeyboardModifier(modifierName)
164                 self.register_command_handler(modifierName, mod.on_toggle_lock)
165                 self.__modifiers["<%s>" % modifierName] = mod
166
167         def unregister_modifier(self, modifierName):
168                 self.unregister_command_handler(modifierName)
169                 del self.__modifiers["<%s>" % modifierName]
170
171         def map_slice_action(self, slice, action):
172                 callback = lambda direction: self(direction, action)
173                 slice.action().triggered.connect(callback)
174                 self.__sliceActions[slice] = (action, callback)
175
176         def __call__(self, direction, action):
177                 activeModifiers = [
178                         mod.name
179                         for mod in self.__modifiers.itervalues()
180                                 if mod.isActive
181                 ]
182
183                 needResetOnce = False
184                 if action.startswith("[") and action.endswith("]"):
185                         commandName = action[1:-1]
186                         if action in self.__commandHandlers:
187                                 self.__commandHandlers[action](commandName, activeModifiers)
188                                 needResetOnce = True
189                         else:
190                                 warnings.warn("Unknown command: [%s]" % commandName)
191                 elif action.startswith("<") and action.endswith(">"):
192                         modName = action[1:-1]
193                         for mod in self.__modifiers.itervalues():
194                                 if mod.name == modName:
195                                         mod.on_toggle_once()
196                                         break
197                         else:
198                                 warnings.warn("Unknown modifier: <%s>" % modName)
199                 else:
200                         self.__keyhandler(action, activeModifiers)
201                         needResetOnce = True
202
203                 if needResetOnce:
204                         for mod in self.__modifiers.itervalues():
205                                 mod.reset_once()