4 from __future__ import with_statement
12 from PyQt4 import QtGui
15 from util import misc as misc_utils
17 from util import qui_utils
18 from util import qwrappers
19 from util import qtpie
20 from util import qtpieboard
26 _moduleLogger = logging.getLogger(__name__)
29 class Calculator(qwrappers.ApplicationWrapper):
31 def __init__(self, app):
33 self._hiddenCategories = set()
34 self._hiddenUnits = {}
35 qwrappers.ApplicationWrapper.__init__(self, app, constants)
37 def load_settings(self):
39 with open(constants._user_settings_, "r") as settingsFile:
40 settings = simplejson.load(settingsFile)
42 _moduleLogger.info("No settings")
45 _moduleLogger.info("Settings were corrupt")
48 self._fullscreenAction.setChecked(settings.get("isFullScreen", False))
50 def save_settings(self):
52 "isFullScreen": self._fullscreenAction.isChecked(),
54 with open(constants._user_settings_, "w") as settingsFile:
55 simplejson.dump(settings, settingsFile)
61 def _new_main_window(self):
62 return MainWindow(None, self)
64 @misc_utils.log_exception(_moduleLogger)
65 def _on_about(self, checked = True):
66 raise NotImplementedError("Booh")
69 class QValueEntry(object):
72 self._widget = QtGui.QLineEdit("")
73 qui_utils.mark_numbers_preferred(self._widget)
84 value = str(self._widget.text()).strip()
86 0 < value.find(whitespace)
87 for whitespace in string.whitespace
90 raise ValueError('Invalid input "%s"' % value)
93 def set_value(self, value):
96 0 < value.find(whitespace)
97 for whitespace in string.whitespace
99 raise ValueError('Invalid input "%s"' % value)
100 self._widget.setText(value)
102 def append(self, value):
103 value = value.strip()
105 0 < value.find(whitespace)
106 for whitespace in string.whitespace
108 raise ValueError('Invalid input "%s"' % value)
109 self.set_value(self.get_value() + value)
112 value = self.get_value()[0:-1]
113 self.set_value(value)
118 value = property(get_value, set_value, clear)
121 class MainWindow(qwrappers.WindowWrapper):
123 _plugin_search_paths = [
124 os.path.join(os.path.dirname(__file__), "plugins/"),
127 _user_history = "%s/history.stack" % constants._data_path_
129 def __init__(self, parent, app):
130 qwrappers.WindowWrapper.__init__(self, parent, app)
132 self._historyView = qhistory.QCalcHistory(self._app.errorLog)
133 self._userEntry = QValueEntry()
134 self._userEntry.entry.returnPressed.connect(self._on_push)
135 self._userEntryLayout = QtGui.QHBoxLayout()
136 self._userEntryLayout.addWidget(self._userEntry.toplevel, 10)
138 self._controlLayout = QtGui.QVBoxLayout()
139 self._controlLayout.setContentsMargins(0, 0, 0, 0)
140 self._controlLayout.addWidget(self._historyView.toplevel, 1000)
141 self._controlLayout.addLayout(self._userEntryLayout, 0)
143 self._keyboardTabs = QtGui.QTabWidget()
145 self._layout.addLayout(self._controlLayout)
146 self._layout.addWidget(self._keyboardTabs)
148 self._copyItemAction = QtGui.QAction(None)
149 self._copyItemAction.setText("Copy")
150 self._copyItemAction.setShortcut(QtGui.QKeySequence("CTRL+c"))
151 self._copyItemAction.triggered.connect(self._on_copy)
153 self._pasteItemAction = QtGui.QAction(None)
154 self._pasteItemAction.setText("Paste")
155 self._pasteItemAction.setShortcut(QtGui.QKeySequence("CTRL+v"))
156 self._pasteItemAction.triggered.connect(self._on_paste)
158 self._closeWindowAction = QtGui.QAction(None)
159 self._closeWindowAction.setText("Close")
160 self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
161 self._closeWindowAction.triggered.connect(self._on_close_window)
163 self._window.addAction(self._copyItemAction)
164 self._window.addAction(self._pasteItemAction)
166 self._constantPlugins = plugin_utils.ConstantPluginManager()
167 self._constantPlugins.add_path(*self._plugin_search_paths)
168 for pluginName in ["Builtins", "Trigonometry", "Computer", "Alphabet"]:
170 pluginId = self._constantPlugins.lookup_plugin(pluginName)
171 self._constantPlugins.enable_plugin(pluginId)
173 _moduleLogger.info("Failed to load plugin %s" % pluginName)
175 self._operatorPlugins = plugin_utils.OperatorPluginManager()
176 self._operatorPlugins.add_path(*self._plugin_search_paths)
177 for pluginName in ["Builtins", "Trigonometry", "Computer", "Alphabet"]:
179 pluginId = self._operatorPlugins.lookup_plugin(pluginName)
180 self._operatorPlugins.enable_plugin(pluginId)
182 _moduleLogger.info("Failed to load plugin %s" % pluginName)
184 self._keyboardPlugins = plugin_utils.KeyboardPluginManager()
185 self._keyboardPlugins.add_path(*self._plugin_search_paths)
186 self._activeKeyboards = []
188 self._history = history.RpnCalcHistory(
190 self._userEntry, self._errorDisplay,
191 self._constantPlugins.constants, self._operatorPlugins.operators
195 # Basic keyboard stuff
196 self._handler = qtpieboard.KeyboardHandler(self._on_entry_direct)
197 self._handler.register_command_handler("push", self._on_push)
198 self._handler.register_command_handler("unpush", self._on_unpush)
199 self._handler.register_command_handler("backspace", self._on_entry_backspace)
200 self._handler.register_command_handler("clear", self._on_entry_clear)
203 entryKeyboardId = self._keyboardPlugins.lookup_plugin("Entry")
204 self._keyboardPlugins.enable_plugin(entryKeyboardId)
205 entryPlugin = self._keyboardPlugins.keyboards["Entry"].construct_keyboard()
206 entryKeyboard = entryPlugin.setup(self._history, self._handler)
207 self._userEntryLayout.addWidget(entryKeyboard.toplevel)
210 self.enable_plugin(self._keyboardPlugins.lookup_plugin("Builtins"))
211 self.enable_plugin(self._keyboardPlugins.lookup_plugin("Trigonometry"))
212 self.enable_plugin(self._keyboardPlugins.lookup_plugin("Computer"))
213 self.enable_plugin(self._keyboardPlugins.lookup_plugin("Alphabet"))
215 self.set_fullscreen(self._app.fullscreenAction.isChecked())
216 self.set_orientation(self._app.orientationAction.isChecked())
218 def walk_children(self):
221 def set_orientation(self, isPortrait):
222 qwrappers.WindowWrapper.set_orientation(self, isPortrait)
224 defaultLayoutOrientation = QtGui.QBoxLayout.TopToBottom
225 tabPosition = QtGui.QTabWidget.South
227 defaultLayoutOrientation = QtGui.QBoxLayout.LeftToRight
228 tabPosition = QtGui.QTabWidget.North
229 self._layout.setDirection(defaultLayoutOrientation)
230 self._keyboardTabs.setTabPosition(tabPosition)
232 def enable_plugin(self, pluginId):
233 self._keyboardPlugins.enable_plugin(pluginId)
234 pluginData = self._keyboardPlugins.plugin_info(pluginId)
235 pluginName = pluginData[0]
236 plugin = self._keyboardPlugins.keyboards[pluginName].construct_keyboard()
237 relIcon = self._keyboardPlugins.keyboards[pluginName].icon
238 for iconPath in self._keyboardPlugins.keyboards[pluginName].iconPaths:
239 absIconPath = os.path.join(iconPath, relIcon)
240 if os.path.exists(absIconPath):
241 icon = QtGui.QIcon(absIconPath)
245 pluginKeyboard = plugin.setup(self._history, self._handler)
247 self._activeKeyboards.append({
248 "pluginName": pluginName,
250 "pluginKeyboard": pluginKeyboard,
253 self._keyboardTabs.addTab(pluginKeyboard.toplevel, pluginName)
255 self._keyboardTabs.addTab(pluginKeyboard.toplevel, icon, "")
258 qwrappers.WindowWrapper.close(self)
261 def _load_history(self):
264 with open(self._user_history, "rU") as f:
266 (part.strip() for part in line.split(" "))
267 for line in f.readlines()
272 self._history.deserialize_stack(serialized)
274 def _save_history(self):
275 serialized = self._history.serialize_stack()
276 with open(self._user_history, "w") as f:
277 for lineData in serialized:
278 line = " ".join(data for data in lineData)
279 f.write("%s\n" % line)
281 @misc_utils.log_exception(_moduleLogger)
282 def _on_child_close(self, something = None):
285 @misc_utils.log_exception(_moduleLogger)
286 def _on_copy(self, *args):
287 eqNode = self._historyView.peek()
288 resultNode = eqNode.simplify()
289 self._app._clipboard.setText(str(resultNode))
291 @misc_utils.log_exception(_moduleLogger)
292 def _on_paste(self, *args):
293 result = str(self._app._clipboard.text())
294 self._userEntry.append(result)
296 @misc_utils.log_exception(_moduleLogger)
297 def _on_entry_direct(self, keys, modifiers):
298 if "shift" in modifiers:
300 self._userEntry.append(keys)
302 @misc_utils.log_exception(_moduleLogger)
303 def _on_push(self, *args):
304 self._history.push_entry()
306 @misc_utils.log_exception(_moduleLogger)
307 def _on_unpush(self, *args):
308 self._historyView.unpush()
310 @misc_utils.log_exception(_moduleLogger)
311 def _on_entry_backspace(self, *args):
312 self._userEntry.pop()
314 @misc_utils.log_exception(_moduleLogger)
315 def _on_entry_clear(self, *args):
316 self._userEntry.clear()
318 @misc_utils.log_exception(_moduleLogger)
319 def _on_clear_all(self, *args):
320 self._history.clear()
324 app = QtGui.QApplication([])
325 handle = Calculator(app)
330 if __name__ == "__main__":
331 logging.basicConfig(level = logging.DEBUG)
333 os.makedirs(constants._data_path_)