Moving the webpage to the wiki
[gonvert] / src / gonvert_qt.py
index ab5a754..02b64c9 100755 (executable)
@@ -3,7 +3,6 @@
 
 #@todo Research Fn
 #@todo Research optimizations
-#@todo Look into switching favorites from selection to checking?
 
 from __future__ import with_statement
 
@@ -29,6 +28,10 @@ IS_MAEMO = True
 
 
 def split_number(number):
+       if number == 0.0:
+               # Optimize the startup case
+               return "0.", "0"
+
        try:
                fractional, integer = math.modf(number)
        except TypeError:
@@ -40,15 +43,16 @@ def split_number(number):
                if "e+" in integerDisplay:
                        integerDisplay = number
                        fractionalDisplay = ""
-               elif "e-" in fractionalDisplay and 0.0 < integer:
-                       integerDisplay = number
-                       fractionalDisplay = ""
                elif "e-" in fractionalDisplay:
-                       integerDisplay = ""
-                       fractionalDisplay = number
+                       if 0.0 < integer:
+                               integerDisplay = number
+                               fractionalDisplay = ""
+                       else:
+                               integerDisplay = ""
+                               fractionalDisplay = number
                else:
-                       integerDisplay = integerDisplay.split(".", 1)[0] + "."
-                       fractionalDisplay = fractionalDisplay.rsplit(".", 1)[-1]
+                       integerDisplay = integerDisplay[0:-2] + "."
+                       fractionalDisplay = fractionalDisplay[2:]
 
        return integerDisplay, fractionalDisplay
 
@@ -156,7 +160,6 @@ class Gonvert(object):
                        self._mainWindow.select_category(self._recent[-1][0])
 
        def request_category(self):
-
                if self._condensedAction.isChecked():
                        if self._catWindow is not None:
                                self._catWindow.hide()
@@ -1036,19 +1039,19 @@ class FavoritesWindow(object):
                self._categories = QtGui.QTreeWidget()
                self._categories.setHeaderLabels(["Categories"])
                self._categories.setHeaderHidden(True)
+               self._categories.setSelectionMode(QtGui.QAbstractItemView.NoSelection)
                if not IS_MAEMO:
                        self._categories.setAlternatingRowColors(True)
-               self._categories.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
-               self._categories.setSelectionMode(QtGui.QAbstractItemView.MultiSelection)
                self._childWidgets = []
                for catName in self._source:
                        twi = QtGui.QTreeWidgetItem(self._categories)
                        twi.setText(0, catName)
                        self._childWidgets.append(twi)
-                       if catName not in self._hidden:
-                               self._categories.setItemSelected(twi, True)
-               self._selection = self._categories.selectionModel()
-               self._selection.selectionChanged.connect(self._on_selection_changed)
+                       if catName in self._hidden:
+                               twi.setCheckState(0, QtCore.Qt.Unchecked)
+                       else:
+                               twi.setCheckState(0, QtCore.Qt.Checked)
+               self._categories.itemChanged.connect(self._on_item_changed)
 
                self._allButton = QtGui.QPushButton("All")
                self._allButton.clicked.connect(self._on_select_all)
@@ -1121,29 +1124,36 @@ class FavoritesWindow(object):
        @misc_utils.log_exception(_moduleLogger)
        def _on_select_all(self, checked = False):
                for child in self._childWidgets:
-                       self._categories.setItemSelected(child, True)
+                       child.setCheckState(0, QtCore.Qt.Checked)
 
        @misc_utils.log_exception(_moduleLogger)
        def _on_invert_selection(self, checked = False):
                for child in self._childWidgets:
-                       isSelected = self._categories.isItemSelected(child)
-                       self._categories.setItemSelected(child, not isSelected)
+                       state = child.checkState(0)
+                       if state == QtCore.Qt.Unchecked:
+                               newState = QtCore.Qt.Checked
+                       elif state == QtCore.Qt.Checked:
+                               newState = QtCore.Qt.Unchecked
+                       else:
+                               raise RuntimeError("Bad check state %r" % state)
+                       child.setCheckState(0, newState)
 
        @misc_utils.log_exception(_moduleLogger)
        def _on_select_none(self, checked = False):
                for child in self._childWidgets:
-                       self._categories.setItemSelected(child, False)
+                       child.setCheckState(0, QtCore.Qt.Unchecked)
 
        @misc_utils.log_exception(_moduleLogger)
-       def _on_selection_changed(self, selected, deselected):
-               self._hidden.clear()
-               selectedNames = set(
-                       str(item.text(0))
-                       for item in self._categories.selectedItems()
-               )
-               for name in self._source:
-                       if name not in selectedNames:
-                               self._hidden.add(name)
+       def _on_item_changed(self, item, column):
+               state = item.checkState(column)
+               if state == QtCore.Qt.Unchecked:
+                       name = str(item.text(column))
+                       self._hidden.add(name)
+               elif state == QtCore.Qt.Checked:
+                       name = str(item.text(column))
+                       self._hidden.remove(name)
+               else:
+                       raise RuntimeError("Bad check state %r" % state)
 
        @misc_utils.log_exception(_moduleLogger)
        def _on_close_window(self, checked = True):
@@ -1344,6 +1354,11 @@ class UnitData(object):
        VALUE_COLUMN_1 = 2
        UNIT_COLUMN = 3
 
+       __slots__ = [
+               "_name", "_unit", "_description", "_conversion",
+               "_value", "_integerDisplay", "_fractionalDisplay",
+       ]
+
        def __init__(self, name, unit, description, conversion):
                self._name = name
                self._unit = unit
@@ -1386,6 +1401,10 @@ class UnitModel(QtCore.QAbstractItemModel):
                super(UnitModel, self).__init__(parent)
                self._categoryName = categoryName
                self._unitData = unit_data.UNIT_DESCRIPTIONS[self._categoryName]
+               if self._categoryName == "Computer Numbers":
+                       self._sanitize_value = self._sanitize_alpha_value
+               else:
+                       self._sanitize_value = self._sanitize_numeric_value
 
                self._children = []
                for key in unit_data.get_units(self._categoryName):
@@ -1402,19 +1421,20 @@ class UnitModel(QtCore.QAbstractItemModel):
 
        @misc_utils.log_exception(_moduleLogger)
        def data(self, index, role):
-               if not index.isValid():
-                       return None
+               #if not index.isValid():
+               #       return None
+
+               if role == QtCore.Qt.DisplayRole:
+                       item = index.internalPointer()
+                       if isinstance(item, UnitData):
+                               return item.data(index.column())
+                       elif item is UnitData.HEADERS:
+                               return item[index.column()]
                elif role == QtCore.Qt.TextAlignmentRole:
                        return UnitData.ALIGNMENT[index.column()]
-               elif role != QtCore.Qt.DisplayRole:
+               else:
                        return None
 
-               item = index.internalPointer()
-               if isinstance(item, UnitData):
-                       return item.data(index.column())
-               elif item is UnitData.HEADERS:
-                       return item[index.column()]
-
        @misc_utils.log_exception(_moduleLogger)
        def sort(self, column, order = QtCore.Qt.AscendingOrder):
                self._sortSettings = column, order
@@ -1445,11 +1465,10 @@ class UnitModel(QtCore.QAbstractItemModel):
 
        @misc_utils.log_exception(_moduleLogger)
        def index(self, row, column, parent):
-               if not self.hasIndex(row, column, parent):
-                       return QtCore.QModelIndex()
-
-               if parent.isValid():
-                       return QtCore.QModelIndex()
+               #if not self.hasIndex(row, column, parent):
+               #       return QtCore.QModelIndex()
+               #elif parent.isValid():
+               #       return QtCore.QModelIndex()
 
                parentItem = UnitData.HEADERS
                childItem = self._children[row]
@@ -1509,8 +1528,10 @@ class UnitModel(QtCore.QAbstractItemModel):
                ):
                        # Sort takes care of marking everything as changed
                        self.sort(*self._sortSettings)
+                       return True
                else:
                        self._values_changed()
+                       return False
 
        def __len__(self):
                return len(self._children)
@@ -1525,17 +1546,18 @@ class UnitModel(QtCore.QAbstractItemModel):
                bottomRight = self.createIndex(len(self._children)-1, len(UnitData.HEADERS)-1, self._children[-1])
                self.dataChanged.emit(topLeft, bottomRight)
 
-       def _sanitize_value(self, userEntry):
-               if self._categoryName == "Computer Numbers":
-                       if userEntry == '':
-                               value = '0'
-                       else:
-                               value = userEntry
+       def _sanitize_alpha_value(self, userEntry):
+               if userEntry:
+                       value = userEntry
                else:
-                       if userEntry == '':
-                               value = 0.0
-                       else:
-                               value = float(userEntry)
+                       value = '0'
+               return value
+
+       def _sanitize_numeric_value(self, userEntry):
+               if userEntry:
+                       value = float(userEntry)
+               else:
+                       value = 0.0
                return value
 
 
@@ -1552,6 +1574,10 @@ class UnitWindow(object):
                self._selectedUnitValue.textEdited.connect(self._on_value_edited)
                maeqt.mark_numbers_preferred(self._selectedUnitValue)
                self._selectedUnitSymbol = QtGui.QLabel()
+               self._updateDelayTimer = QtCore.QTimer()
+               self._updateDelayTimer.setInterval(100)
+               self._updateDelayTimer.setSingleShot(True)
+               self._updateDelayTimer.timeout.connect(self._on_value_edited_delayed)
 
                self._selectedUnitLayout = QtGui.QHBoxLayout()
                self._selectedUnitLayout.addWidget(self._selectedUnitName)
@@ -1561,15 +1587,15 @@ class UnitWindow(object):
                self._unitsModel = UnitModel(self._categoryName)
                self._unitsView = QtGui.QTreeView()
                self._unitsView.setModel(self._unitsModel)
-               self._unitsView.clicked.connect(self._on_unit_clicked)
                self._unitsView.setUniformRowHeights(True)
                self._unitsView.setSortingEnabled(True)
                self._unitsView.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
                self._unitsView.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
                self._unitsView.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
+               self._unitsView.setHeaderHidden(True)
+               self._unitsView.clicked.connect(self._on_unit_clicked)
                if not IS_MAEMO:
                        self._unitsView.setAlternatingRowColors(True)
-               self._unitsView.setHeaderHidden(True)
 
                viewHeader = self._unitsView.header()
                viewHeader.setSortIndicatorShown(True)
@@ -1582,7 +1608,7 @@ class UnitWindow(object):
                viewHeader.setStretchLastSection(False)
 
                # Trying to make things faster by locking in the initial size of the immutable columns
-               nameSize = min(viewHeader.sectionSize(UnitData.NAME_COLUMN), 150)
+               nameSize = min(viewHeader.sectionSize(UnitData.NAME_COLUMN), 300)
                viewHeader.setResizeMode(UnitData.NAME_COLUMN, QtGui.QHeaderView.Fixed)
                viewHeader.resizeSection(UnitData.NAME_COLUMN, nameSize)
                unitSize = min(viewHeader.sectionSize(UnitData.UNIT_COLUMN), 150)
@@ -1731,11 +1757,21 @@ class UnitWindow(object):
                if self._favoritesWindow is not None:
                        yield self._favoritesWindow
 
+       def _select_unit(self, index):
+               unit = self._unitsModel.get_unit(index)
+               self._selectedUnitName.setText(unit.name)
+               self._selectedUnitValue.setText(str(unit.value))
+               self._selectedUnitSymbol.setText(unit.unit)
+
+               self._selectedIndex = index
+               self._app.add_recent(self._categoryName, self._unitsModel.get_unit(index).name)
+
        def _update_favorites(self, force = False):
                if self._app.showFavoritesAction.isChecked():
                        unitNames = list(self._unitsModel.get_unit_names())
+                       hiddenUnits = self._app.get_hidden_units(self._categoryName)
                        for i, unitName in enumerate(unitNames):
-                               if unitName in self._app.get_hidden_units(self._categoryName):
+                               if unitName in hiddenUnits:
                                        self._unitsView.setRowHidden(i, self._unitsView.rootIndex(), True)
                                else:
                                        self._unitsView.setRowHidden(i, self._unitsView.rootIndex(), False)
@@ -1748,8 +1784,9 @@ class UnitWindow(object):
        def _on_show_favorites(self, checked = True):
                if checked:
                        unitNames = list(self._unitsModel.get_unit_names())
+                       hiddenUnits = self._app.get_hidden_units(self._categoryName)
                        for i, unitName in enumerate(unitNames):
-                               if unitName in self._app.get_hidden_units(self._categoryName):
+                               if unitName in hiddenUnits:
                                        self._unitsView.setRowHidden(i, self._unitsView.rootIndex(), True)
                else:
                        for i in xrange(len(self._unitsModel)):
@@ -1774,11 +1811,33 @@ class UnitWindow(object):
 
        @misc_utils.log_exception(_moduleLogger)
        def _on_previous_unit(self, checked = True):
-               self._select_unit(self._selectedIndex - 1)
+               index = self._selectedIndex - 1
+               unitData = self._unitsModel.get_unit(index)
+               unitName = unitData.name
+
+               if self._app.showFavoritesAction.isChecked():
+                       hiddenUnits = self._app.get_hidden_units(self._categoryName)
+                       while unitName in hiddenUnits:
+                               index -= 1
+                               unitData = self._unitsModel.get_unit(index)
+                               unitName = unitData.name
+
+               self.select_unit(unitName)
 
        @misc_utils.log_exception(_moduleLogger)
        def _on_next_unit(self, checked = True):
-               self._select_unit(self._selectedIndex + 1)
+               index = self._selectedIndex + 1
+               unitData = self._unitsModel.get_unit(index)
+               unitName = unitData.name
+
+               if self._app.showFavoritesAction.isChecked():
+                       hiddenUnits = self._app.get_hidden_units(self._categoryName)
+                       while unitName in hiddenUnits:
+                               index += 1
+                               unitData = self._unitsModel.get_unit(index)
+                               unitName = unitData.name
+
+               self.select_unit(unitName)
 
        @misc_utils.log_exception(_moduleLogger)
        def _on_close_window(self, checked = True):
@@ -1802,24 +1861,24 @@ class UnitWindow(object):
 
        @misc_utils.log_exception(_moduleLogger)
        def _on_value_edited(self, *args):
-               userInput = self._selectedUnitValue.text()
-               self._unitsModel.update_values(self._selectedIndex, str(userInput))
-               self._update_favorites()
+               if not self._updateDelayTimer.isActive():
+                       self._updateDelayTimer.start()
 
-       def _select_unit(self, index):
-               unit = self._unitsModel.get_unit(index)
-               self._selectedUnitName.setText(unit.name)
-               self._selectedUnitValue.setText(str(unit.value))
-               self._selectedUnitSymbol.setText(unit.unit)
-
-               self._selectedIndex = index
-               self._app.add_recent(self._categoryName, self._unitsModel.get_unit(index).name)
+       @misc_utils.log_exception(_moduleLogger)
+       def _on_value_edited_delayed(self, *args):
+               userInput = str(self._selectedUnitValue.text())
+               orderChanged = self._unitsModel.update_values(self._selectedIndex, userInput)
+               if orderChanged:
+                       self._update_favorites()
 
 
 def run_gonvert():
        app = QtGui.QApplication([])
        handle = Gonvert(app)
-       return app.exec_()
+       if constants.PROFILE_STARTUP:
+               return 0
+       else:
+               return app.exec_()
 
 
 if __name__ == "__main__":