5 #@todo Research optimizations
7 from __future__ import with_statement
15 from PyQt4 import QtGui
16 from PyQt4 import QtCore
20 from util import misc as misc_utils
24 _moduleLogger = logging.getLogger(__name__)
30 def split_number(number):
32 # Optimize the startup case
36 fractional, integer = math.modf(number)
38 integerDisplay = number
39 fractionalDisplay = ""
41 integerDisplay = str(integer)
42 fractionalDisplay = str(fractional)
43 if "e+" in integerDisplay:
44 integerDisplay = number
45 fractionalDisplay = ""
46 elif "e-" in fractionalDisplay:
48 integerDisplay = number
49 fractionalDisplay = ""
52 fractionalDisplay = number
54 integerDisplay = integerDisplay[0:-2] + "."
55 fractionalDisplay = fractionalDisplay[2:]
57 return integerDisplay, fractionalDisplay
60 class Gonvert(object):
63 os.path.dirname(__file__),
64 os.path.join(os.path.dirname(__file__), "../share"),
65 os.path.join(os.path.dirname(__file__), "../data"),
70 def __init__(self, app):
72 for dataPath in self._DATA_PATHS:
73 appIconPath = os.path.join(dataPath, "pixmaps", "gonvert.png")
74 if os.path.isfile(appIconPath):
75 self._dataPath = dataPath
78 raise RuntimeError("UI Descriptor not found!")
80 self._appIconPath = appIconPath
82 self._hiddenCategories = set()
83 self._hiddenUnits = {}
84 self._clipboard = QtGui.QApplication.clipboard()
86 self._jumpWindow = None
87 self._recentWindow = None
88 self._mainWindow = None
89 self._catWindow = None
90 self._quickWindow = None
92 self._on_jump_close = lambda obj = None: self._on_child_close("_jumpWindow", obj)
93 self._on_recent_close = lambda obj = None: self._on_child_close("_recentWindow", obj)
94 self._on_cat_close = lambda obj = None: self._on_child_close("_catWindow", obj)
95 self._on_quick_close = lambda obj = None: self._on_child_close("_quickWindow", obj)
97 self._condensedAction = QtGui.QAction(None)
98 self._condensedAction.setText("Condensed View")
99 self._condensedAction.setCheckable(True)
100 self._condensedAction.triggered.connect(self._on_condensed_start)
102 self._jumpAction = QtGui.QAction(None)
103 self._jumpAction.setText("Quick Jump")
104 self._jumpAction.setStatusTip("Search for a unit and jump straight to it")
105 self._jumpAction.setToolTip("Search for a unit and jump straight to it")
106 self._jumpAction.setShortcut(QtGui.QKeySequence("CTRL+j"))
107 self._jumpAction.triggered.connect(self._on_jump_start)
109 self._recentAction = QtGui.QAction(None)
110 self._recentAction.setText("Recent Units")
111 self._recentAction.setStatusTip("View the recent units")
112 self._recentAction.setToolTip("View the recent units")
113 self._recentAction.setShortcut(QtGui.QKeySequence("CTRL+r"))
114 self._recentAction.triggered.connect(self._on_recent_start)
116 self._fullscreenAction = QtGui.QAction(None)
117 self._fullscreenAction.setText("Fullscreen")
118 self._fullscreenAction.setCheckable(True)
119 self._fullscreenAction.setShortcut(QtGui.QKeySequence("CTRL+Enter"))
120 self._fullscreenAction.toggled.connect(self._on_toggle_fullscreen)
122 self._showFavoritesAction = QtGui.QAction(None)
123 self._showFavoritesAction.setCheckable(True)
124 self._showFavoritesAction.setText("Favorites Only")
126 self._sortActionGroup = QtGui.QActionGroup(None)
127 self._sortByNameAction = QtGui.QAction(self._sortActionGroup)
128 self._sortByNameAction.setText("Sort By Name")
129 self._sortByNameAction.setStatusTip("Sort the units by name")
130 self._sortByNameAction.setToolTip("Sort the units by name")
131 self._sortByNameAction.setCheckable(True)
132 self._sortByValueAction = QtGui.QAction(self._sortActionGroup)
133 self._sortByValueAction.setText("Sort By Value")
134 self._sortByValueAction.setStatusTip("Sort the units by value")
135 self._sortByValueAction.setToolTip("Sort the units by value")
136 self._sortByValueAction.setCheckable(True)
137 self._sortByUnitAction = QtGui.QAction(self._sortActionGroup)
138 self._sortByUnitAction.setText("Sort By Unit")
139 self._sortByUnitAction.setStatusTip("Sort the units by unit")
140 self._sortByUnitAction.setToolTip("Sort the units by unit")
141 self._sortByUnitAction.setCheckable(True)
143 self._sortByNameAction.setChecked(True)
145 self._logAction = QtGui.QAction(None)
146 self._logAction.setText("Log")
147 self._logAction.setShortcut(QtGui.QKeySequence("CTRL+l"))
148 self._logAction.triggered.connect(self._on_log)
150 self._quitAction = QtGui.QAction(None)
151 self._quitAction.setText("Quit")
152 self._quitAction.setShortcut(QtGui.QKeySequence("CTRL+q"))
153 self._quitAction.triggered.connect(self._on_quit)
155 self._app.lastWindowClosed.connect(self._on_app_quit)
158 self.request_category()
160 self._mainWindow.select_category(self._recent[-1][0])
162 def request_category(self):
164 if self._condensedAction.isChecked():
165 if self._catWindow is not None:
166 self._catWindow.hide()
168 if self._quickWindow is None:
169 self._quickWindow = QuickConvert(None, self)
170 self._quickWindow.window.destroyed.connect(self._on_quick_close)
172 self._quickWindow.show()
174 self._mainWindow = self._quickWindow
176 if self._quickWindow is not None:
177 self._quickWindow.hide()
179 if self._catWindow is None:
180 self._catWindow = CategoryWindow(None, self)
181 self._catWindow.window.destroyed.connect(self._on_cat_close)
183 self._catWindow.window.show()
185 self._mainWindow = self._catWindow
187 return self._mainWindow
189 def search_units(self):
190 jumpWindow = QuickJump(None, self)
191 jumpWindow.window.destroyed.connect(self._on_jump_close)
192 self._jumpWindow = jumpWindow
193 return self._jumpWindow
195 def show_recent(self):
196 recentWindow = Recent(None, self)
197 recentWindow.window.destroyed.connect(self._on_recent_close)
198 self._recentWindow = recentWindow
199 return self._recentWindow
201 def add_recent(self, categoryName, unitName):
202 catUnit = categoryName, unitName
204 self._recent.remove(catUnit)
206 pass # ignore if its not already in the recent history
207 assert catUnit not in self._recent
208 self._recent.append(catUnit)
210 def get_recent_unit(self, categoryName, fromMostRecent = 0):
212 for catName, unitName in reversed(self._recent):
213 if catName == categoryName:
214 recentUnitName = unitName
215 if fromMostRecent <= 0:
219 return recentUnitName
221 def get_recent(self):
222 return reversed(self._recent)
225 def hiddenCategories(self):
226 return self._hiddenCategories
228 def get_hidden_units(self, categoryName):
230 return self._hiddenUnits[categoryName]
232 self._hiddenUnits[categoryName] = set()
233 return self._hiddenUnits[categoryName]
235 def load_settings(self):
237 with open(constants._user_settings_, "r") as settingsFile:
238 settings = simplejson.load(settingsFile)
240 _moduleLogger.info("No settings")
243 _moduleLogger.info("Settings were corrupt")
246 self._fullscreenAction.setChecked(settings.get("isFullScreen", False))
248 sortBy = settings.get("sortBy", "name")
249 if sortBy not in ["name", "value", "unit"]:
250 _moduleLogger.info("Setting sortBy is not a valid value: %s" % sortBy)
253 self._sortByNameAction.setChecked(True)
254 self._sortByValueAction.setChecked(False)
255 self._sortByUnitAction.setChecked(False)
256 elif sortBy == "value":
257 self._sortByNameAction.setChecked(False)
258 self._sortByValueAction.setChecked(True)
259 self._sortByUnitAction.setChecked(False)
260 elif sortBy == "unit":
261 self._sortByNameAction.setChecked(False)
262 self._sortByValueAction.setChecked(False)
263 self._sortByUnitAction.setChecked(True)
265 raise RuntimeError("How did this sortBy come about? %s" % sortBy)
267 recent = settings.get("recent", self._recent)
268 for category, unit in recent:
269 self.add_recent(category, unit)
271 self._hiddenCategories = set(settings.get("hiddenCategories", set()))
272 self._hiddenUnits = dict(
273 (catName, set(units))
274 for (catName, units) in settings.get("hiddenUnits", {}).iteritems()
277 self._showFavoritesAction.setChecked(settings.get("showFavorites", True))
279 self._condensedAction.setChecked(settings.get("useQuick", self._condensedAction.isChecked()))
281 def save_settings(self):
282 if self._sortByNameAction.isChecked():
284 elif self._sortByValueAction.isChecked():
286 elif self._sortByUnitAction.isChecked():
289 raise RuntimeError("Unknown sorting value")
291 "isFullScreen": self._fullscreenAction.isChecked(),
292 "recent": self._recent,
293 "hiddenCategories": list(self._hiddenCategories),
295 (catName, list(units))
296 for (catName, units) in self._hiddenUnits.iteritems()
298 "showFavorites": self._showFavoritesAction.isChecked(),
299 "useQuick": self._condensedAction.isChecked(),
302 with open(constants._user_settings_, "w") as settingsFile:
303 simplejson.dump(settings, settingsFile)
306 def appIconPath(self):
307 return self._appIconPath
310 def jumpAction(self):
311 return self._jumpAction
314 def recentAction(self):
315 return self._recentAction
318 def fullscreenAction(self):
319 return self._fullscreenAction
322 def condensedAction(self):
323 return self._condensedAction
326 def sortByNameAction(self):
327 return self._sortByNameAction
330 def sortByValueAction(self):
331 return self._sortByValueAction
334 def sortByUnitAction(self):
335 return self._sortByUnitAction
339 return self._logAction
342 def quitAction(self):
343 return self._quitAction
346 def showFavoritesAction(self):
347 return self._showFavoritesAction
349 def _walk_children(self):
350 if self._catWindow is not None:
351 yield self._catWindow
352 if self._quickWindow is not None:
353 yield self._quickWindow
354 if self._jumpWindow is not None:
355 yield self._jumpWindow
356 if self._recentWindow is not None:
357 yield self._recentWindow
359 def _close_windows(self):
360 if self._catWindow is not None:
361 self._catWindow.window.destroyed.disconnect(self._on_cat_close)
362 self._catWindow.close()
363 self._catWindow = None
364 if self._quickWindow is not None:
365 self._quickWindow.window.destroyed.disconnect(self._on_quick_close)
366 self._quickWindow.close()
367 self._quickWindow = None
368 if self._jumpWindow is not None:
369 self._jumpWindow.window.destroyed.disconnect(self._on_jump_close)
370 self._jumpWindow.close()
371 self._jumpWindow = None
372 if self._recentWindow is not None:
373 self._recentWindow.window.destroyed.disconnect(self._on_recent_close)
374 self._recentWindow.close()
375 self._recentWindow = None
377 @misc_utils.log_exception(_moduleLogger)
378 def _on_app_quit(self, checked = False):
381 @misc_utils.log_exception(_moduleLogger)
382 def _on_child_close(self, name, obj = None):
383 if not hasattr(self, name):
384 _moduleLogger.info("Something weird going on when we don't have a %s" % name)
386 setattr(self, name, None)
388 @misc_utils.log_exception(_moduleLogger)
389 def _on_toggle_fullscreen(self, checked = False):
390 for window in self._walk_children():
391 window.set_fullscreen(checked)
393 @misc_utils.log_exception(_moduleLogger)
394 def _on_condensed_start(self, checked = False):
395 self.request_category()
397 self._mainWindow.select_category(self._recent[-1][0])
399 @misc_utils.log_exception(_moduleLogger)
400 def _on_jump_start(self, checked = False):
403 @misc_utils.log_exception(_moduleLogger)
404 def _on_recent_start(self, checked = False):
407 @misc_utils.log_exception(_moduleLogger)
408 def _on_log(self, checked = False):
409 with open(constants._user_logpath_, "r") as f:
410 logLines = f.xreadlines()
411 log = "".join(logLines)
412 self._clipboard.setText(log)
414 @misc_utils.log_exception(_moduleLogger)
415 def _on_quit(self, checked = False):
416 self._close_windows()
419 class QuickJump(object):
423 def __init__(self, parent, app):
426 self._searchLabel = QtGui.QLabel("Search:")
427 self._searchEntry = QtGui.QLineEdit("")
428 self._searchEntry.textEdited.connect(self._on_search_edited)
430 self._entryLayout = QtGui.QHBoxLayout()
431 self._entryLayout.addWidget(self._searchLabel)
432 self._entryLayout.addWidget(self._searchEntry)
434 self._resultsBox = QtGui.QTreeWidget()
435 self._resultsBox.setHeaderLabels(["Categories", "Units"])
436 self._resultsBox.setHeaderHidden(True)
438 self._resultsBox.setAlternatingRowColors(True)
439 self._resultsBox.itemClicked.connect(self._on_result_clicked)
441 self._layout = QtGui.QVBoxLayout()
442 self._layout.addLayout(self._entryLayout)
443 self._layout.addWidget(self._resultsBox)
445 centralWidget = QtGui.QWidget()
446 centralWidget.setLayout(self._layout)
448 self._window = QtGui.QMainWindow(parent)
449 self._window.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
450 maeqt.set_autorient(self._window, True)
451 maeqt.set_stackable(self._window, True)
452 self._window.setWindowTitle("%s - Quick Jump" % constants.__pretty_app_name__)
453 self._window.setWindowIcon(QtGui.QIcon(self._app.appIconPath))
454 self._window.setCentralWidget(centralWidget)
456 self._closeWindowAction = QtGui.QAction(None)
457 self._closeWindowAction.setText("Close")
458 self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
459 self._closeWindowAction.triggered.connect(self._on_close_window)
462 self._window.addAction(self._closeWindowAction)
463 self._window.addAction(self._app.quitAction)
464 self._window.addAction(self._app.fullscreenAction)
466 fileMenu = self._window.menuBar().addMenu("&Units")
467 fileMenu.addAction(self._closeWindowAction)
468 fileMenu.addAction(self._app.quitAction)
470 viewMenu = self._window.menuBar().addMenu("&View")
471 viewMenu.addAction(self._app.fullscreenAction)
473 self._window.addAction(self._app.logAction)
475 self.set_fullscreen(self._app.fullscreenAction.isChecked())
491 def set_fullscreen(self, isFullscreen):
493 self._window.showFullScreen()
495 self._window.showNormal()
497 @misc_utils.log_exception(_moduleLogger)
498 def _on_close_window(self, checked = True):
501 @misc_utils.log_exception(_moduleLogger)
502 def _on_result_clicked(self, item, columnIndex):
503 categoryName = unicode(item.text(0))
504 unitName = unicode(item.text(1))
505 catWindow = self._app.request_category()
506 unitsWindow = catWindow.select_category(categoryName)
507 unitsWindow.select_unit(unitName)
510 @misc_utils.log_exception(_moduleLogger)
511 def _on_search_edited(self, *args):
512 userInput = self._searchEntry.text()
513 if len(userInput) < self.MINIMAL_ENTRY:
516 self._resultsBox.clear()
517 lowerInput = str(userInput).lower()
518 for catIndex, category in enumerate(unit_data.UNIT_CATEGORIES):
519 units = unit_data.get_units(category)
520 for unitIndex, unit in enumerate(units):
521 loweredUnit = unit.lower()
522 if lowerInput in loweredUnit:
523 twi = QtGui.QTreeWidgetItem(self._resultsBox)
524 twi.setText(0, category)
528 class Recent(object):
530 def __init__(self, parent, app):
533 self._resultsBox = QtGui.QTreeWidget()
534 self._resultsBox.setHeaderLabels(["Categories", "Units"])
535 self._resultsBox.setHeaderHidden(True)
537 self._resultsBox.setAlternatingRowColors(True)
538 self._resultsBox.itemClicked.connect(self._on_result_clicked)
540 self._layout = QtGui.QVBoxLayout()
541 self._layout.addWidget(self._resultsBox)
543 centralWidget = QtGui.QWidget()
544 centralWidget.setLayout(self._layout)
546 self._window = QtGui.QMainWindow(parent)
547 self._window.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
548 maeqt.set_autorient(self._window, True)
549 maeqt.set_stackable(self._window, True)
550 self._window.setWindowTitle("%s - Recent" % constants.__pretty_app_name__)
551 self._window.setWindowIcon(QtGui.QIcon(self._app.appIconPath))
552 self._window.setCentralWidget(centralWidget)
554 for cat, unit in self._app.get_recent():
555 twi = QtGui.QTreeWidgetItem(self._resultsBox)
559 self._closeWindowAction = QtGui.QAction(None)
560 self._closeWindowAction.setText("Close")
561 self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
562 self._closeWindowAction.triggered.connect(self._on_close_window)
565 self._window.addAction(self._closeWindowAction)
566 self._window.addAction(self._app.quitAction)
567 self._window.addAction(self._app.fullscreenAction)
569 fileMenu = self._window.menuBar().addMenu("&Units")
570 fileMenu.addAction(self._closeWindowAction)
571 fileMenu.addAction(self._app.quitAction)
573 viewMenu = self._window.menuBar().addMenu("&View")
574 viewMenu.addAction(self._app.fullscreenAction)
576 self._window.addAction(self._app.logAction)
578 self.set_fullscreen(self._app.fullscreenAction.isChecked())
594 def set_fullscreen(self, isFullscreen):
596 self._window.showFullScreen()
598 self._window.showNormal()
600 @misc_utils.log_exception(_moduleLogger)
601 def _on_close_window(self, checked = True):
604 @misc_utils.log_exception(_moduleLogger)
605 def _on_result_clicked(self, item, columnIndex):
606 categoryName = unicode(item.text(0))
607 unitName = unicode(item.text(1))
608 catWindow = self._app.request_category()
609 unitsWindow = catWindow.select_category(categoryName)
610 unitsWindow.select_unit(unitName)
614 class QuickConvert(object):
616 def __init__(self, parent, app):
618 self._categoryName = ""
619 self._inputUnitName = ""
620 self._outputUnitName = ""
622 self._favoritesWindow = None
624 self._inputUnitValue = QtGui.QLineEdit()
625 maeqt.mark_numbers_preferred(self._inputUnitValue)
626 self._inputUnitValue.textEdited.connect(self._on_value_edited)
627 self._inputUnitSymbol = QtGui.QLabel()
629 self._outputUnitValue = QtGui.QLineEdit()
630 maeqt.mark_numbers_preferred(self._outputUnitValue)
631 self._outputUnitValue.textEdited.connect(self._on_output_value_edited)
632 self._outputUnitSymbol = QtGui.QLabel()
634 self._conversionLayout = QtGui.QHBoxLayout()
635 self._conversionLayout.addWidget(self._inputUnitValue)
636 self._conversionLayout.addWidget(self._inputUnitSymbol)
637 self._conversionLayout.addWidget(self._outputUnitValue)
638 self._conversionLayout.addWidget(self._outputUnitSymbol)
640 self._categoryView = QtGui.QTreeWidget()
641 self._categoryView.setHeaderLabels(["Categories"])
642 self._categoryView.setHeaderHidden(False)
644 self._categoryView.setAlternatingRowColors(True)
645 self._categoryView.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
646 self._categoryView.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
647 for catName in unit_data.UNIT_CATEGORIES:
648 twi = QtGui.QTreeWidgetItem(self._categoryView)
649 twi.setText(0, catName)
650 self._categorySelection = self._categoryView.selectionModel()
651 self._categorySelection.selectionChanged.connect(self._on_category_selection_changed)
653 self._inputView = QtGui.QTreeWidget()
654 self._inputView.setHeaderLabels(["From", "Name"])
655 self._inputView.setHeaderHidden(False)
656 self._inputView.header().hideSection(1)
658 self._inputView.setAlternatingRowColors(True)
659 self._inputView.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
660 self._inputView.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
661 self._inputSelection = self._inputView.selectionModel()
662 self._inputSelection.selectionChanged.connect(self._on_input_selection_changed)
664 self._outputView = QtGui.QTreeWidget()
665 self._outputView.setHeaderLabels(["To", "Name"])
666 self._outputView.setHeaderHidden(False)
667 self._outputView.header().hideSection(1)
669 self._outputView.setAlternatingRowColors(True)
670 self._outputView.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
671 self._outputView.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
672 self._outputWidgets = []
673 self._outputSelection = self._outputView.selectionModel()
674 self._outputSelection.selectionChanged.connect(self._on_output_selection_changed)
676 self._selectionLayout = QtGui.QHBoxLayout()
677 self._selectionLayout.addWidget(self._categoryView)
678 self._selectionLayout.addWidget(self._inputView)
679 self._selectionLayout.addWidget(self._outputView)
681 self._layout = QtGui.QVBoxLayout()
682 self._layout.addLayout(self._conversionLayout)
683 self._layout.addLayout(self._selectionLayout)
685 centralWidget = QtGui.QWidget()
686 centralWidget.setLayout(self._layout)
688 self._window = QtGui.QMainWindow(parent)
689 self._window.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
690 maeqt.set_autorient(self._window, True)
691 maeqt.set_stackable(self._window, True)
692 self._window.setWindowTitle("%s - Quick Convert" % (constants.__pretty_app_name__, ))
693 self._window.setWindowIcon(QtGui.QIcon(app.appIconPath))
694 self._window.setCentralWidget(centralWidget)
696 self._chooseCatFavoritesAction = QtGui.QAction(None)
697 self._chooseCatFavoritesAction.setText("Select Categories")
698 self._chooseCatFavoritesAction.triggered.connect(self._on_choose_category_favorites)
700 self._chooseUnitFavoritesAction = QtGui.QAction(None)
701 self._chooseUnitFavoritesAction.setText("Select Units")
702 self._chooseUnitFavoritesAction.triggered.connect(self._on_choose_unit_favorites)
703 self._chooseUnitFavoritesAction.setEnabled(False)
705 self._app.showFavoritesAction.toggled.connect(self._on_show_favorites)
707 self._closeWindowAction = QtGui.QAction(None)
708 self._closeWindowAction.setText("Close Window")
709 self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
710 self._closeWindowAction.triggered.connect(self._on_close_window)
713 self._window.addAction(self._closeWindowAction)
714 self._window.addAction(self._app.quitAction)
715 self._window.addAction(self._app.fullscreenAction)
717 fileMenu = self._window.menuBar().addMenu("&Units")
718 fileMenu.addAction(self._chooseCatFavoritesAction)
719 fileMenu.addAction(self._chooseUnitFavoritesAction)
721 viewMenu = self._window.menuBar().addMenu("&View")
722 viewMenu.addAction(self._app.showFavoritesAction)
723 viewMenu.addAction(self._app.condensedAction)
724 viewMenu.addSeparator()
725 viewMenu.addAction(self._app.jumpAction)
726 viewMenu.addAction(self._app.recentAction)
728 fileMenu = self._window.menuBar().addMenu("&Units")
729 fileMenu.addAction(self._chooseCatFavoritesAction)
730 fileMenu.addAction(self._chooseUnitFavoritesAction)
731 fileMenu.addAction(self._closeWindowAction)
732 fileMenu.addAction(self._app.quitAction)
734 viewMenu = self._window.menuBar().addMenu("&View")
735 viewMenu.addAction(self._app.showFavoritesAction)
736 viewMenu.addAction(self._app.condensedAction)
737 viewMenu.addSeparator()
738 viewMenu.addAction(self._app.jumpAction)
739 viewMenu.addAction(self._app.recentAction)
740 viewMenu.addSeparator()
741 viewMenu.addAction(self._app.fullscreenAction)
743 self._window.addAction(self._app.logAction)
745 self._update_favorites()
746 self.set_fullscreen(self._app.fullscreenAction.isChecked())
762 def set_fullscreen(self, isFullscreen):
764 self._window.showFullScreen()
766 self._window.showNormal()
768 def select_category(self, categoryName):
769 self._select_category(categoryName)
771 i = unit_data.UNIT_CATEGORIES.index(categoryName)
772 rootIndex = self._categoryView.rootIndex()
773 currentIndex = self._categoryView.model().index(i, 0, rootIndex)
774 self._categoryView.scrollTo(currentIndex)
775 self._categoryView.setItemSelected(self._categoryView.topLevelItem(i), True)
779 def select_unit(self, name):
780 self.select_input(name)
783 def select_input(self, name):
784 self._select_input(name)
786 i = self._unitNames.index(name)
787 rootIndex = self._inputView.rootIndex()
788 currentIndex = self._inputView.model().index(i, 0, rootIndex)
789 self._inputView.scrollTo(currentIndex)
790 self._inputView.setItemSelected(self._inputView.topLevelItem(i), True)
792 def select_output(self, name):
793 self._select_output(name)
795 i = self._unitNames.index(name)
796 rootIndex = self._outputView.rootIndex()
797 currentIndex = self._outputView.model().index(i, 0, rootIndex)
798 self._outputView.scrollTo(currentIndex)
799 self._outputView.setItemSelected(self._outputView.topLevelItem(i), True)
801 def _select_category(self, categoryName):
802 self._inputUnitName = ""
803 self._outputUnitName = ""
804 self._inputUnitValue.setText("")
805 self._inputUnitSymbol.setText("")
806 self._inputView.clear()
807 self._outputUnitValue.setText("")
808 self._outputUnitSymbol.setText("")
809 self._outputView.clear()
810 self._categoryName = categoryName
811 self._chooseUnitFavoritesAction.setEnabled(True)
813 unitData = unit_data.UNIT_DESCRIPTIONS[categoryName]
814 self._unitNames = list(unit_data.get_units(categoryName))
815 self._unitNames.sort()
816 for key in self._unitNames:
817 conversion, unit, description = unitData[key]
820 twi = QtGui.QTreeWidgetItem(self._inputView)
824 twi = QtGui.QTreeWidgetItem(self._outputView)
828 defaultInputUnitName = self._app.get_recent_unit(categoryName)
829 if defaultInputUnitName:
830 self.select_input(defaultInputUnitName)
831 defaultOutputUnitName = self._app.get_recent_unit(categoryName, 1)
832 assert defaultOutputUnitName
833 self.select_output(defaultOutputUnitName)
835 def _select_input(self, name):
836 self._app.add_recent(self._categoryName, name)
837 self._inputUnitName = name
839 unitData = unit_data.UNIT_DESCRIPTIONS[self._categoryName]
840 conversion, unit, description = unitData[name]
842 self._inputUnitSymbol.setText(unit if unit else name)
844 if "" not in [self._categoryName, self._inputUnitName, self._outputUnitName]:
845 self._update_output()
847 def _select_output(self, name):
848 # Add the output to recent but don't make things weird by making it the most recent
849 self._app.add_recent(self._categoryName, name)
850 self._app.add_recent(self._categoryName, self._inputUnitName)
851 self._outputUnitName = name
853 unitData = unit_data.UNIT_DESCRIPTIONS[self._categoryName]
854 conversion, unit, description = unitData[name]
856 self._outputUnitSymbol.setText(unit if unit else name)
858 if "" not in [self._categoryName, self._inputUnitName, self._outputUnitName]:
859 self._update_output()
861 def _sanitize_value(self, userEntry):
862 if self._categoryName == "Computer Numbers":
871 value = float(userEntry)
874 def _update_output(self):
875 assert self._categoryName
876 assert self._inputUnitName
877 assert self._outputUnitName
879 userInput = str(self._inputUnitValue.text())
880 value = self._sanitize_value(userInput)
882 unitData = unit_data.UNIT_DESCRIPTIONS[self._categoryName]
883 inputConversion, _, _ = unitData[self._inputUnitName]
884 outputConversion, _, _ = unitData[self._outputUnitName]
886 func, arg = inputConversion
887 base = func.to_base(value, arg)
889 func, arg = outputConversion
890 newValue = func.from_base(base, arg)
891 self._outputUnitValue.setText(str(newValue))
893 def _update_input(self):
894 assert self._categoryName
895 assert self._inputUnitName
896 assert self._outputUnitName
898 userOutput = str(self._outputUnitValue.text())
899 value = self._sanitize_value(userOutput)
901 unitData = unit_data.UNIT_DESCRIPTIONS[self._categoryName]
902 inputConversion, _, _ = unitData[self._inputUnitName]
903 outputConversion, _, _ = unitData[self._outputUnitName]
905 func, arg = outputConversion
906 base = func.to_base(value, arg)
908 func, arg = inputConversion
909 newValue = func.from_base(base, arg)
910 self._inputUnitValue.setText(str(newValue))
912 def _update_favorites(self):
913 if self._app.showFavoritesAction.isChecked():
914 assert self._categoryView.topLevelItemCount() == len(unit_data.UNIT_CATEGORIES)
915 for i, catName in enumerate(unit_data.UNIT_CATEGORIES):
916 if catName in self._app.hiddenCategories:
917 self._categoryView.setRowHidden(i, self._categoryView.rootIndex(), True)
919 self._categoryView.setRowHidden(i, self._categoryView.rootIndex(), False)
921 for i, unitName in enumerate(self._unitNames):
922 if unitName in self._app.get_hidden_units(self._categoryName):
923 self._inputView.setRowHidden(i, self._inputView.rootIndex(), True)
924 self._outputView.setRowHidden(i, self._outputView.rootIndex(), True)
926 self._inputView.setRowHidden(i, self._inputView.rootIndex(), False)
927 self._outputView.setRowHidden(i, self._outputView.rootIndex(), False)
929 for i in xrange(self._categoryView.topLevelItemCount()):
930 self._categoryView.setRowHidden(i, self._categoryView.rootIndex(), False)
932 for i in xrange(len(self._unitNames)):
933 self._inputView.setRowHidden(i, self._inputView.rootIndex(), False)
934 self._outputView.setRowHidden(i, self._outputView.rootIndex(), False)
936 @misc_utils.log_exception(_moduleLogger)
937 def _on_close_window(self, checked = True):
940 @misc_utils.log_exception(_moduleLogger)
941 def _on_show_favorites(self, checked = True):
943 assert self._categoryView.topLevelItemCount() == len(unit_data.UNIT_CATEGORIES)
944 for i, catName in enumerate(unit_data.UNIT_CATEGORIES):
945 if catName in self._app.hiddenCategories:
946 self._categoryView.setRowHidden(i, self._categoryView.rootIndex(), True)
948 for i, unitName in enumerate(self._unitNames):
949 if unitName in self._app.get_hidden_units(self._categoryName):
950 self._inputView.setRowHidden(i, self._inputView.rootIndex(), True)
951 self._outputView.setRowHidden(i, self._outputView.rootIndex(), True)
953 for i in xrange(self._categoryView.topLevelItemCount()):
954 self._categoryView.setRowHidden(i, self._categoryView.rootIndex(), False)
956 for i in xrange(len(self._unitNames)):
957 self._inputView.setRowHidden(i, self._inputView.rootIndex(), False)
958 self._outputView.setRowHidden(i, self._outputView.rootIndex(), False)
960 @misc_utils.log_exception(_moduleLogger)
961 def _on_choose_category_favorites(self, obj = None):
962 assert self._favoritesWindow is None
963 self._favoritesWindow = FavoritesWindow(
966 unit_data.UNIT_CATEGORIES,
967 self._app.hiddenCategories
969 self._favoritesWindow.window.destroyed.connect(self._on_close_favorites)
970 return self._favoritesWindow
972 @misc_utils.log_exception(_moduleLogger)
973 def _on_choose_unit_favorites(self, obj = None):
974 assert self._favoritesWindow is None
975 self._favoritesWindow = FavoritesWindow(
978 unit_data.get_units(self._categoryName),
979 self._app.get_hidden_units(self._categoryName)
981 self._favoritesWindow.window.destroyed.connect(self._on_close_favorites)
982 return self._favoritesWindow
984 @misc_utils.log_exception(_moduleLogger)
985 def _on_close_favorites(self, obj = None):
986 self._favoritesWindow = None
987 self._update_favorites()
989 @misc_utils.log_exception(_moduleLogger)
990 def _on_value_edited(self, *args):
991 self._update_output()
993 @misc_utils.log_exception(_moduleLogger)
994 def _on_output_value_edited(self, *args):
997 @misc_utils.log_exception(_moduleLogger)
998 def _on_category_selection_changed(self, selected, deselected):
1001 for item in self._categoryView.selectedItems()
1003 assert len(selectedNames) == 1
1004 self._select_category(selectedNames[0])
1006 @misc_utils.log_exception(_moduleLogger)
1007 def _on_input_selection_changed(self, selected, deselected):
1010 for item in self._inputView.selectedItems()
1013 assert len(selectedNames) == 1
1014 name = selectedNames[0]
1015 self._select_input(name)
1019 @misc_utils.log_exception(_moduleLogger)
1020 def _on_output_selection_changed(self, selected, deselected):
1023 for item in self._outputView.selectedItems()
1026 assert len(selectedNames) == 1
1027 name = selectedNames[0]
1028 self._select_output(name)
1033 class FavoritesWindow(object):
1035 def __init__(self, parent, app, source, hidden):
1037 self._source = list(source)
1038 self._hidden = hidden
1040 self._categories = QtGui.QTreeWidget()
1041 self._categories.setHeaderLabels(["Categories"])
1042 self._categories.setHeaderHidden(True)
1043 self._categories.setSelectionMode(QtGui.QAbstractItemView.NoSelection)
1045 self._categories.setAlternatingRowColors(True)
1046 self._childWidgets = []
1047 for catName in self._source:
1048 twi = QtGui.QTreeWidgetItem(self._categories)
1049 twi.setText(0, catName)
1050 self._childWidgets.append(twi)
1051 if catName in self._hidden:
1052 twi.setCheckState(0, QtCore.Qt.Unchecked)
1054 twi.setCheckState(0, QtCore.Qt.Checked)
1055 self._categories.itemChanged.connect(self._on_item_changed)
1057 self._allButton = QtGui.QPushButton("All")
1058 self._allButton.clicked.connect(self._on_select_all)
1059 self._invertButton = QtGui.QPushButton("Invert")
1060 self._invertButton.clicked.connect(self._on_invert_selection)
1061 self._noneButton = QtGui.QPushButton("None")
1062 self._noneButton.clicked.connect(self._on_select_none)
1064 self._buttonLayout = QtGui.QHBoxLayout()
1065 self._buttonLayout.addWidget(self._allButton)
1066 self._buttonLayout.addWidget(self._invertButton)
1067 self._buttonLayout.addWidget(self._noneButton)
1069 self._layout = QtGui.QVBoxLayout()
1070 self._layout.addWidget(self._categories)
1071 self._layout.addLayout(self._buttonLayout)
1073 centralWidget = QtGui.QWidget()
1074 centralWidget.setLayout(self._layout)
1076 self._window = QtGui.QMainWindow(parent)
1077 self._window.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
1078 maeqt.set_autorient(self._window, True)
1079 maeqt.set_stackable(self._window, True)
1080 self._window.setWindowTitle("%s - Favorites" % constants.__pretty_app_name__)
1081 self._window.setWindowIcon(QtGui.QIcon(self._app.appIconPath))
1082 self._window.setCentralWidget(centralWidget)
1084 self._closeWindowAction = QtGui.QAction(None)
1085 self._closeWindowAction.setText("Close")
1086 self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
1087 self._closeWindowAction.triggered.connect(self._on_close_window)
1090 self._window.addAction(self._closeWindowAction)
1091 self._window.addAction(self._app.quitAction)
1092 self._window.addAction(self._app.fullscreenAction)
1094 fileMenu = self._window.menuBar().addMenu("&Units")
1095 fileMenu.addAction(self._closeWindowAction)
1096 fileMenu.addAction(self._app.quitAction)
1098 viewMenu = self._window.menuBar().addMenu("&View")
1099 viewMenu.addAction(self._app.fullscreenAction)
1101 self._window.addAction(self._app.logAction)
1103 self.set_fullscreen(self._app.fullscreenAction.isChecked())
1117 self._window.close()
1119 def set_fullscreen(self, isFullscreen):
1121 self._window.showFullScreen()
1123 self._window.showNormal()
1125 @misc_utils.log_exception(_moduleLogger)
1126 def _on_select_all(self, checked = False):
1127 for child in self._childWidgets:
1128 child.setCheckState(0, QtCore.Qt.Checked)
1130 @misc_utils.log_exception(_moduleLogger)
1131 def _on_invert_selection(self, checked = False):
1132 for child in self._childWidgets:
1133 state = child.checkState(0)
1134 if state == QtCore.Qt.Unchecked:
1135 newState = QtCore.Qt.Checked
1136 elif state == QtCore.Qt.Checked:
1137 newState = QtCore.Qt.Unchecked
1139 raise RuntimeError("Bad check state %r" % state)
1140 child.setCheckState(0, newState)
1142 @misc_utils.log_exception(_moduleLogger)
1143 def _on_select_none(self, checked = False):
1144 for child in self._childWidgets:
1145 child.setCheckState(0, QtCore.Qt.Unchecked)
1147 @misc_utils.log_exception(_moduleLogger)
1148 def _on_item_changed(self, item, column):
1149 state = item.checkState(column)
1150 if state == QtCore.Qt.Unchecked:
1151 name = str(item.text(column))
1152 self._hidden.add(name)
1153 elif state == QtCore.Qt.Checked:
1154 name = str(item.text(column))
1155 self._hidden.remove(name)
1157 raise RuntimeError("Bad check state %r" % state)
1159 @misc_utils.log_exception(_moduleLogger)
1160 def _on_close_window(self, checked = True):
1164 class CategoryWindow(object):
1166 def __init__(self, parent, app):
1168 self._unitWindow = None
1169 self._favoritesWindow = None
1171 self._categories = QtGui.QTreeWidget()
1172 self._categories.setHeaderLabels(["Categories"])
1173 self._categories.itemClicked.connect(self._on_category_clicked)
1174 self._categories.setHeaderHidden(True)
1175 self._categories.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
1176 self._categories.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
1178 self._categories.setAlternatingRowColors(True)
1179 for catName in unit_data.UNIT_CATEGORIES:
1180 twi = QtGui.QTreeWidgetItem(self._categories)
1181 twi.setText(0, catName)
1183 self._layout = QtGui.QVBoxLayout()
1184 self._layout.addWidget(self._categories)
1186 centralWidget = QtGui.QWidget()
1187 centralWidget.setLayout(self._layout)
1189 self._window = QtGui.QMainWindow(parent)
1190 self._window.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
1191 maeqt.set_autorient(self._window, True)
1192 maeqt.set_stackable(self._window, True)
1193 self._window.setWindowTitle("%s - Categories" % constants.__pretty_app_name__)
1194 self._window.setWindowIcon(QtGui.QIcon(self._app.appIconPath))
1195 self._window.setCentralWidget(centralWidget)
1197 self._chooseFavoritesAction = QtGui.QAction(None)
1198 self._chooseFavoritesAction.setText("Select Favorites")
1199 self._chooseFavoritesAction.setShortcut(QtGui.QKeySequence("CTRL+f"))
1200 self._chooseFavoritesAction.triggered.connect(self._on_choose_favorites)
1202 self._app.showFavoritesAction.toggled.connect(self._on_show_favorites)
1204 self._closeWindowAction = QtGui.QAction(None)
1205 self._closeWindowAction.setText("Close")
1206 self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
1207 self._closeWindowAction.triggered.connect(self._on_close_window)
1210 fileMenu = self._window.menuBar().addMenu("&Units")
1211 fileMenu.addAction(self._chooseFavoritesAction)
1213 viewMenu = self._window.menuBar().addMenu("&View")
1214 viewMenu.addAction(self._app.showFavoritesAction)
1215 viewMenu.addAction(self._app.condensedAction)
1216 viewMenu.addSeparator()
1217 viewMenu.addAction(self._app.jumpAction)
1218 viewMenu.addAction(self._app.recentAction)
1220 self._window.addAction(self._closeWindowAction)
1221 self._window.addAction(self._app.quitAction)
1222 self._window.addAction(self._app.fullscreenAction)
1224 fileMenu = self._window.menuBar().addMenu("&Units")
1225 fileMenu.addAction(self._chooseFavoritesAction)
1226 fileMenu.addAction(self._closeWindowAction)
1227 fileMenu.addAction(self._app.quitAction)
1229 viewMenu = self._window.menuBar().addMenu("&View")
1230 viewMenu.addAction(self._app.showFavoritesAction)
1231 viewMenu.addAction(self._app.condensedAction)
1232 viewMenu.addSeparator()
1233 viewMenu.addAction(self._app.jumpAction)
1234 viewMenu.addAction(self._app.recentAction)
1235 viewMenu.addSeparator()
1236 viewMenu.addAction(self._app.fullscreenAction)
1238 self._window.addAction(self._app.logAction)
1240 self._update_favorites()
1241 self.set_fullscreen(self._app.fullscreenAction.isChecked())
1248 def walk_children(self):
1249 if self._unitWindow is not None:
1250 yield self._unitWindow
1251 if self._favoritesWindow is not None:
1252 yield self._favoritesWindow
1256 for child in self.walk_children():
1260 for child in self.walk_children():
1265 for child in self.walk_children():
1266 child.window.destroyed.disconnect(self._on_child_close)
1268 self._window.close()
1270 def select_category(self, categoryName):
1271 self._select_category(categoryName)
1273 i = unit_data.UNIT_CATEGORIES.index(categoryName)
1274 rootIndex = self._categories.rootIndex()
1275 currentIndex = self._categories.model().index(i, 0, rootIndex)
1276 self._categories.scrollTo(currentIndex)
1277 self._categories.setItemSelected(self._categories.topLevelItem(i), True)
1278 return self._unitWindow
1280 def set_fullscreen(self, isFullscreen):
1282 self._window.showFullScreen()
1284 self._window.showNormal()
1285 for child in self.walk_children():
1286 child.set_fullscreen(isFullscreen)
1288 def _select_category(self, categoryName):
1289 for child in self.walk_children():
1290 child.window.destroyed.disconnect(self._on_child_close)
1292 self._unitWindow = UnitWindow(self._window, categoryName, self._app)
1293 self._unitWindow.window.destroyed.connect(self._on_child_close)
1295 def _update_favorites(self):
1296 if self._app.showFavoritesAction.isChecked():
1297 assert self._categories.topLevelItemCount() == len(unit_data.UNIT_CATEGORIES)
1298 for i, catName in enumerate(unit_data.UNIT_CATEGORIES):
1299 if catName in self._app.hiddenCategories:
1300 self._categories.setRowHidden(i, self._categories.rootIndex(), True)
1302 self._categories.setRowHidden(i, self._categories.rootIndex(), False)
1304 for i in xrange(self._categories.topLevelItemCount()):
1305 self._categories.setRowHidden(i, self._categories.rootIndex(), False)
1307 @misc_utils.log_exception(_moduleLogger)
1308 def _on_show_favorites(self, checked = True):
1310 assert self._categories.topLevelItemCount() == len(unit_data.UNIT_CATEGORIES)
1311 for i, catName in enumerate(unit_data.UNIT_CATEGORIES):
1312 if catName in self._app.hiddenCategories:
1313 self._categories.setRowHidden(i, self._categories.rootIndex(), True)
1315 for i in xrange(self._categories.topLevelItemCount()):
1316 self._categories.setRowHidden(i, self._categories.rootIndex(), False)
1318 @misc_utils.log_exception(_moduleLogger)
1319 def _on_choose_favorites(self, obj = None):
1320 assert self._favoritesWindow is None
1321 self._favoritesWindow = FavoritesWindow(
1324 unit_data.UNIT_CATEGORIES,
1325 self._app.hiddenCategories
1327 self._favoritesWindow.window.destroyed.connect(self._on_close_favorites)
1328 return self._favoritesWindow
1330 @misc_utils.log_exception(_moduleLogger)
1331 def _on_close_favorites(self, obj = None):
1332 self._favoritesWindow = None
1333 self._update_favorites()
1335 @misc_utils.log_exception(_moduleLogger)
1336 def _on_child_close(self, obj = None):
1337 self._unitWindow = None
1339 @misc_utils.log_exception(_moduleLogger)
1340 def _on_close_window(self, checked = True):
1343 @misc_utils.log_exception(_moduleLogger)
1344 def _on_category_clicked(self, item, columnIndex):
1345 categoryName = unicode(item.text(0))
1346 self.select_category(categoryName)
1349 class UnitData(object):
1351 HEADERS = ["Name", "Value", "", "Unit"]
1352 ALIGNMENT = [QtCore.Qt.AlignLeft, QtCore.Qt.AlignRight, QtCore.Qt.AlignLeft, QtCore.Qt.AlignLeft]
1359 "_name", "_unit", "_description", "_conversion",
1360 "_value", "_integerDisplay", "_fractionalDisplay",
1363 def __init__(self, name, unit, description, conversion):
1366 self._description = description
1367 self._conversion = conversion
1370 self._integerDisplay, self._fractionalDisplay = split_number(self._value)
1380 def update_value(self, newValue):
1381 self._value = newValue
1382 self._integerDisplay, self._fractionalDisplay = split_number(newValue)
1389 def conversion(self):
1390 return self._conversion
1392 def data(self, column):
1394 return [self._name, self._integerDisplay, self._fractionalDisplay, self._unit][column]
1399 class UnitModel(QtCore.QAbstractItemModel):
1401 def __init__(self, categoryName, parent=None):
1402 super(UnitModel, self).__init__(parent)
1403 self._categoryName = categoryName
1404 self._unitData = unit_data.UNIT_DESCRIPTIONS[self._categoryName]
1405 if self._categoryName == "Computer Numbers":
1406 self._sanitize_value = self._sanitize_alpha_value
1408 self._sanitize_value = self._sanitize_numeric_value
1411 for key in unit_data.get_units(self._categoryName):
1412 conversion, unit, description = self._unitData[key]
1413 self._children.append(UnitData(key, unit, description, conversion))
1414 self._sortSettings = None
1416 @misc_utils.log_exception(_moduleLogger)
1417 def columnCount(self, parent):
1418 if parent.isValid():
1421 return len(UnitData.HEADERS)
1423 @misc_utils.log_exception(_moduleLogger)
1424 def data(self, index, role):
1425 #if not index.isValid():
1428 if role == QtCore.Qt.DisplayRole:
1429 item = index.internalPointer()
1430 if isinstance(item, UnitData):
1431 return item.data(index.column())
1432 elif item is UnitData.HEADERS:
1433 return item[index.column()]
1434 elif role == QtCore.Qt.TextAlignmentRole:
1435 return UnitData.ALIGNMENT[index.column()]
1439 @misc_utils.log_exception(_moduleLogger)
1440 def sort(self, column, order = QtCore.Qt.AscendingOrder):
1441 self._sortSettings = column, order
1442 isReverse = order == QtCore.Qt.AscendingOrder
1443 if column == UnitData.NAME_COLUMN:
1444 key_func = lambda item: item.name
1445 elif column in [UnitData.VALUE_COLUMN_0, UnitData.VALUE_COLUMN_1]:
1446 key_func = lambda item: item.value
1447 elif column == UnitData.UNIT_COLUMN:
1448 key_func = lambda item: item.unit
1449 self._children.sort(key=key_func, reverse = isReverse)
1453 @misc_utils.log_exception(_moduleLogger)
1454 def flags(self, index):
1455 if not index.isValid():
1456 return QtCore.Qt.NoItemFlags
1458 return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
1460 @misc_utils.log_exception(_moduleLogger)
1461 def headerData(self, section, orientation, role):
1462 if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
1463 return UnitData.HEADERS[section]
1467 @misc_utils.log_exception(_moduleLogger)
1468 def index(self, row, column, parent):
1469 #if not self.hasIndex(row, column, parent):
1470 # return QtCore.QModelIndex()
1471 #elif parent.isValid():
1472 # return QtCore.QModelIndex()
1474 parentItem = UnitData.HEADERS
1475 childItem = self._children[row]
1477 return self.createIndex(row, column, childItem)
1479 return QtCore.QModelIndex()
1481 @misc_utils.log_exception(_moduleLogger)
1482 def parent(self, index):
1483 if not index.isValid():
1484 return QtCore.QModelIndex()
1486 childItem = index.internalPointer()
1487 if isinstance(childItem, UnitData):
1488 return QtCore.QModelIndex()
1489 elif childItem is UnitData.HEADERS:
1492 @misc_utils.log_exception(_moduleLogger)
1493 def rowCount(self, parent):
1494 if 0 < parent.column():
1497 if not parent.isValid():
1498 return len(self._children)
1500 return len(self._children)
1502 def get_unit(self, index):
1504 return self._children[index]
1506 def get_unit_names(self):
1507 for child in self._children:
1510 def index_unit(self, unitName):
1511 for i, child in enumerate(self._children):
1512 if child.name == unitName:
1515 raise RuntimeError("Unit not found")
1517 def update_values(self, fromIndex, userInput):
1518 value = self._sanitize_value(userInput)
1519 func, arg = self._children[fromIndex].conversion
1520 base = func.to_base(value, arg)
1521 for i, child in enumerate(self._children):
1522 func, arg = child.conversion
1523 newValue = func.from_base(base, arg)
1524 child.update_value(newValue)
1527 self._sortSettings is not None and
1528 self._sortSettings[0] in [UnitData.VALUE_COLUMN_0, UnitData.VALUE_COLUMN_1]
1530 # Sort takes care of marking everything as changed
1531 self.sort(*self._sortSettings)
1534 self._values_changed()
1538 return len(self._children)
1540 def _values_changed(self):
1541 topLeft = self.createIndex(0, UnitData.VALUE_COLUMN_0, self._children[0])
1542 bottomRight = self.createIndex(len(self._children)-1, UnitData.VALUE_COLUMN_1, self._children[-1])
1543 self.dataChanged.emit(topLeft, bottomRight)
1545 def _all_changed(self):
1546 topLeft = self.createIndex(0, 0, self._children[0])
1547 bottomRight = self.createIndex(len(self._children)-1, len(UnitData.HEADERS)-1, self._children[-1])
1548 self.dataChanged.emit(topLeft, bottomRight)
1550 def _sanitize_alpha_value(self, userEntry):
1557 def _sanitize_numeric_value(self, userEntry):
1559 value = float(userEntry)
1565 class UnitWindow(object):
1567 def __init__(self, parent, category, app):
1569 self._categoryName = category
1570 self._selectedIndex = 0
1571 self._favoritesWindow = None
1573 self._selectedUnitName = QtGui.QLabel()
1574 self._selectedUnitValue = QtGui.QLineEdit()
1575 self._selectedUnitValue.textEdited.connect(self._on_value_edited)
1576 maeqt.mark_numbers_preferred(self._selectedUnitValue)
1577 self._selectedUnitSymbol = QtGui.QLabel()
1578 self._updateDelayTimer = QtCore.QTimer()
1579 self._updateDelayTimer.setInterval(100)
1580 self._updateDelayTimer.setSingleShot(True)
1581 self._updateDelayTimer.timeout.connect(self._on_value_edited_delayed)
1583 self._selectedUnitLayout = QtGui.QHBoxLayout()
1584 self._selectedUnitLayout.addWidget(self._selectedUnitName)
1585 self._selectedUnitLayout.addWidget(self._selectedUnitValue)
1586 self._selectedUnitLayout.addWidget(self._selectedUnitSymbol)
1588 self._unitsModel = UnitModel(self._categoryName)
1589 self._unitsView = QtGui.QTreeView()
1590 self._unitsView.setModel(self._unitsModel)
1591 self._unitsView.setUniformRowHeights(True)
1592 self._unitsView.setSortingEnabled(True)
1593 self._unitsView.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
1594 self._unitsView.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
1595 self._unitsView.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
1596 self._unitsView.setHeaderHidden(True)
1597 self._unitsView.clicked.connect(self._on_unit_clicked)
1599 self._unitsView.setAlternatingRowColors(True)
1601 viewHeader = self._unitsView.header()
1602 viewHeader.setSortIndicatorShown(True)
1603 viewHeader.setClickable(True)
1605 viewHeader.setResizeMode(UnitData.NAME_COLUMN, QtGui.QHeaderView.ResizeToContents)
1606 viewHeader.setResizeMode(UnitData.VALUE_COLUMN_0, QtGui.QHeaderView.ResizeToContents)
1607 viewHeader.setResizeMode(UnitData.VALUE_COLUMN_1, QtGui.QHeaderView.ResizeToContents)
1608 viewHeader.setResizeMode(UnitData.UNIT_COLUMN, QtGui.QHeaderView.ResizeToContents)
1609 viewHeader.setStretchLastSection(False)
1611 # Trying to make things faster by locking in the initial size of the immutable columns
1612 nameSize = min(viewHeader.sectionSize(UnitData.NAME_COLUMN), 150)
1613 viewHeader.setResizeMode(UnitData.NAME_COLUMN, QtGui.QHeaderView.Fixed)
1614 viewHeader.resizeSection(UnitData.NAME_COLUMN, nameSize)
1615 unitSize = min(viewHeader.sectionSize(UnitData.UNIT_COLUMN), 150)
1616 viewHeader.setResizeMode(UnitData.UNIT_COLUMN, QtGui.QHeaderView.Fixed)
1617 viewHeader.resizeSection(UnitData.UNIT_COLUMN, unitSize)
1619 self._layout = QtGui.QVBoxLayout()
1620 self._layout.addLayout(self._selectedUnitLayout)
1621 self._layout.addWidget(self._unitsView)
1623 centralWidget = QtGui.QWidget()
1624 centralWidget.setLayout(self._layout)
1626 self._window = QtGui.QMainWindow(parent)
1627 self._window.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
1628 maeqt.set_autorient(self._window, True)
1629 maeqt.set_stackable(self._window, True)
1630 self._window.setWindowTitle("%s - %s" % (constants.__pretty_app_name__, category))
1631 self._window.setWindowIcon(QtGui.QIcon(app.appIconPath))
1632 self._window.setCentralWidget(centralWidget)
1634 defaultUnitName = self._app.get_recent_unit(self._categoryName)
1636 self.select_unit(defaultUnitName)
1638 self._select_unit(0)
1640 if self._app.sortByNameAction.isChecked():
1641 sortColumn = UnitData.NAME_COLUMN
1642 elif self._app.sortByValueAction.isChecked():
1643 sortColumn = UnitData.VALUE_COLUMN_0
1644 elif self._app.sortByUnitAction.isChecked():
1645 sortColumn = UnitData.UNIT_COLUMN
1647 raise RuntimeError("No sort column selected")
1649 # By default it sorts by he first column (name)
1650 self._unitsModel.sort(sortColumn)
1652 self._chooseFavoritesAction = QtGui.QAction(None)
1653 self._chooseFavoritesAction.setText("Select Favorites")
1654 self._chooseFavoritesAction.setShortcut(QtGui.QKeySequence("CTRL+f"))
1655 self._chooseFavoritesAction.triggered.connect(self._on_choose_favorites)
1657 self._app.showFavoritesAction.toggled.connect(self._on_show_favorites)
1659 self._previousUnitAction = QtGui.QAction(None)
1660 self._previousUnitAction.setText("Previous Unit")
1661 self._previousUnitAction.setShortcut(QtGui.QKeySequence("Up"))
1662 self._previousUnitAction.triggered.connect(self._on_previous_unit)
1664 self._nextUnitAction = QtGui.QAction(None)
1665 self._nextUnitAction.setText("Next Unit")
1666 self._nextUnitAction.setShortcut(QtGui.QKeySequence("Down"))
1667 self._nextUnitAction.triggered.connect(self._on_next_unit)
1669 self._closeWindowAction = QtGui.QAction(None)
1670 self._closeWindowAction.setText("Close Window")
1671 self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
1672 self._closeWindowAction.triggered.connect(self._on_close_window)
1675 self._window.addAction(self._closeWindowAction)
1676 self._window.addAction(self._app.quitAction)
1677 self._window.addAction(self._app.fullscreenAction)
1679 fileMenu = self._window.menuBar().addMenu("&Units")
1680 fileMenu.addAction(self._chooseFavoritesAction)
1682 viewMenu = self._window.menuBar().addMenu("&View")
1683 viewMenu.addAction(self._app.showFavoritesAction)
1684 viewMenu.addAction(self._app.condensedAction)
1685 viewMenu.addSeparator()
1686 viewMenu.addAction(self._app.sortByNameAction)
1687 viewMenu.addAction(self._app.sortByValueAction)
1688 viewMenu.addAction(self._app.sortByUnitAction)
1689 viewMenu.addSeparator()
1690 viewMenu.addAction(self._app.jumpAction)
1691 viewMenu.addAction(self._app.recentAction)
1693 fileMenu = self._window.menuBar().addMenu("&Units")
1694 fileMenu.addAction(self._chooseFavoritesAction)
1695 fileMenu.addAction(self._closeWindowAction)
1696 fileMenu.addAction(self._app.quitAction)
1698 viewMenu = self._window.menuBar().addMenu("&View")
1699 viewMenu.addAction(self._app.showFavoritesAction)
1700 viewMenu.addAction(self._app.condensedAction)
1701 viewMenu.addSeparator()
1702 viewMenu.addAction(self._app.sortByNameAction)
1703 viewMenu.addAction(self._app.sortByValueAction)
1704 viewMenu.addAction(self._app.sortByUnitAction)
1705 viewMenu.addSeparator()
1706 viewMenu.addAction(self._app.jumpAction)
1707 viewMenu.addAction(self._app.recentAction)
1708 viewMenu.addSeparator()
1709 viewMenu.addAction(self._app.fullscreenAction)
1711 self._app.sortByNameAction.triggered.connect(self._on_sort_by_name)
1712 self._app.sortByValueAction.triggered.connect(self._on_sort_by_value)
1713 self._app.sortByUnitAction.triggered.connect(self._on_sort_by_unit)
1715 self._window.addAction(self._app.logAction)
1716 self._window.addAction(self._nextUnitAction)
1717 self._window.addAction(self._previousUnitAction)
1718 self._window.addAction(self._chooseFavoritesAction)
1720 self._update_favorites()
1721 self.set_fullscreen(self._app.fullscreenAction.isChecked())
1729 for child in self.walk_children():
1734 for child in self.walk_children():
1739 for child in self.walk_children():
1740 child.window.destroyed.disconnect(self._on_child_close)
1742 self._window.close()
1744 def set_fullscreen(self, isFullscreen):
1746 self._window.showFullScreen()
1748 self._window.showNormal()
1750 def select_unit(self, unitName):
1751 index = self._unitsModel.index_unit(unitName)
1752 self._select_unit(index)
1754 qindex = self._unitsModel.createIndex(index, 0, self._unitsModel.get_unit(index))
1755 self._unitsView.scrollTo(qindex)
1757 def walk_children(self):
1758 if self._favoritesWindow is not None:
1759 yield self._favoritesWindow
1761 def _update_favorites(self, force = False):
1762 if self._app.showFavoritesAction.isChecked():
1763 unitNames = list(self._unitsModel.get_unit_names())
1764 for i, unitName in enumerate(unitNames):
1765 if unitName in self._app.get_hidden_units(self._categoryName):
1766 self._unitsView.setRowHidden(i, self._unitsView.rootIndex(), True)
1768 self._unitsView.setRowHidden(i, self._unitsView.rootIndex(), False)
1771 for i in xrange(len(self._unitsModel)):
1772 self._unitsView.setRowHidden(i, self._unitsView.rootIndex(), False)
1774 @misc_utils.log_exception(_moduleLogger)
1775 def _on_show_favorites(self, checked = True):
1777 unitNames = list(self._unitsModel.get_unit_names())
1778 for i, unitName in enumerate(unitNames):
1779 if unitName in self._app.get_hidden_units(self._categoryName):
1780 self._unitsView.setRowHidden(i, self._unitsView.rootIndex(), True)
1782 for i in xrange(len(self._unitsModel)):
1783 self._unitsView.setRowHidden(i, self._unitsView.rootIndex(), False)
1785 @misc_utils.log_exception(_moduleLogger)
1786 def _on_choose_favorites(self, obj = None):
1787 assert self._favoritesWindow is None
1788 self._favoritesWindow = FavoritesWindow(
1791 unit_data.get_units(self._categoryName),
1792 self._app.get_hidden_units(self._categoryName)
1794 self._favoritesWindow.window.destroyed.connect(self._on_close_favorites)
1795 return self._favoritesWindow
1797 @misc_utils.log_exception(_moduleLogger)
1798 def _on_close_favorites(self, obj = None):
1799 self._favoritesWindow = None
1800 self._update_favorites(force=True)
1802 @misc_utils.log_exception(_moduleLogger)
1803 def _on_previous_unit(self, checked = True):
1804 self._select_unit(self._selectedIndex - 1)
1806 @misc_utils.log_exception(_moduleLogger)
1807 def _on_next_unit(self, checked = True):
1808 self._select_unit(self._selectedIndex + 1)
1810 @misc_utils.log_exception(_moduleLogger)
1811 def _on_close_window(self, checked = True):
1814 @misc_utils.log_exception(_moduleLogger)
1815 def _on_sort_by_name(self, checked = False):
1816 self._unitsModel.sort(UnitData.NAME_COLUMN, QtCore.Qt.DescendingOrder)
1818 @misc_utils.log_exception(_moduleLogger)
1819 def _on_sort_by_value(self, checked = False):
1820 self._unitsModel.sort(UnitData.VALUE_COLUMN_0)
1822 @misc_utils.log_exception(_moduleLogger)
1823 def _on_sort_by_unit(self, checked = False):
1824 self._unitsModel.sort(UnitData.UNIT_COLUMN, QtCore.Qt.DescendingOrder)
1826 @misc_utils.log_exception(_moduleLogger)
1827 def _on_unit_clicked(self, index):
1828 self._select_unit(index.row())
1830 @misc_utils.log_exception(_moduleLogger)
1831 def _on_value_edited(self, *args):
1832 if not self._updateDelayTimer.isActive():
1833 self._updateDelayTimer.start()
1835 @misc_utils.log_exception(_moduleLogger)
1836 def _on_value_edited_delayed(self, *args):
1837 userInput = str(self._selectedUnitValue.text())
1838 orderChanged = self._unitsModel.update_values(self._selectedIndex, userInput)
1840 self._update_favorites()
1842 def _select_unit(self, index):
1843 unit = self._unitsModel.get_unit(index)
1844 self._selectedUnitName.setText(unit.name)
1845 self._selectedUnitValue.setText(str(unit.value))
1846 self._selectedUnitSymbol.setText(unit.unit)
1848 self._selectedIndex = index
1849 self._app.add_recent(self._categoryName, self._unitsModel.get_unit(index).name)
1853 app = QtGui.QApplication([])
1854 handle = Gonvert(app)
1855 if constants.PROFILE_STARTUP:
1861 if __name__ == "__main__":
1862 logging.basicConfig(level = logging.DEBUG)
1864 os.makedirs(constants._data_path_)