4 from __future__ import with_statement
12 from PyQt4 import QtGui
13 from PyQt4 import QtCore
17 from util import misc as misc_utils
21 _moduleLogger = logging.getLogger("gonvert_glade")
27 def change_menu_label(widgets, labelname, newtext):
28 item_label = widgets.get_widget(labelname).get_children()[0]
29 item_label.set_text(newtext)
32 def split_number(number):
34 fractional, integer = math.modf(number)
36 integerDisplay = number
37 fractionalDisplay = ""
39 integerDisplay = str(integer)
40 fractionalDisplay = str(fractional)
41 if "e+" in integerDisplay:
42 integerDisplay = number
43 fractionalDisplay = ""
44 elif "e-" in fractionalDisplay and 0.0 < integer:
45 integerDisplay = number
46 fractionalDisplay = ""
47 elif "e-" in fractionalDisplay:
49 fractionalDisplay = number
51 integerDisplay = integerDisplay.split(".", 1)[0] + "."
52 fractionalDisplay = fractionalDisplay.rsplit(".", 1)[-1]
54 return integerDisplay, fractionalDisplay
57 class Gonvert(object):
60 os.path.dirname(__file__),
61 os.path.join(os.path.dirname(__file__), "../data"),
62 os.path.join(os.path.dirname(__file__), "../lib"),
67 def __init__(self, app):
69 for dataPath in self._DATA_PATHS:
70 appIconPath = os.path.join(dataPath, "pixmaps", "gonvert.png")
71 if os.path.isfile(appIconPath):
72 self._dataPath = dataPath
75 raise RuntimeError("UI Descriptor not found!")
77 self._appIconPath = appIconPath
79 self._hiddenCategories = set()
80 self._hiddenUnits = {}
81 self._clipboard = QtGui.QApplication.clipboard()
83 self._jumpWindow = None
84 self._recentWindow = None
85 self._catWindow = None
87 self._jumpAction = QtGui.QAction(None)
88 self._jumpAction.setText("Quick Jump")
89 self._jumpAction.setStatusTip("Search for a unit and jump straight to it")
90 self._jumpAction.setToolTip("Search for a unit and jump straight to it")
91 self._jumpAction.setShortcut(QtGui.QKeySequence("CTRL+j"))
92 self._jumpAction.triggered.connect(self._on_jump_start)
94 self._recentAction = QtGui.QAction(None)
95 self._recentAction.setText("Recent Units")
96 self._recentAction.setStatusTip("View the recent units")
97 self._recentAction.setToolTip("View the recent units")
98 self._recentAction.setShortcut(QtGui.QKeySequence("CTRL+r"))
99 self._recentAction.triggered.connect(self._on_recent_start)
101 self._fullscreenAction = QtGui.QAction(None)
102 self._fullscreenAction.setText("Fullscreen")
103 self._fullscreenAction.setCheckable(True)
104 self._fullscreenAction.setShortcut(QtGui.QKeySequence("CTRL+Enter"))
105 self._fullscreenAction.toggled.connect(self._on_toggle_fullscreen)
107 self._showFavoritesAction = QtGui.QAction(None)
108 self._showFavoritesAction.setCheckable(True)
109 self._showFavoritesAction.setText("Favorites Only")
111 self._logAction = QtGui.QAction(None)
112 self._logAction.setText("Log")
113 self._logAction.setShortcut(QtGui.QKeySequence("CTRL+l"))
114 self._logAction.triggered.connect(self._on_log)
116 self._quitAction = QtGui.QAction(None)
117 self._quitAction.setText("Quit")
118 self._quitAction.setShortcut(QtGui.QKeySequence("CTRL+q"))
119 self._quitAction.triggered.connect(self._on_quit)
121 self._app.lastWindowClosed.connect(self._on_app_quit)
122 self.request_category()
125 def request_category(self):
126 if self._catWindow is not None:
127 self._catWindow.close()
128 self._catWindow = None
129 self._catWindow = CategoryWindow(None, self)
130 self._catWindow.window.destroyed.connect(lambda obj = None: self._on_child_close("_catWindow", obj))
131 return self._catWindow
133 def search_units(self):
134 if self._jumpWindow is not None:
135 self._jumpWindow.close()
136 self._jumpWindow = None
137 self._jumpWindow = QuickJump(None, self)
138 self._jumpWindow.window.destroyed.connect(lambda obj = None: self._on_child_close("_jumpWindow", obj))
139 return self._jumpWindow
141 def show_recent(self):
142 if self._recentWindow is not None:
143 self._recentWindow.close()
144 self._recentWindow = None
145 self._recentWindow = Recent(None, self)
146 self._recentWindow.window.destroyed.connect(lambda obj = None: self._on_child_close("_recentWindow", obj))
147 return self._recentWindow
149 def add_recent(self, categoryName, unitName):
150 catUnit = categoryName, unitName
152 self._recent.remove(catUnit)
154 pass # ignore if its not already in the recent history
155 assert catUnit not in self._recent
156 self._recent.append(catUnit)
158 def get_recent_unit(self, categoryName):
159 for catName, unitName in reversed(self._recent):
160 if catName == categoryName:
165 def get_recent(self):
166 return reversed(self._recent)
169 def hiddenCategories(self):
170 return self._hiddenCategories
172 def get_hidden_units(self, categoryName):
174 return self._hiddenUnits[categoryName]
176 self._hiddenUnits[categoryName] = set()
177 return self._hiddenUnits[categoryName]
179 def load_settings(self):
181 with open(constants._user_settings_, "r") as settingsFile:
182 settings = simplejson.load(settingsFile)
184 _moduleLogger.info("No settings")
187 _moduleLogger.info("Settings were corrupt")
190 self._fullscreenAction.setChecked(settings.get("isFullScreen", False))
192 recent = settings.get("recent", self._recent)
193 for category, unit in recent:
194 self.add_recent(category, unit)
196 self._hiddenCategories = set(settings.get("hiddenCategories", set()))
197 self._hiddenUnits = dict(
198 (catName, set(units))
199 for (catName, units) in settings.get("hiddenUnits", {}).iteritems()
202 self._showFavoritesAction.setChecked(settings.get("showFavorites", True))
205 self._catWindow.select_category(self._recent[-1][0])
207 def save_settings(self):
209 "isFullScreen": self._fullscreenAction.isChecked(),
210 "recent": self._recent,
211 "hiddenCategories": list(self._hiddenCategories),
213 (catName, list(units))
214 for (catName, units) in self._hiddenUnits.iteritems()
216 "showFavorites": self._showFavoritesAction.isChecked(),
218 with open(constants._user_settings_, "w") as settingsFile:
219 simplejson.dump(settings, settingsFile)
222 def appIconPath(self):
223 return self._appIconPath
226 def jumpAction(self):
227 return self._jumpAction
230 def recentAction(self):
231 return self._recentAction
234 def fullscreenAction(self):
235 return self._fullscreenAction
239 return self._logAction
242 def quitAction(self):
243 return self._quitAction
246 def showFavoritesAction(self):
247 return self._showFavoritesAction
249 def _walk_children(self):
250 if self._catWindow is not None:
251 yield self._catWindow
252 if self._jumpWindow is not None:
253 yield self._jumpWindow
254 if self._recentWindow is not None:
255 yield self._recentWindow
257 @misc_utils.log_exception(_moduleLogger)
258 def _on_app_quit(self, checked = False):
261 @misc_utils.log_exception(_moduleLogger)
262 def _on_child_close(self, name, obj = None):
263 if not hasattr(self, name):
264 _moduleLogger.info("Something weird going on when we don't have a %s" % name)
266 setattr(self, name, None)
268 @misc_utils.log_exception(_moduleLogger)
269 def _on_toggle_fullscreen(self, checked = False):
270 for window in self._walk_children():
271 window.set_fullscreen(checked)
273 @misc_utils.log_exception(_moduleLogger)
274 def _on_jump_start(self, checked = False):
277 @misc_utils.log_exception(_moduleLogger)
278 def _on_recent_start(self, checked = False):
281 @misc_utils.log_exception(_moduleLogger)
282 def _on_log(self, checked = False):
283 with open(constants._user_logpath_, "r") as f:
284 logLines = f.xreadlines()
285 log = "".join(logLines)
286 self._clipboard.setText(log)
288 @misc_utils.log_exception(_moduleLogger)
289 def _on_quit(self, checked = False):
290 for window in self._walk_children():
294 class QuickJump(object):
298 def __init__(self, parent, app):
301 self._searchLabel = QtGui.QLabel("Search:")
302 self._searchEntry = QtGui.QLineEdit("")
303 self._searchEntry.textEdited.connect(self._on_search_edited)
305 self._entryLayout = QtGui.QHBoxLayout()
306 self._entryLayout.addWidget(self._searchLabel)
307 self._entryLayout.addWidget(self._searchEntry)
309 self._resultsBox = QtGui.QTreeWidget()
310 self._resultsBox.setHeaderLabels(["Categories", "Units"])
311 self._resultsBox.setHeaderHidden(True)
313 self._resultsBox.setAlternatingRowColors(True)
314 self._resultsBox.itemClicked.connect(self._on_result_clicked)
316 self._layout = QtGui.QVBoxLayout()
317 self._layout.addLayout(self._entryLayout)
318 self._layout.addWidget(self._resultsBox)
320 centralWidget = QtGui.QWidget()
321 centralWidget.setLayout(self._layout)
323 self._window = QtGui.QMainWindow(parent)
324 self._window.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
325 maeqt.set_autorient(self._window, True)
326 maeqt.set_stackable(self._window, True)
327 if parent is not None:
328 self._window.setWindowModality(QtCore.Qt.WindowModal)
329 self._window.setWindowTitle("%s - Quick Jump" % constants.__pretty_app_name__)
330 self._window.setWindowIcon(QtGui.QIcon(self._app.appIconPath))
331 self._window.setCentralWidget(centralWidget)
333 self._closeWindowAction = QtGui.QAction(None)
334 self._closeWindowAction.setText("Close")
335 self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
336 self._closeWindowAction.triggered.connect(self._on_close_window)
339 self._window.addAction(self._closeWindowAction)
340 self._window.addAction(self._app.quitAction)
341 self._window.addAction(self._app.fullscreenAction)
343 fileMenu = self._window.menuBar().addMenu("&Units")
344 fileMenu.addAction(self._closeWindowAction)
345 fileMenu.addAction(self._app.quitAction)
347 viewMenu = self._window.menuBar().addMenu("&View")
348 viewMenu.addAction(self._app.fullscreenAction)
350 self._window.addAction(self._app.logAction)
352 self.set_fullscreen(self._app.fullscreenAction.isChecked())
362 def set_fullscreen(self, isFullscreen):
364 self._window.showFullScreen()
366 self._window.showNormal()
368 @misc_utils.log_exception(_moduleLogger)
369 def _on_close_window(self, checked = True):
372 @misc_utils.log_exception(_moduleLogger)
373 def _on_result_clicked(self, item, columnIndex):
374 categoryName = unicode(item.text(0))
375 unitName = unicode(item.text(1))
376 catWindow = self._app.request_category()
377 unitsWindow = catWindow.select_category(categoryName)
378 unitsWindow.select_unit(unitName)
381 @misc_utils.log_exception(_moduleLogger)
382 def _on_search_edited(self, *args):
383 userInput = self._searchEntry.text()
384 if len(userInput) < self.MINIMAL_ENTRY:
387 self._resultsBox.clear()
388 lowerInput = str(userInput).lower()
389 for catIndex, category in enumerate(unit_data.UNIT_CATEGORIES):
390 units = unit_data.get_units(category)
391 for unitIndex, unit in enumerate(units):
392 loweredUnit = unit.lower()
393 if lowerInput in loweredUnit:
394 twi = QtGui.QTreeWidgetItem(self._resultsBox)
395 twi.setText(0, category)
399 class Recent(object):
401 def __init__(self, parent, app):
404 self._resultsBox = QtGui.QTreeWidget()
405 self._resultsBox.setHeaderLabels(["Categories", "Units"])
406 self._resultsBox.setHeaderHidden(True)
408 self._resultsBox.setAlternatingRowColors(True)
409 self._resultsBox.itemClicked.connect(self._on_result_clicked)
411 self._layout = QtGui.QVBoxLayout()
412 self._layout.addWidget(self._resultsBox)
414 centralWidget = QtGui.QWidget()
415 centralWidget.setLayout(self._layout)
417 self._window = QtGui.QMainWindow(parent)
418 self._window.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
419 maeqt.set_autorient(self._window, True)
420 maeqt.set_stackable(self._window, True)
421 if parent is not None:
422 self._window.setWindowModality(QtCore.Qt.WindowModal)
423 self._window.setWindowTitle("%s - Recent" % constants.__pretty_app_name__)
424 self._window.setWindowIcon(QtGui.QIcon(self._app.appIconPath))
425 self._window.setCentralWidget(centralWidget)
427 for cat, unit in self._app.get_recent():
428 twi = QtGui.QTreeWidgetItem(self._resultsBox)
432 self._closeWindowAction = QtGui.QAction(None)
433 self._closeWindowAction.setText("Close")
434 self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
435 self._closeWindowAction.triggered.connect(self._on_close_window)
438 self._window.addAction(self._closeWindowAction)
439 self._window.addAction(self._app.quitAction)
440 self._window.addAction(self._app.fullscreenAction)
442 fileMenu = self._window.menuBar().addMenu("&Units")
443 fileMenu.addAction(self._closeWindowAction)
444 fileMenu.addAction(self._app.quitAction)
446 viewMenu = self._window.menuBar().addMenu("&View")
447 viewMenu.addAction(self._app.fullscreenAction)
449 self._window.addAction(self._app.logAction)
451 self.set_fullscreen(self._app.fullscreenAction.isChecked())
461 def set_fullscreen(self, isFullscreen):
463 self._window.showFullScreen()
465 self._window.showNormal()
467 @misc_utils.log_exception(_moduleLogger)
468 def _on_close_window(self, checked = True):
471 @misc_utils.log_exception(_moduleLogger)
472 def _on_result_clicked(self, item, columnIndex):
473 categoryName = unicode(item.text(0))
474 unitName = unicode(item.text(1))
475 catWindow = self._app.request_category()
476 unitsWindow = catWindow.select_category(categoryName)
477 unitsWindow.select_unit(unitName)
481 class FavoriteCategoriesWindow(object):
483 def __init__(self, parent, app, source, hidden):
485 self._source = list(source)
486 self._hidden = hidden
488 self._categories = QtGui.QTreeWidget()
489 self._categories.setHeaderLabels(["Categories"])
490 self._categories.setHeaderHidden(True)
492 self._categories.setAlternatingRowColors(True)
493 self._categories.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
494 self._categories.setSelectionMode(QtGui.QAbstractItemView.MultiSelection)
495 self._childWidgets = []
496 for catName in self._source:
497 twi = QtGui.QTreeWidgetItem(self._categories)
498 twi.setText(0, catName)
499 self._childWidgets.append(twi)
500 if catName not in self._hidden:
501 self._categories.setItemSelected(twi, True)
502 self._selection = self._categories.selectionModel()
503 self._selection.selectionChanged.connect(self._on_selection_changed)
505 self._allButton = QtGui.QPushButton("All")
506 self._allButton.clicked.connect(self._on_select_all)
507 self._invertButton = QtGui.QPushButton("Invert")
508 self._invertButton.clicked.connect(self._on_invert_selection)
509 self._noneButton = QtGui.QPushButton("None")
510 self._noneButton.clicked.connect(self._on_select_none)
512 self._buttonLayout = QtGui.QHBoxLayout()
513 self._buttonLayout.addWidget(self._allButton)
514 self._buttonLayout.addWidget(self._invertButton)
515 self._buttonLayout.addWidget(self._noneButton)
517 self._layout = QtGui.QVBoxLayout()
518 self._layout.addWidget(self._categories)
519 self._layout.addLayout(self._buttonLayout)
521 centralWidget = QtGui.QWidget()
522 centralWidget.setLayout(self._layout)
524 self._window = QtGui.QMainWindow(parent)
525 self._window.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
526 maeqt.set_autorient(self._window, True)
527 maeqt.set_stackable(self._window, True)
528 if parent is not None:
529 self._window.setWindowModality(QtCore.Qt.WindowModal)
530 self._window.setWindowTitle("%s - Favorites" % constants.__pretty_app_name__)
531 self._window.setWindowIcon(QtGui.QIcon(self._app.appIconPath))
532 self._window.setCentralWidget(centralWidget)
534 self._closeWindowAction = QtGui.QAction(None)
535 self._closeWindowAction.setText("Close")
536 self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
537 self._closeWindowAction.triggered.connect(self._on_close_window)
540 self._window.addAction(self._closeWindowAction)
541 self._window.addAction(self._app.quitAction)
542 self._window.addAction(self._app.fullscreenAction)
544 fileMenu = self._window.menuBar().addMenu("&Units")
545 fileMenu.addAction(self._closeWindowAction)
546 fileMenu.addAction(self._app.quitAction)
548 viewMenu = self._window.menuBar().addMenu("&View")
549 viewMenu.addAction(self._app.fullscreenAction)
551 self._window.addAction(self._app.logAction)
553 self.set_fullscreen(self._app.fullscreenAction.isChecked())
563 def set_fullscreen(self, isFullscreen):
565 self._window.showFullScreen()
567 self._window.showNormal()
569 @misc_utils.log_exception(_moduleLogger)
570 def _on_select_all(self, checked = False):
571 for child in self._childWidgets:
572 self._categories.setItemSelected(child, True)
574 @misc_utils.log_exception(_moduleLogger)
575 def _on_invert_selection(self, checked = False):
576 for child in self._childWidgets:
577 isSelected = self._categories.isItemSelected(child)
578 self._categories.setItemSelected(child, not isSelected)
580 @misc_utils.log_exception(_moduleLogger)
581 def _on_select_none(self, checked = False):
582 for child in self._childWidgets:
583 self._categories.setItemSelected(child, False)
585 @misc_utils.log_exception(_moduleLogger)
586 def _on_selection_changed(self, selected, deselected):
590 for item in self._categories.selectedItems()
592 for name in self._source:
593 if name not in selectedNames:
594 self._hidden.add(name)
596 @misc_utils.log_exception(_moduleLogger)
597 def _on_close_window(self, checked = True):
601 class CategoryWindow(object):
603 def __init__(self, parent, app):
605 self._unitWindow = None
606 self._favoritesWindow = None
608 self._categories = QtGui.QTreeWidget()
609 self._categories.setHeaderLabels(["Categories"])
610 self._categories.itemClicked.connect(self._on_category_clicked)
611 self._categories.setHeaderHidden(True)
613 self._categories.setAlternatingRowColors(True)
614 for catName in unit_data.UNIT_CATEGORIES:
615 twi = QtGui.QTreeWidgetItem(self._categories)
616 twi.setText(0, catName)
618 self._layout = QtGui.QVBoxLayout()
619 self._layout.addWidget(self._categories)
621 centralWidget = QtGui.QWidget()
622 centralWidget.setLayout(self._layout)
624 self._window = QtGui.QMainWindow(parent)
625 self._window.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
626 maeqt.set_autorient(self._window, True)
627 maeqt.set_stackable(self._window, True)
628 if parent is not None:
629 self._window.setWindowModality(QtCore.Qt.WindowModal)
630 self._window.setWindowTitle("%s - Categories" % constants.__pretty_app_name__)
631 self._window.setWindowIcon(QtGui.QIcon(self._app.appIconPath))
632 self._window.setCentralWidget(centralWidget)
634 self._chooseFavoritesAction = QtGui.QAction(None)
635 self._chooseFavoritesAction.setText("Select Favorites")
636 self._chooseFavoritesAction.setShortcut(QtGui.QKeySequence("CTRL+f"))
637 self._chooseFavoritesAction.triggered.connect(self._on_choose_favorites)
639 self._app.showFavoritesAction.toggled.connect(self._on_show_favorites)
641 self._closeWindowAction = QtGui.QAction(None)
642 self._closeWindowAction.setText("Close")
643 self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
644 self._closeWindowAction.triggered.connect(self._on_close_window)
647 fileMenu = self._window.menuBar().addMenu("&Units")
648 fileMenu.addAction(self._chooseFavoritesAction)
650 viewMenu = self._window.menuBar().addMenu("&View")
651 viewMenu.addAction(self._app.showFavoritesAction)
652 viewMenu.addSeparator()
653 viewMenu.addAction(self._app.jumpAction)
654 viewMenu.addAction(self._app.recentAction)
656 self._window.addAction(self._closeWindowAction)
657 self._window.addAction(self._app.quitAction)
658 self._window.addAction(self._app.fullscreenAction)
660 fileMenu = self._window.menuBar().addMenu("&Units")
661 fileMenu.addAction(self._chooseFavoritesAction)
662 fileMenu.addAction(self._closeWindowAction)
663 fileMenu.addAction(self._app.quitAction)
665 viewMenu = self._window.menuBar().addMenu("&View")
666 viewMenu.addAction(self._app.showFavoritesAction)
667 viewMenu.addSeparator()
668 viewMenu.addAction(self._app.jumpAction)
669 viewMenu.addAction(self._app.recentAction)
670 viewMenu.addSeparator()
671 viewMenu.addAction(self._app.fullscreenAction)
673 self._window.addAction(self._app.logAction)
675 self._update_favorites()
676 self.set_fullscreen(self._app.fullscreenAction.isChecked())
683 def walk_children(self):
684 if self._unitWindow is not None:
685 yield self._unitWindow
686 if self._favoritesWindow is not None:
687 yield self._favoritesWindow
690 for child in self.walk_children():
694 def select_category(self, categoryName):
695 for child in self.walk_children():
697 self._unitWindow = UnitWindow(self._window, categoryName, self._app)
698 self._unitWindow.window.destroyed.connect(self._on_child_close)
700 i = unit_data.UNIT_CATEGORIES.index(categoryName)
701 rootIndex = self._categories.rootIndex()
702 currentIndex = self._categories.model().index(i, 0, rootIndex)
703 self._categories.scrollTo(currentIndex)
704 return self._unitWindow
706 def set_fullscreen(self, isFullscreen):
708 self._window.showFullScreen()
710 self._window.showNormal()
711 for child in self.walk_children():
712 child.set_fullscreen(isFullscreen)
714 def _update_favorites(self):
715 if self._app.showFavoritesAction.isChecked():
716 assert self._categories.topLevelItemCount() == len(unit_data.UNIT_CATEGORIES)
717 for i, catName in enumerate(unit_data.UNIT_CATEGORIES):
718 if catName in self._app.hiddenCategories:
719 self._categories.setRowHidden(i, self._categories.rootIndex(), True)
721 self._categories.setRowHidden(i, self._categories.rootIndex(), False)
723 for i in xrange(self._categories.topLevelItemCount()):
724 self._categories.setRowHidden(i, self._categories.rootIndex(), False)
726 @misc_utils.log_exception(_moduleLogger)
727 def _on_choose_favorites(self, obj = None):
728 assert self._favoritesWindow is None
729 self._favoritesWindow = FavoriteCategoriesWindow(
732 unit_data.UNIT_CATEGORIES,
733 self._app.hiddenCategories
735 self._favoritesWindow.window.destroyed.connect(self._on_close_favorites)
736 return self._favoritesWindow
738 @misc_utils.log_exception(_moduleLogger)
739 def _on_show_favorites(self, checked = True):
741 assert self._categories.topLevelItemCount() == len(unit_data.UNIT_CATEGORIES)
742 for i, catName in enumerate(unit_data.UNIT_CATEGORIES):
743 if catName in self._app.hiddenCategories:
744 self._categories.setRowHidden(i, self._categories.rootIndex(), True)
746 for i in xrange(self._categories.topLevelItemCount()):
747 self._categories.setRowHidden(i, self._categories.rootIndex(), False)
749 @misc_utils.log_exception(_moduleLogger)
750 def _on_close_favorites(self, obj = None):
751 self._favoritesWindow = None
752 self._update_favorites()
754 @misc_utils.log_exception(_moduleLogger)
755 def _on_child_close(self, obj = None):
756 self._unitWindow = None
758 @misc_utils.log_exception(_moduleLogger)
759 def _on_close_window(self, checked = True):
762 @misc_utils.log_exception(_moduleLogger)
763 def _on_category_clicked(self, item, columnIndex):
764 categoryName = unicode(item.text(0))
765 self.select_category(categoryName)
768 class UnitData(object):
770 HEADERS = ["Name", "Value", "", "Unit"]
771 ALIGNMENT = [QtCore.Qt.AlignLeft, QtCore.Qt.AlignRight, QtCore.Qt.AlignLeft, QtCore.Qt.AlignLeft]
777 def __init__(self, name, unit, description, conversion):
780 self._description = description
781 self._conversion = conversion
784 self._integerDisplay, self._fractionalDisplay = split_number(self._value)
794 def update_value(self, newValue):
795 self._value = newValue
796 self._integerDisplay, self._fractionalDisplay = split_number(newValue)
803 def conversion(self):
804 return self._conversion
806 def data(self, column):
808 return [self._name, self._integerDisplay, self._fractionalDisplay, self._unit][column]
813 class UnitModel(QtCore.QAbstractItemModel):
815 def __init__(self, categoryName, parent=None):
816 super(UnitModel, self).__init__(parent)
817 self._categoryName = categoryName
818 self._unitData = unit_data.UNIT_DESCRIPTIONS[self._categoryName]
821 for key in unit_data.get_units(self._categoryName):
822 conversion, unit, description = self._unitData[key]
823 self._children.append(UnitData(key, unit, description, conversion))
824 self._sortSettings = None
826 @misc_utils.log_exception(_moduleLogger)
827 def columnCount(self, parent):
831 return len(UnitData.HEADERS)
833 @misc_utils.log_exception(_moduleLogger)
834 def data(self, index, role):
835 if not index.isValid():
837 elif role == QtCore.Qt.TextAlignmentRole:
838 return UnitData.ALIGNMENT[index.column()]
839 elif role != QtCore.Qt.DisplayRole:
842 item = index.internalPointer()
843 if isinstance(item, UnitData):
844 return item.data(index.column())
845 elif item is UnitData.HEADERS:
846 return item[index.column()]
848 @misc_utils.log_exception(_moduleLogger)
849 def sort(self, column, order = QtCore.Qt.AscendingOrder):
850 self._sortSettings = column, order
851 isReverse = order == QtCore.Qt.AscendingOrder
852 if column == UnitData.NAME_COLUMN:
853 key_func = lambda item: item.name
854 elif column in [UnitData.VALUE_COLUMN_0, UnitData.VALUE_COLUMN_1]:
855 key_func = lambda item: item.value
856 elif column == UnitData.UNIT_COLUMN:
857 key_func = lambda item: item.unit
858 self._children.sort(key=key_func, reverse = isReverse)
862 @misc_utils.log_exception(_moduleLogger)
863 def flags(self, index):
864 if not index.isValid():
865 return QtCore.Qt.NoItemFlags
867 return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
869 @misc_utils.log_exception(_moduleLogger)
870 def headerData(self, section, orientation, role):
871 if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
872 return UnitData.HEADERS[section]
876 @misc_utils.log_exception(_moduleLogger)
877 def index(self, row, column, parent):
878 if not self.hasIndex(row, column, parent):
879 return QtCore.QModelIndex()
882 return QtCore.QModelIndex()
884 parentItem = UnitData.HEADERS
885 childItem = self._children[row]
887 return self.createIndex(row, column, childItem)
889 return QtCore.QModelIndex()
891 @misc_utils.log_exception(_moduleLogger)
892 def parent(self, index):
893 if not index.isValid():
894 return QtCore.QModelIndex()
896 childItem = index.internalPointer()
897 if isinstance(childItem, UnitData):
898 return QtCore.QModelIndex()
899 elif childItem is UnitData.HEADERS:
902 @misc_utils.log_exception(_moduleLogger)
903 def rowCount(self, parent):
904 if 0 < parent.column():
907 if not parent.isValid():
908 return len(self._children)
910 return len(self._children)
912 def get_unit(self, index):
914 return self._children[index]
916 def get_unit_names(self):
917 for child in self._children:
920 def index_unit(self, unitName):
921 for i, child in enumerate(self._children):
922 if child.name == unitName:
925 raise RuntimeError("Unit not found")
927 def update_values(self, fromIndex, userInput):
928 value = self._sanitize_value(userInput)
929 func, arg = self._children[fromIndex].conversion
930 base = func.to_base(value, arg)
931 for i, child in enumerate(self._children):
934 func, arg = child.conversion
935 newValue = func.from_base(base, arg)
936 child.update_value(newValue)
939 self._sortSettings is not None and
940 self._sortSettings[0] in [UnitData.VALUE_COLUMN_0, UnitData.VALUE_COLUMN_1]
942 self.sort(*self._sortSettings)
945 self._values_changed()
948 return len(self._children)
950 def _values_changed(self):
951 topLeft = self.createIndex(0, UnitData.VALUE_COLUMN_0, self._children[0])
952 bottomRight = self.createIndex(len(self._children)-1, UnitData.VALUE_COLUMN_1, self._children[-1])
953 self.dataChanged.emit(topLeft, bottomRight)
955 def _all_changed(self):
956 topLeft = self.createIndex(0, 0, self._children[0])
957 bottomRight = self.createIndex(len(self._children)-1, len(UnitData.HEADERS)-1, self._children[-1])
958 self.dataChanged.emit(topLeft, bottomRight)
960 def _sanitize_value(self, userEntry):
961 if self._categoryName == "Computer Numbers":
970 value = float(userEntry)
974 class UnitWindow(object):
976 def __init__(self, parent, category, app):
978 self._categoryName = category
979 self._selectedIndex = 0
980 self._favoritesWindow = None
982 self._selectedUnitName = QtGui.QLabel()
983 self._selectedUnitValue = QtGui.QLineEdit()
984 self._selectedUnitValue.setInputMethodHints(QtCore.Qt.ImhPreferNumbers)
985 self._selectedUnitValue.textEdited.connect(self._on_value_edited)
986 self._selectedUnitSymbol = QtGui.QLabel()
988 self._selectedUnitLayout = QtGui.QHBoxLayout()
989 self._selectedUnitLayout.addWidget(self._selectedUnitName)
990 self._selectedUnitLayout.addWidget(self._selectedUnitValue)
991 self._selectedUnitLayout.addWidget(self._selectedUnitSymbol)
993 self._unitsModel = UnitModel(self._categoryName)
994 self._unitsView = QtGui.QTreeView()
995 self._unitsView.setModel(self._unitsModel)
996 self._unitsView.clicked.connect(self._on_unit_clicked)
997 self._unitsView.setUniformRowHeights(True)
998 self._unitsView.setSortingEnabled(True)
999 self._unitsView.setTextElideMode(QtCore.Qt.ElideNone)
1000 self._unitsView.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
1002 self._unitsView.setAlternatingRowColors(True)
1004 self._unitsView.setHeaderHidden(True)
1006 viewHeader = self._unitsView.header()
1007 viewHeader.setSortIndicatorShown(True)
1008 viewHeader.setClickable(True)
1010 viewHeader.setResizeMode(UnitData.NAME_COLUMN, QtGui.QHeaderView.ResizeToContents)
1011 viewHeader.setResizeMode(UnitData.VALUE_COLUMN_0, QtGui.QHeaderView.ResizeToContents)
1012 viewHeader.setResizeMode(UnitData.VALUE_COLUMN_1, QtGui.QHeaderView.ResizeToContents)
1013 viewHeader.setResizeMode(UnitData.UNIT_COLUMN, QtGui.QHeaderView.ResizeToContents)
1014 viewHeader.setStretchLastSection(False)
1016 # Trying to make things faster by locking in the initial size of the immutable columns
1017 nameSize = viewHeader.sectionSize(UnitData.NAME_COLUMN)
1018 viewHeader.setResizeMode(UnitData.NAME_COLUMN, QtGui.QHeaderView.Fixed)
1019 viewHeader.resizeSection(UnitData.NAME_COLUMN, nameSize)
1020 unitSize = viewHeader.sectionSize(UnitData.UNIT_COLUMN)
1021 viewHeader.setResizeMode(UnitData.UNIT_COLUMN, QtGui.QHeaderView.Fixed)
1022 viewHeader.resizeSection(UnitData.UNIT_COLUMN, unitSize)
1024 self._layout = QtGui.QVBoxLayout()
1025 self._layout.addLayout(self._selectedUnitLayout)
1026 self._layout.addWidget(self._unitsView)
1028 centralWidget = QtGui.QWidget()
1029 centralWidget.setLayout(self._layout)
1031 self._window = QtGui.QMainWindow(parent)
1032 self._window.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
1033 maeqt.set_autorient(self._window, True)
1034 maeqt.set_stackable(self._window, True)
1035 if parent is not None:
1036 self._window.setWindowModality(QtCore.Qt.WindowModal)
1037 self._window.setWindowTitle("%s - %s" % (constants.__pretty_app_name__, category))
1038 self._window.setWindowIcon(QtGui.QIcon(app.appIconPath))
1039 self._window.setCentralWidget(centralWidget)
1041 defaultUnitName = self._app.get_recent_unit(self._categoryName)
1043 self.select_unit(defaultUnitName)
1045 self._select_unit(0)
1046 self._unitsModel.sort(UnitData.NAME_COLUMN)
1048 self._sortActionGroup = QtGui.QActionGroup(None)
1049 self._sortByNameAction = QtGui.QAction(self._sortActionGroup)
1050 self._sortByNameAction.setText("Sort By Name")
1051 self._sortByNameAction.setStatusTip("Sort the units by name")
1052 self._sortByNameAction.setToolTip("Sort the units by name")
1053 self._sortByNameAction.setCheckable(True)
1054 self._sortByValueAction = QtGui.QAction(self._sortActionGroup)
1055 self._sortByValueAction.setText("Sort By Value")
1056 self._sortByValueAction.setStatusTip("Sort the units by value")
1057 self._sortByValueAction.setToolTip("Sort the units by value")
1058 self._sortByValueAction.setCheckable(True)
1059 self._sortByUnitAction = QtGui.QAction(self._sortActionGroup)
1060 self._sortByUnitAction.setText("Sort By Unit")
1061 self._sortByUnitAction.setStatusTip("Sort the units by unit")
1062 self._sortByUnitAction.setToolTip("Sort the units by unit")
1063 self._sortByUnitAction.setCheckable(True)
1065 self._sortByValueAction.setChecked(True)
1067 self._chooseFavoritesAction = QtGui.QAction(None)
1068 self._chooseFavoritesAction.setText("Select Favorites")
1069 self._chooseFavoritesAction.setShortcut(QtGui.QKeySequence("CTRL+f"))
1070 self._chooseFavoritesAction.triggered.connect(self._on_choose_favorites)
1072 self._app.showFavoritesAction.toggled.connect(self._on_show_favorites)
1074 self._previousUnitAction = QtGui.QAction(None)
1075 self._previousUnitAction.setText("Previous Unit")
1076 self._previousUnitAction.setShortcut(QtGui.QKeySequence("Up"))
1077 self._previousUnitAction.triggered.connect(self._on_previous_unit)
1079 self._nextUnitAction = QtGui.QAction(None)
1080 self._nextUnitAction.setText("Next Unit")
1081 self._nextUnitAction.setShortcut(QtGui.QKeySequence("Down"))
1082 self._nextUnitAction.triggered.connect(self._on_next_unit)
1084 self._closeWindowAction = QtGui.QAction(None)
1085 self._closeWindowAction.setText("Close Window")
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._chooseFavoritesAction)
1097 viewMenu = self._window.menuBar().addMenu("&View")
1098 viewMenu.addAction(self._app.showFavoritesAction)
1099 viewMenu.addSeparator()
1100 viewMenu.addAction(self._sortByNameAction)
1101 viewMenu.addAction(self._sortByValueAction)
1102 viewMenu.addAction(self._sortByUnitAction)
1103 viewMenu.addSeparator()
1104 viewMenu.addAction(self._app.jumpAction)
1105 viewMenu.addAction(self._app.recentAction)
1107 fileMenu = self._window.menuBar().addMenu("&Units")
1108 fileMenu.addAction(self._chooseFavoritesAction)
1109 fileMenu.addAction(self._closeWindowAction)
1110 fileMenu.addAction(self._app.quitAction)
1112 viewMenu = self._window.menuBar().addMenu("&View")
1113 viewMenu.addAction(self._app.showFavoritesAction)
1114 viewMenu.addSeparator()
1115 viewMenu.addAction(self._sortByNameAction)
1116 viewMenu.addAction(self._sortByValueAction)
1117 viewMenu.addAction(self._sortByUnitAction)
1118 viewMenu.addSeparator()
1119 viewMenu.addAction(self._app.jumpAction)
1120 viewMenu.addAction(self._app.recentAction)
1121 viewMenu.addSeparator()
1122 viewMenu.addAction(self._app.fullscreenAction)
1124 self._sortByNameAction.triggered.connect(self._on_sort_by_name)
1125 self._sortByValueAction.triggered.connect(self._on_sort_by_value)
1126 self._sortByUnitAction.triggered.connect(self._on_sort_by_unit)
1128 self._window.addAction(self._app.logAction)
1129 self._window.addAction(self._nextUnitAction)
1130 self._window.addAction(self._previousUnitAction)
1131 self._window.addAction(self._chooseFavoritesAction)
1133 self._update_favorites()
1134 self.set_fullscreen(self._app.fullscreenAction.isChecked())
1142 for child in self.walk_children():
1144 self._window.close()
1146 def set_fullscreen(self, isFullscreen):
1148 self._window.showFullScreen()
1150 self._window.showNormal()
1152 def select_unit(self, unitName):
1153 index = self._unitsModel.index_unit(unitName)
1154 self._select_unit(index)
1156 def walk_children(self):
1157 if self._favoritesWindow is not None:
1158 yield self._favoritesWindow
1160 def _update_favorites(self, force = False):
1161 if self._app.showFavoritesAction.isChecked():
1162 unitNames = list(self._unitsModel.get_unit_names())
1163 for i, unitName in enumerate(unitNames):
1164 if unitName in self._app.get_hidden_units(self._categoryName):
1165 self._unitsView.setRowHidden(i, self._unitsView.rootIndex(), True)
1167 self._unitsView.setRowHidden(i, self._unitsView.rootIndex(), False)
1170 for i in xrange(len(self._unitsModel)):
1171 self._unitsView.setRowHidden(i, self._unitsView.rootIndex(), False)
1173 @misc_utils.log_exception(_moduleLogger)
1174 def _on_choose_favorites(self, obj = None):
1175 assert self._favoritesWindow is None
1176 self._favoritesWindow = FavoriteCategoriesWindow(
1179 unit_data.get_units(self._categoryName),
1180 self._app.get_hidden_units(self._categoryName)
1182 self._favoritesWindow.window.destroyed.connect(self._on_close_favorites)
1183 return self._favoritesWindow
1185 @misc_utils.log_exception(_moduleLogger)
1186 def _on_show_favorites(self, checked = True):
1188 unitNames = list(self._unitsModel.get_unit_names())
1189 for i, unitName in enumerate(unitNames):
1190 if unitName in self._app.get_hidden_units(self._categoryName):
1191 self._unitsView.setRowHidden(i, self._unitsView.rootIndex(), True)
1193 for i in xrange(len(self._unitsModel)):
1194 self._unitsView.setRowHidden(i, self._unitsView.rootIndex(), False)
1196 @misc_utils.log_exception(_moduleLogger)
1197 def _on_close_favorites(self, obj = None):
1198 self._favoritesWindow = None
1199 self._update_favorites(force=True)
1201 @misc_utils.log_exception(_moduleLogger)
1202 def _on_previous_unit(self, checked = True):
1203 self._select_unit(self._selectedIndex - 1)
1205 @misc_utils.log_exception(_moduleLogger)
1206 def _on_next_unit(self, checked = True):
1207 self._select_unit(self._selectedIndex + 1)
1209 @misc_utils.log_exception(_moduleLogger)
1210 def _on_close_window(self, checked = True):
1213 @misc_utils.log_exception(_moduleLogger)
1214 def _on_sort_by_name(self, checked = False):
1215 self._unitsModel.sort(UnitData.NAME_COLUMN, QtCore.Qt.DescendingOrder)
1217 @misc_utils.log_exception(_moduleLogger)
1218 def _on_sort_by_value(self, checked = False):
1219 self._unitsModel.sort(UnitData.VALUE_COLUMN_0)
1221 @misc_utils.log_exception(_moduleLogger)
1222 def _on_sort_by_unit(self, checked = False):
1223 self._unitsModel.sort(UnitData.UNIT_COLUMN, QtCore.Qt.DescendingOrder)
1225 @misc_utils.log_exception(_moduleLogger)
1226 def _on_unit_clicked(self, index):
1227 self._select_unit(index.row())
1229 @misc_utils.log_exception(_moduleLogger)
1230 def _on_value_edited(self, *args):
1231 userInput = self._selectedUnitValue.text()
1232 self._unitsModel.update_values(self._selectedIndex, str(userInput))
1233 self._update_favorites()
1235 def _select_unit(self, index):
1236 unit = self._unitsModel.get_unit(index)
1237 self._selectedUnitName.setText(unit.name)
1238 self._selectedUnitValue.setText(str(unit.value))
1239 self._selectedUnitSymbol.setText(unit.unit)
1241 self._selectedIndex = index
1242 qindex = self._unitsModel.createIndex(index, 0, self._unitsModel.get_unit(index))
1243 self._unitsView.scrollTo(qindex)
1244 self._app.add_recent(self._categoryName, self._unitsModel.get_unit(index).name)
1248 app = QtGui.QApplication([])
1249 handle = Gonvert(app)
1253 if __name__ == "__main__":
1254 logging.basicConfig(level = logging.DEBUG)
1256 os.makedirs(constants._data_path_)