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