0ac3d6ac8e132c17fcb1255801251aaa9d144739
[gonvert] / src / gonvert_qt.py
1 #!/usr/bin/env python
2 # -*- coding: UTF8 -*-
3
4 from __future__ import with_statement
5
6 import sys
7 import os
8 import math
9 import logging
10
11 from PyQt4 import QtGui
12 from PyQt4 import QtCore
13
14 import constants
15 from util import misc as misc_utils
16 import unit_data
17
18
19 _moduleLogger = logging.getLogger("gonvert_glade")
20
21
22 def change_menu_label(widgets, labelname, newtext):
23         item_label = widgets.get_widget(labelname).get_children()[0]
24         item_label.set_text(newtext)
25
26
27 def split_number(number):
28         try:
29                 fractional, integer = math.modf(number)
30         except TypeError:
31                 integerDisplay = number
32                 fractionalDisplay = ""
33         else:
34                 integerDisplay = str(integer)
35                 fractionalDisplay = str(fractional)
36                 if "e+" in integerDisplay:
37                         integerDisplay = number
38                         fractionalDisplay = ""
39                 elif "e-" in fractionalDisplay and 0.0 < integer:
40                         integerDisplay = number
41                         fractionalDisplay = ""
42                 elif "e-" in fractionalDisplay:
43                         integerDisplay = ""
44                         fractionalDisplay = number
45                 else:
46                         integerDisplay = integerDisplay.split(".", 1)[0] + "."
47                         fractionalDisplay = fractionalDisplay.rsplit(".", 1)[-1]
48
49         return integerDisplay, fractionalDisplay
50
51
52 class Gonvert(object):
53
54         # @todo Persist settings
55         # @todo Favorites
56         # @todo Add menus to all windows
57         # @bug Fix the resurrecting window problem
58         # @todo Ctrl+l support
59         # @todo Unit conversion window: focus always on input, arrows switch units
60
61         _DATA_PATHS = [
62                 os.path.dirname(__file__),
63                 os.path.join(os.path.dirname(__file__), "../data"),
64                 os.path.join(os.path.dirname(__file__), "../lib"),
65                 '/usr/share/gonvert',
66                 '/usr/lib/gonvert',
67         ]
68
69         def __init__(self):
70                 self._dataPath = ""
71                 for dataPath in self._DATA_PATHS:
72                         appIconPath = os.path.join(dataPath, "pixmaps", "gonvert.png")
73                         if os.path.isfile(appIconPath):
74                                 self._dataPath = dataPath
75                                 break
76                 else:
77                         raise RuntimeError("UI Descriptor not found!")
78                 self._appIconPath = appIconPath
79                 self._recent = []
80                 self._isFullscreen = False
81
82                 self._jumpWindow = None
83                 self._recentWindow = None
84                 self._catWindow = None
85
86                 self._jumpAction = QtGui.QAction(None)
87                 self._jumpAction.setText("Quick Jump")
88                 self._jumpAction.setStatusTip("Search for a unit and jump straight to it")
89                 self._jumpAction.setToolTip("Search for a unit and jump straight to it")
90                 self._jumpAction.setShortcut(QtGui.QKeySequence("CTRL+j"))
91                 self._jumpAction.triggered.connect(self._on_jump_start)
92
93                 self._recentAction = QtGui.QAction(None)
94                 self._recentAction.setText("Recent Units")
95                 self._recentAction.setStatusTip("View the recent units")
96                 self._recentAction.setToolTip("View the recent units")
97                 self._recentAction.setShortcut(QtGui.QKeySequence("CTRL+r"))
98                 self._recentAction.triggered.connect(self._on_recent_start)
99
100                 self._fullscreenAction = QtGui.QAction(None)
101                 self._fullscreenAction.setText("Toggle Fullscreen")
102                 self._fullscreenAction.setShortcut(QtGui.QKeySequence("CTRL+Enter"))
103                 self._fullscreenAction.triggered.connect(self._on_toggle_fullscreen)
104
105                 self._quitAction = QtGui.QAction(None)
106                 self._quitAction.setText("Quit")
107                 self._quitAction.setShortcut(QtGui.QKeySequence("CTRL+q"))
108                 self._quitAction.triggered.connect(self._on_quit)
109
110                 self.request_category()
111
112         def request_category(self):
113                 if self._catWindow is not None:
114                         self._catWindow.close()
115                         self._catWindow = None
116                 self._catWindow = CategoryWindow(None, self)
117                 return self._catWindow
118
119         def search_units(self):
120                 if self._jumpWindow is not None:
121                         self._jumpWindow.close()
122                         self._jumpWindow = None
123                 self._jumpWindow = QuickJump(None, self)
124                 return self._jumpWindow
125
126         def show_recent(self):
127                 if self._recentWindow is not None:
128                         self._recentWindow.close()
129                         self._recentWindow = None
130                 self._recentWindow = Recent(None, self)
131                 return self._recentWindow
132
133         def add_recent(self, categoryName, unitName):
134                 catUnit = categoryName, unitName
135                 try:
136                         self._recent.remove(catUnit)
137                 except ValueError:
138                         pass # ignore if its not already in the recent history
139                 self._recent.append(catUnit)
140
141         def get_recent_unit(self, categoryName):
142                 for catName, unitName in reversed(self._recent):
143                         if catName == categoryName:
144                                 return unitName
145                 else:
146                         return ""
147
148         def get_recent(self):
149                 return reversed(self._recent)
150
151         @property
152         def appIconPath(self):
153                 return self._appIconPath
154
155         @property
156         def jumpAction(self):
157                 return self._jumpAction
158
159         @property
160         def recentAction(self):
161                 return self._recentAction
162
163         @property
164         def fullscreenAction(self):
165                 return self._fullscreenAction
166
167         @property
168         def quitAction(self):
169                 return self._quitAction
170
171         def _walk_children(self):
172                 if self._catWindow is not None:
173                         yield self._catWindow
174                 if self._jumpWindow is not None:
175                         yield self._jumpWindow
176                 if self._recentWindow is not None:
177                         yield self._recentWindow
178
179         @misc_utils.log_exception(_moduleLogger)
180         def _on_toggle_fullscreen(self, checked = False):
181                 self._isFullscreen = not self._isFullscreen
182                 for window in self._walk_children():
183                         window.setFullscreen(self._isFullscreen)
184
185         @misc_utils.log_exception(_moduleLogger)
186         def _on_jump_start(self, checked = False):
187                 self.search_units()
188
189         @misc_utils.log_exception(_moduleLogger)
190         def _on_recent_start(self, checked = False):
191                 self.show_recent()
192
193         @misc_utils.log_exception(_moduleLogger)
194         def _on_quit(self, checked = False):
195                 for window in self._walk_children():
196                         window.close()
197
198
199 class CategoryWindow(object):
200
201         def __init__(self, parent, app):
202                 self._app = app
203                 self._unitWindow = None
204
205                 self._categories = QtGui.QTreeWidget()
206                 self._categories.setHeaderLabels(["Categories"])
207                 self._categories.itemClicked.connect(self._on_category_clicked)
208                 self._categories.setHeaderHidden(True)
209                 self._categories.setAlternatingRowColors(True)
210                 for catName in unit_data.UNIT_CATEGORIES:
211                         twi = QtGui.QTreeWidgetItem(self._categories)
212                         twi.setText(0, catName)
213
214                 self._layout = QtGui.QVBoxLayout()
215                 self._layout.addWidget(self._categories)
216
217                 centralWidget = QtGui.QWidget()
218                 centralWidget.setLayout(self._layout)
219
220                 self._window = QtGui.QMainWindow(parent)
221                 if parent is not None:
222                         self._window.setWindowModality(QtCore.Qt.WindowModal)
223                 self._window.setWindowTitle("%s - Categories" % constants.__pretty_app_name__)
224                 self._window.setWindowIcon(QtGui.QIcon(self._app.appIconPath))
225                 self._window.setCentralWidget(centralWidget)
226
227                 self._closeWindowAction = QtGui.QAction(None)
228                 self._closeWindowAction.setText("Window")
229                 self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
230                 self._closeWindowAction.triggered.connect(self._on_close_window)
231
232                 fileMenu = self._window.menuBar().addMenu("&File")
233                 fileMenu.addAction(self._closeWindowAction)
234                 fileMenu.addAction(self._app.quitAction)
235
236                 viewMenu = self._window.menuBar().addMenu("&View")
237                 viewMenu.addAction(self._app.fullscreenAction)
238                 viewMenu.addSeparator()
239                 viewMenu.addAction(self._app.jumpAction)
240                 viewMenu.addAction(self._app.recentAction)
241
242                 self._window.show()
243
244         def walk_children(self):
245                 if self._unitWindow is not None:
246                         yield self._unitWindow
247
248         def close(self):
249                 for child in self.walk_children():
250                         child.close()
251                 self._window.close()
252
253         def selectCategory(self, categoryName):
254                 for child in self.walk_children():
255                         child.close()
256                 self._unitWindow = UnitWindow(self._window, categoryName, self._app)
257                 return self._unitWindow
258
259         def setFullscreen(self, isFullscreen):
260                 if isFullscreen:
261                         self._window.showFullScreen()
262                 else:
263                         self._window.showNormal()
264                 for child in self.walk_children():
265                         child.setFullscreen(isFullscreen)
266
267         @misc_utils.log_exception(_moduleLogger)
268         def _on_close_window(self, checked = True):
269                 self.close()
270
271         @misc_utils.log_exception(_moduleLogger)
272         def _on_category_clicked(self, item, columnIndex):
273                 categoryName = unicode(item.text(0))
274                 self.selectCategory(categoryName)
275
276
277 class QuickJump(object):
278
279         MINIMAL_ENTRY = 3
280
281         def __init__(self, parent, app):
282                 self._app = app
283
284                 self._searchLabel = QtGui.QLabel("Search:")
285                 self._searchEntry = QtGui.QLineEdit("")
286                 self._searchEntry.textEdited.connect(self._on_search_edited)
287
288                 self._entryLayout = QtGui.QHBoxLayout()
289                 self._entryLayout.addWidget(self._searchLabel)
290                 self._entryLayout.addWidget(self._searchEntry)
291
292                 self._resultsBox = QtGui.QTreeWidget()
293                 self._resultsBox.setHeaderLabels(["Categories", "Units"])
294                 self._resultsBox.setHeaderHidden(True)
295                 self._resultsBox.setAlternatingRowColors(True)
296                 self._resultsBox.itemClicked.connect(self._on_result_clicked)
297
298                 self._layout = QtGui.QVBoxLayout()
299                 self._layout.addLayout(self._entryLayout)
300                 self._layout.addWidget(self._resultsBox)
301
302                 centralWidget = QtGui.QWidget()
303                 centralWidget.setLayout(self._layout)
304
305                 self._window = QtGui.QMainWindow(parent)
306                 if parent is not None:
307                         self._window.setWindowModality(QtCore.Qt.WindowModal)
308                 self._window.setWindowTitle("%s - Quick Jump" % constants.__pretty_app_name__)
309                 self._window.setWindowIcon(QtGui.QIcon(self._app.appIconPath))
310                 self._window.setCentralWidget(centralWidget)
311
312                 self._closeWindowAction = QtGui.QAction(None)
313                 self._closeWindowAction.setText("Window")
314                 self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
315                 self._closeWindowAction.triggered.connect(self._on_close_window)
316
317                 fileMenu = self._window.menuBar().addMenu("&File")
318                 fileMenu.addAction(self._closeWindowAction)
319                 fileMenu.addAction(self._app.quitAction)
320
321                 viewMenu = self._window.menuBar().addMenu("&View")
322                 viewMenu.addAction(self._app.fullscreenAction)
323
324                 self._window.show()
325
326         def close(self):
327                 self._window.close()
328
329         def setFullscreen(self, isFullscreen):
330                 if isFullscreen:
331                         self._window.showFullScreen()
332                 else:
333                         self._window.showNormal()
334
335         @misc_utils.log_exception(_moduleLogger)
336         def _on_close_window(self, checked = True):
337                 self.close()
338
339         @misc_utils.log_exception(_moduleLogger)
340         def _on_result_clicked(self, item, columnIndex):
341                 categoryName = unicode(item.text(0))
342                 unitName = unicode(item.text(1))
343                 catWindow = self._app.request_category()
344                 unitsWindow = catWindow.selectCategory(categoryName)
345                 unitsWindow.select_unit(unitName)
346                 self.close()
347
348         @misc_utils.log_exception(_moduleLogger)
349         def _on_search_edited(self, *args):
350                 userInput = self._searchEntry.text()
351                 if len(userInput) <  self.MINIMAL_ENTRY:
352                         return
353
354                 self._resultsBox.clear()
355                 lowerInput = str(userInput).lower()
356                 for catIndex, category in enumerate(unit_data.UNIT_CATEGORIES):
357                         units = unit_data.get_units(category)
358                         for unitIndex, unit in enumerate(units):
359                                 loweredUnit = unit.lower()
360                                 if lowerInput in loweredUnit:
361                                         twi = QtGui.QTreeWidgetItem(self._resultsBox)
362                                         twi.setText(0, category)
363                                         twi.setText(1, unit)
364
365
366 class Recent(object):
367
368         def __init__(self, parent, app):
369                 self._app = app
370
371                 self._resultsBox = QtGui.QTreeWidget()
372                 self._resultsBox.setHeaderLabels(["Categories", "Units"])
373                 self._resultsBox.setHeaderHidden(True)
374                 self._resultsBox.setAlternatingRowColors(True)
375                 self._resultsBox.itemClicked.connect(self._on_result_clicked)
376
377                 self._layout = QtGui.QVBoxLayout()
378                 self._layout.addWidget(self._resultsBox)
379
380                 centralWidget = QtGui.QWidget()
381                 centralWidget.setLayout(self._layout)
382
383                 self._window = QtGui.QMainWindow(parent)
384                 if parent is not None:
385                         self._window.setWindowModality(QtCore.Qt.WindowModal)
386                 self._window.setWindowTitle("%s - Recent" % constants.__pretty_app_name__)
387                 self._window.setWindowIcon(QtGui.QIcon(self._app.appIconPath))
388                 self._window.setCentralWidget(centralWidget)
389
390                 for cat, unit in self._app.get_recent():
391                         twi = QtGui.QTreeWidgetItem(self._resultsBox)
392                         twi.setText(0, cat)
393                         twi.setText(1, unit)
394
395                 self._closeWindowAction = QtGui.QAction(None)
396                 self._closeWindowAction.setText("Window")
397                 self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
398                 self._closeWindowAction.triggered.connect(self._on_close_window)
399
400                 fileMenu = self._window.menuBar().addMenu("&File")
401                 fileMenu.addAction(self._closeWindowAction)
402                 fileMenu.addAction(self._app.quitAction)
403
404                 viewMenu = self._window.menuBar().addMenu("&View")
405                 viewMenu.addAction(self._app.fullscreenAction)
406
407                 self._window.show()
408
409         def close(self):
410                 self._window.close()
411
412         def setFullscreen(self, isFullscreen):
413                 if isFullscreen:
414                         self._window.showFullScreen()
415                 else:
416                         self._window.showNormal()
417
418         @misc_utils.log_exception(_moduleLogger)
419         def _on_close_window(self, checked = True):
420                 self.close()
421
422         @misc_utils.log_exception(_moduleLogger)
423         def _on_result_clicked(self, item, columnIndex):
424                 categoryName = unicode(item.text(0))
425                 unitName = unicode(item.text(1))
426                 catWindow = self._app.request_category()
427                 unitsWindow = catWindow.selectCategory(categoryName)
428                 unitsWindow.select_unit(unitName)
429                 self.close()
430
431
432 class UnitData(object):
433
434         HEADERS = ["Name", "Value", "", "Unit"]
435         ALIGNMENT = [QtCore.Qt.AlignLeft, QtCore.Qt.AlignRight, QtCore.Qt.AlignLeft, QtCore.Qt.AlignLeft]
436
437         def __init__(self, name, unit, description, conversion):
438                 self._name = name
439                 self._unit = unit
440                 self._description = description
441                 self._conversion = conversion
442
443                 self._value = 0.0
444                 self._integerDisplay, self._fractionalDisplay = split_number(self._value)
445
446         @property
447         def name(self):
448                 return self._name
449
450         @property
451         def value(self):
452                 return self._value
453
454         def update_value(self, newValue):
455                 self._value = newValue
456                 self._integerDisplay, self._fractionalDisplay = split_number(newValue)
457
458         @property
459         def unit(self):
460                 return self._unit
461
462         @property
463         def conversion(self):
464                 return self._conversion
465
466         def data(self, column):
467                 try:
468                         return [self._name, self._integerDisplay, self._fractionalDisplay, self._unit][column]
469                 except IndexError:
470                         return None
471
472
473 class UnitModel(QtCore.QAbstractItemModel):
474
475         def __init__(self, categoryName, parent=None):
476                 super(UnitModel, self).__init__(parent)
477                 self._categoryName = categoryName
478                 self._unitData = unit_data.UNIT_DESCRIPTIONS[self._categoryName]
479
480                 self._children = []
481                 for key in unit_data.get_units(self._categoryName):
482                         conversion, unit, description = self._unitData[key]
483                         self._children.append(UnitData(key, unit, description, conversion))
484                 self._sortSettings = None
485
486         @misc_utils.log_exception(_moduleLogger)
487         def columnCount(self, parent):
488                 if parent.isValid():
489                         return 0
490                 else:
491                         return len(UnitData.HEADERS)
492
493         @misc_utils.log_exception(_moduleLogger)
494         def data(self, index, role):
495                 if not index.isValid():
496                         return None
497                 elif role == QtCore.Qt.TextAlignmentRole:
498                         return UnitData.ALIGNMENT[index.column()]
499                 elif role != QtCore.Qt.DisplayRole:
500                         return None
501
502                 item = index.internalPointer()
503                 if isinstance(item, UnitData):
504                         return item.data(index.column())
505                 elif item is UnitData.HEADERS:
506                         return item[index.column()]
507
508         @misc_utils.log_exception(_moduleLogger)
509         def sort(self, column, order = QtCore.Qt.AscendingOrder):
510                 self._sortSettings = column, order
511                 isReverse = order == QtCore.Qt.AscendingOrder
512                 if column == 0:
513                         key_func = lambda item: item.name
514                 elif column in [1, 2]:
515                         key_func = lambda item: item.value
516                 elif column == 3:
517                         key_func = lambda item: item.unit
518                 self._children.sort(key=key_func, reverse = isReverse)
519
520                 self._all_changed()
521
522         @misc_utils.log_exception(_moduleLogger)
523         def flags(self, index):
524                 if not index.isValid():
525                         return QtCore.Qt.NoItemFlags
526
527                 return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
528
529         @misc_utils.log_exception(_moduleLogger)
530         def headerData(self, section, orientation, role):
531                 if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
532                         return UnitData.HEADERS[section]
533
534                 return None
535
536         @misc_utils.log_exception(_moduleLogger)
537         def index(self, row, column, parent):
538                 if not self.hasIndex(row, column, parent):
539                         return QtCore.QModelIndex()
540
541                 if parent.isValid():
542                         return QtCore.QModelIndex()
543
544                 parentItem = UnitData.HEADERS
545                 childItem = self._children[row]
546                 if childItem:
547                         return self.createIndex(row, column, childItem)
548                 else:
549                         return QtCore.QModelIndex()
550
551         @misc_utils.log_exception(_moduleLogger)
552         def parent(self, index):
553                 if not index.isValid():
554                         return QtCore.QModelIndex()
555
556                 childItem = index.internalPointer()
557                 if isinstance(childItem, UnitData):
558                         return QtCore.QModelIndex()
559                 elif childItem is UnitData.HEADERS:
560                         return None
561
562         @misc_utils.log_exception(_moduleLogger)
563         def rowCount(self, parent):
564                 if 0 < parent.column():
565                         return 0
566
567                 if not parent.isValid():
568                         return len(self._children)
569                 else:
570                         return len(self._children)
571
572         def get_unit(self, index):
573                 return self._children[index]
574
575         def index_unit(self, unitName):
576                 for i, child in enumerate(self._children):
577                         if child.name == unitName:
578                                 return i
579                 else:
580                         raise RuntimeError("Unit not found")
581
582         def update_values(self, fromIndex, userInput):
583                 value = self._sanitize_value(userInput)
584                 func, arg = self._children[fromIndex].conversion
585                 base = func.to_base(value, arg)
586                 for i, child in enumerate(self._children):
587                         if i == fromIndex:
588                                 continue
589                         func, arg = child.conversion
590                         newValue = func.from_base(base, arg)
591                         child.update_value(newValue)
592
593                 if self._sortSettings is not None:
594                         self.sort(*self._sortSettings)
595                 self._all_changed()
596
597         def _all_changed(self):
598                 topLeft = self.createIndex(0, 0, self._children[0])
599                 bottomRight = self.createIndex(len(self._children)-1, len(UnitData.HEADERS)-1, self._children[-1])
600                 self.dataChanged.emit(topLeft, bottomRight)
601
602         def _sanitize_value(self, userEntry):
603                 if self._categoryName == "Computer Numbers":
604                         if userEntry == '':
605                                 value = '0'
606                         else:
607                                 value = userEntry
608                 else:
609                         if userEntry == '':
610                                 value = 0.0
611                         else:
612                                 value = float(userEntry)
613                 return value
614
615
616 class UnitWindow(object):
617
618         def __init__(self, parent, category, app):
619                 self._app = app
620                 self._categoryName = category
621                 self._selectedIndex = 0
622
623                 self._selectedUnitName = QtGui.QLabel()
624                 self._selectedUnitValue = QtGui.QLineEdit()
625                 self._selectedUnitValue.textEdited.connect(self._on_value_edited)
626                 self._selectedUnitSymbol = QtGui.QLabel()
627
628                 self._selectedUnitLayout = QtGui.QHBoxLayout()
629                 self._selectedUnitLayout.addWidget(self._selectedUnitName)
630                 self._selectedUnitLayout.addWidget(self._selectedUnitValue)
631                 self._selectedUnitLayout.addWidget(self._selectedUnitSymbol)
632
633                 self._unitsModel = UnitModel(self._categoryName)
634                 self._unitsView = QtGui.QTreeView()
635                 self._unitsView.setModel(self._unitsModel)
636                 self._unitsView.clicked.connect(self._on_unit_clicked)
637                 self._unitsView.setUniformRowHeights(True)
638                 self._unitsView.header().setSortIndicatorShown(True)
639                 self._unitsView.header().setClickable(True)
640                 self._unitsView.setSortingEnabled(True)
641                 self._unitsView.setAlternatingRowColors(True)
642                 if True:
643                         self._unitsView.setHeaderHidden(True)
644
645                 self._layout = QtGui.QVBoxLayout()
646                 self._layout.addLayout(self._selectedUnitLayout)
647                 self._layout.addWidget(self._unitsView)
648
649                 centralWidget = QtGui.QWidget()
650                 centralWidget.setLayout(self._layout)
651
652                 self._window = QtGui.QMainWindow(parent)
653                 if parent is not None:
654                         self._window.setWindowModality(QtCore.Qt.WindowModal)
655                 self._window.setWindowTitle("%s - %s" % (constants.__pretty_app_name__, category))
656                 self._window.setWindowIcon(QtGui.QIcon(app.appIconPath))
657                 self._window.setCentralWidget(centralWidget)
658
659                 defaultUnitName = self._app.get_recent_unit(self._categoryName)
660                 if defaultUnitName:
661                         self.select_unit(defaultUnitName)
662                 else:
663                         self._select_unit(0)
664                 self._unitsModel.sort(1)
665
666                 self._sortActionGroup = QtGui.QActionGroup(None)
667                 self._sortByNameAction = QtGui.QAction(self._sortActionGroup)
668                 self._sortByNameAction.setText("Sort By Name")
669                 self._sortByNameAction.setStatusTip("Sort the units by name")
670                 self._sortByNameAction.setToolTip("Sort the units by name")
671                 self._sortByValueAction = QtGui.QAction(self._sortActionGroup)
672                 self._sortByValueAction.setText("Sort By Value")
673                 self._sortByValueAction.setStatusTip("Sort the units by value")
674                 self._sortByValueAction.setToolTip("Sort the units by value")
675                 self._sortByUnitAction = QtGui.QAction(self._sortActionGroup)
676                 self._sortByUnitAction.setText("Sort By Unit")
677                 self._sortByUnitAction.setStatusTip("Sort the units by unit")
678                 self._sortByUnitAction.setToolTip("Sort the units by unit")
679
680                 self._sortByValueAction.setChecked(True)
681
682                 self._closeWindowAction = QtGui.QAction(None)
683                 self._closeWindowAction.setText("Close Window")
684                 self._closeWindowAction.setShortcut(QtGui.QKeySequence("CTRL+w"))
685                 self._closeWindowAction.triggered.connect(self._on_close_window)
686
687                 fileMenu = self._window.menuBar().addMenu("&File")
688                 fileMenu.addAction(self._closeWindowAction)
689                 fileMenu.addAction(self._app.quitAction)
690
691                 viewMenu = self._window.menuBar().addMenu("&View")
692                 viewMenu.addAction(self._app.fullscreenAction)
693                 viewMenu.addSeparator()
694                 viewMenu.addAction(self._app.jumpAction)
695                 viewMenu.addAction(self._app.recentAction)
696                 viewMenu.addSeparator()
697                 viewMenu.addAction(self._sortByNameAction)
698                 viewMenu.addAction(self._sortByValueAction)
699                 viewMenu.addAction(self._sortByUnitAction)
700
701                 self._sortByNameAction.triggered.connect(self._on_sort_by_name)
702                 self._sortByValueAction.triggered.connect(self._on_sort_by_value)
703                 self._sortByUnitAction.triggered.connect(self._on_sort_by_unit)
704
705                 self._window.show()
706
707         def close(self):
708                 self._window.close()
709
710         def setFullscreen(self, isFullscreen):
711                 if isFullscreen:
712                         self._window.showFullScreen()
713                 else:
714                         self._window.showNormal()
715
716         def select_unit(self, unitName):
717                 index = self._unitsModel.index_unit(unitName)
718                 self._select_unit(index)
719
720         @misc_utils.log_exception(_moduleLogger)
721         def _on_close_window(self, checked = True):
722                 self.close()
723
724         @misc_utils.log_exception(_moduleLogger)
725         def _on_sort_by_name(self, checked = False):
726                 self._unitsModel.sort(0, QtCore.Qt.DescendingOrder)
727
728         @misc_utils.log_exception(_moduleLogger)
729         def _on_sort_by_value(self, checked = False):
730                 self._unitsModel.sort(1)
731
732         @misc_utils.log_exception(_moduleLogger)
733         def _on_sort_by_unit(self, checked = False):
734                 self._unitsModel.sort(3, QtCore.Qt.DescendingOrder)
735
736         @misc_utils.log_exception(_moduleLogger)
737         def _on_unit_clicked(self, index):
738                 self._select_unit(index.row())
739
740         @misc_utils.log_exception(_moduleLogger)
741         def _on_value_edited(self, *args):
742                 userInput = self._selectedUnitValue.text()
743                 self._unitsModel.update_values(self._selectedIndex, str(userInput))
744
745         def _select_unit(self, index):
746                 unit = self._unitsModel.get_unit(index)
747                 self._selectedUnitName.setText(unit.name)
748                 self._selectedUnitValue.setText(str(unit.value))
749                 self._selectedUnitSymbol.setText(unit.unit)
750
751                 self._selectedIndex = index
752                 qindex = self._unitsModel.createIndex(index, 0, self._unitsModel.get_unit(index))
753                 self._unitsView.scrollTo(qindex)
754                 self._app.add_recent(self._categoryName, self._unitsModel.get_unit(index).name)
755
756
757 def run_gonvert():
758         app = QtGui.QApplication([])
759         handle = Gonvert()
760         return app.exec_()
761
762
763 if __name__ == "__main__":
764         logging.basicConfig(level = logging.DEBUG)
765         try:
766                 os.makedirs(constants._data_path_)
767         except OSError, e:
768                 if e.errno != 17:
769                         raise
770
771         val = run_gonvert()
772         sys.exit(val)