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