mirror of
https://github.com/arsenetar/dupeguru.git
synced 2025-03-10 05:34:36 +00:00
Adapted the Qt codebase to the addition of core.gui.result_tree and core.gui.stats_label.
This commit is contained in:
parent
c3a972d39b
commit
cab6d924aa
@ -14,9 +14,16 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
{
|
{
|
||||||
self = [super initWithNibName:@"DetailsPanel" pyClassName:@"PyDetailsPanel" pyParent:aPy];
|
self = [super initWithNibName:@"DetailsPanel" pyClassName:@"PyDetailsPanel" pyParent:aPy];
|
||||||
[self window]; //So the detailsTable is initialized.
|
[self window]; //So the detailsTable is initialized.
|
||||||
|
[self connect];
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
[self disconnect];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
- (PyDetailsPanel *)py
|
- (PyDetailsPanel *)py
|
||||||
{
|
{
|
||||||
return (PyDetailsPanel *)py;
|
return (PyDetailsPanel *)py;
|
||||||
|
@ -13,9 +13,16 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
{
|
{
|
||||||
self = [super initWithPyClassName:@"PyDirectoryOutline" pyParent:aPyParent view:aOutlineView];
|
self = [super initWithPyClassName:@"PyDirectoryOutline" pyParent:aPyParent view:aOutlineView];
|
||||||
[outlineView registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
|
[outlineView registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
|
||||||
|
[self connect];
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
[self disconnect];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
- (PyDirectoryOutline *)py
|
- (PyDirectoryOutline *)py
|
||||||
{
|
{
|
||||||
return (PyDirectoryOutline *)py;
|
return (PyDirectoryOutline *)py;
|
||||||
|
@ -16,11 +16,13 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
{
|
{
|
||||||
self = [super initWithPyClassName:@"PyResultOutline" pyParent:aPyParent view:aOutlineView];
|
self = [super initWithPyClassName:@"PyResultOutline" pyParent:aPyParent view:aOutlineView];
|
||||||
_rootChildrenCounts = nil;
|
_rootChildrenCounts = nil;
|
||||||
|
[self connect];
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
|
[self disconnect];
|
||||||
[_deltaColumns release];
|
[_deltaColumns release];
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
}
|
}
|
||||||
|
@ -14,11 +14,13 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
{
|
{
|
||||||
self = [super initWithPyClassName:@"PyStatsLabel" pyParent:aPyParent];
|
self = [super initWithPyClassName:@"PyStatsLabel" pyParent:aPyParent];
|
||||||
labelView = [aLabelView retain];
|
labelView = [aLabelView retain];
|
||||||
|
[self connect];
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
|
[self disconnect];
|
||||||
[labelView release];
|
[labelView release];
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,11 @@ class DetailsPanel(GUIObject):
|
|||||||
def __init__(self, view, app):
|
def __init__(self, view, app):
|
||||||
GUIObject.__init__(self, view, app)
|
GUIObject.__init__(self, view, app)
|
||||||
self._table = []
|
self._table = []
|
||||||
|
|
||||||
|
def connect(self):
|
||||||
|
GUIObject.connect(self)
|
||||||
self._refresh()
|
self._refresh()
|
||||||
self.connect()
|
self.view.refresh()
|
||||||
|
|
||||||
#--- Private
|
#--- Private
|
||||||
def _refresh(self):
|
def _refresh(self):
|
||||||
|
@ -53,7 +53,9 @@ class DirectoryTree(GUIObject, Tree):
|
|||||||
def __init__(self, view, app):
|
def __init__(self, view, app):
|
||||||
GUIObject.__init__(self, view, app)
|
GUIObject.__init__(self, view, app)
|
||||||
Tree.__init__(self)
|
Tree.__init__(self)
|
||||||
self.connect()
|
|
||||||
|
def connect(self):
|
||||||
|
GUIObject.connect(self)
|
||||||
self._refresh()
|
self._refresh()
|
||||||
self.view.refresh()
|
self.view.refresh()
|
||||||
|
|
||||||
|
@ -54,11 +54,13 @@ class ResultTree(GUIObject, Tree):
|
|||||||
self._power_marker = False
|
self._power_marker = False
|
||||||
self._delta_values = False
|
self._delta_values = False
|
||||||
self._sort_descriptors = (0, True)
|
self._sort_descriptors = (0, True)
|
||||||
self.connect()
|
|
||||||
|
#--- Override
|
||||||
|
def connect(self):
|
||||||
|
GUIObject.connect(self)
|
||||||
self._refresh()
|
self._refresh()
|
||||||
self.view.refresh()
|
self.view.refresh()
|
||||||
|
|
||||||
#--- Override
|
|
||||||
def _select_nodes(self, nodes):
|
def _select_nodes(self, nodes):
|
||||||
Tree._select_nodes(self, nodes)
|
Tree._select_nodes(self, nodes)
|
||||||
self.app._select_dupes(map(attrgetter('_dupe'), nodes))
|
self.app._select_dupes(map(attrgetter('_dupe'), nodes))
|
||||||
|
@ -10,9 +10,8 @@
|
|||||||
from .base import GUIObject
|
from .base import GUIObject
|
||||||
|
|
||||||
class StatsLabel(GUIObject):
|
class StatsLabel(GUIObject):
|
||||||
def __init__(self, view, app):
|
def connect(self):
|
||||||
GUIObject.__init__(self, view, app)
|
GUIObject.connect(self)
|
||||||
self.connect()
|
|
||||||
self.view.refresh()
|
self.view.refresh()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -12,13 +12,12 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import os.path as op
|
import os.path as op
|
||||||
|
|
||||||
from PyQt4.QtCore import Qt, QTimer, QObject, QCoreApplication, QUrl, SIGNAL
|
from PyQt4.QtCore import QTimer, QObject, QCoreApplication, QUrl, SIGNAL
|
||||||
from PyQt4.QtGui import QProgressDialog, QDesktopServices, QFileDialog, QDialog, QMessageBox
|
from PyQt4.QtGui import QDesktopServices, QFileDialog, QDialog, QMessageBox
|
||||||
|
|
||||||
from hsutil import job
|
from hsutil import job
|
||||||
from hsutil.reg import RegistrationRequired
|
from hsutil.reg import RegistrationRequired
|
||||||
|
|
||||||
from core import fs
|
|
||||||
from core.app import DupeGuru as DupeGuruBase, JOB_SCAN, JOB_LOAD, JOB_MOVE, JOB_COPY, JOB_DELETE
|
from core.app import DupeGuru as DupeGuruBase, JOB_SCAN, JOB_LOAD, JOB_MOVE, JOB_COPY, JOB_DELETE
|
||||||
|
|
||||||
from qtlib.about_box import AboutBox
|
from qtlib.about_box import AboutBox
|
||||||
@ -148,10 +147,6 @@ class DupeGuru(DupeGuruBase, QObject):
|
|||||||
if self.main_window._confirm(title, msg):
|
if self.main_window._confirm(title, msg):
|
||||||
DupeGuruBase.add_selected_to_ignore_list(self)
|
DupeGuruBase.add_selected_to_ignore_list(self)
|
||||||
|
|
||||||
def apply_filter(self, filter):
|
|
||||||
DupeGuruBase.apply_filter(self, filter)
|
|
||||||
self.emit(SIGNAL('resultsChanged()'))
|
|
||||||
|
|
||||||
@demo_method
|
@demo_method
|
||||||
def copy_or_move_marked(self, copy):
|
def copy_or_move_marked(self, copy):
|
||||||
opname = 'copy' if copy else 'move'
|
opname = 'copy' if copy else 'move'
|
||||||
@ -165,14 +160,6 @@ class DupeGuru(DupeGuruBase, QObject):
|
|||||||
|
|
||||||
delete_marked = demo_method(DupeGuruBase.delete_marked)
|
delete_marked = demo_method(DupeGuruBase.delete_marked)
|
||||||
|
|
||||||
def make_selected_reference(self):
|
|
||||||
DupeGuruBase.make_selected_reference(self)
|
|
||||||
self.emit(SIGNAL('resultsChanged()'))
|
|
||||||
|
|
||||||
def remove_duplicates(self, duplicates):
|
|
||||||
DupeGuruBase.remove_duplicates(self, duplicates)
|
|
||||||
self.emit(SIGNAL('resultsChanged()'))
|
|
||||||
|
|
||||||
def remove_selected(self):
|
def remove_selected(self):
|
||||||
dupes = self.without_ref(self.selected_dupes)
|
dupes = self.without_ref(self.selected_dupes)
|
||||||
if not dupes:
|
if not dupes:
|
||||||
@ -186,37 +173,10 @@ class DupeGuru(DupeGuruBase, QObject):
|
|||||||
def askForRegCode(self):
|
def askForRegCode(self):
|
||||||
self.reg.ask_for_code()
|
self.reg.ask_for_code()
|
||||||
|
|
||||||
def mark_all(self):
|
|
||||||
self.results.mark_all()
|
|
||||||
self.emit(SIGNAL('dupeMarkingChanged()'))
|
|
||||||
|
|
||||||
def mark_invert(self):
|
|
||||||
self.results.mark_invert()
|
|
||||||
self.emit(SIGNAL('dupeMarkingChanged()'))
|
|
||||||
|
|
||||||
def mark_none(self):
|
|
||||||
self.results.mark_none()
|
|
||||||
self.emit(SIGNAL('dupeMarkingChanged()'))
|
|
||||||
|
|
||||||
def openDebugLog(self):
|
def openDebugLog(self):
|
||||||
debugLogPath = op.join(self.appdata, 'debug.log')
|
debugLogPath = op.join(self.appdata, 'debug.log')
|
||||||
self._open_path(debugLogPath)
|
self._open_path(debugLogPath)
|
||||||
|
|
||||||
def remove_marked_duplicates(self):
|
|
||||||
marked = [d for d in self.results.dupes if self.results.is_marked(d)]
|
|
||||||
self.remove_duplicates(marked)
|
|
||||||
|
|
||||||
def rename_dupe(self, dupe, newname):
|
|
||||||
try:
|
|
||||||
dupe.rename(newname)
|
|
||||||
return True
|
|
||||||
except (IndexError, fs.FSError) as e:
|
|
||||||
logging.warning("dupeGuru Warning: %s" % unicode(e))
|
|
||||||
return False
|
|
||||||
|
|
||||||
def select_dupes(self, dupes):
|
|
||||||
self._select_dupes(dupes)
|
|
||||||
|
|
||||||
def show_about_box(self):
|
def show_about_box(self):
|
||||||
self.about_box.show()
|
self.about_box.show()
|
||||||
|
|
||||||
@ -238,11 +198,6 @@ class DupeGuru(DupeGuruBase, QObject):
|
|||||||
self.prefs.save()
|
self.prefs.save()
|
||||||
self._update_options()
|
self._update_options()
|
||||||
|
|
||||||
def toggle_marking_for_dupes(self, dupes):
|
|
||||||
for dupe in dupes:
|
|
||||||
self.results.mark_toggle(dupe)
|
|
||||||
self.emit(SIGNAL('dupeMarkingChanged()'))
|
|
||||||
|
|
||||||
#--- Events
|
#--- Events
|
||||||
def application_will_terminate(self):
|
def application_will_terminate(self):
|
||||||
self.save()
|
self.save()
|
||||||
@ -253,7 +208,7 @@ class DupeGuru(DupeGuruBase, QObject):
|
|||||||
self.reg.show_nag()
|
self.reg.show_nag()
|
||||||
|
|
||||||
def job_finished(self, jobid):
|
def job_finished(self, jobid):
|
||||||
self.emit(SIGNAL('resultsChanged()'))
|
self._job_completed(jobid)
|
||||||
if jobid in (JOB_MOVE, JOB_COPY, JOB_DELETE) and self.last_op_error_count > 0:
|
if jobid in (JOB_MOVE, JOB_COPY, JOB_DELETE) and self.last_op_error_count > 0:
|
||||||
msg = "{0} files could not be processed.".format(self.results.mark_count)
|
msg = "{0} files could not be processed.".format(self.results.mark_count)
|
||||||
QMessageBox.warning(self.main_window, 'Warning', msg)
|
QMessageBox.warning(self.main_window, 'Warning', msg)
|
||||||
|
@ -23,6 +23,7 @@ class DetailsDialog(QDialog):
|
|||||||
self.tableModel = DetailsModel(self.model)
|
self.tableModel = DetailsModel(self.model)
|
||||||
# tableView is defined in subclasses
|
# tableView is defined in subclasses
|
||||||
self.tableView.setModel(self.tableModel)
|
self.tableView.setModel(self.tableModel)
|
||||||
|
self.model.connect()
|
||||||
|
|
||||||
def _setupUi(self): # Virtual
|
def _setupUi(self): # Virtual
|
||||||
pass
|
pass
|
||||||
|
@ -64,6 +64,7 @@ class DirectoriesModel(TreeModel):
|
|||||||
def __init__(self, app):
|
def __init__(self, app):
|
||||||
TreeModel.__init__(self)
|
TreeModel.__init__(self)
|
||||||
self.model = DirectoryTree(self, app)
|
self.model = DirectoryTree(self, app)
|
||||||
|
self.model.connect()
|
||||||
|
|
||||||
def _createNode(self, ref, row):
|
def _createNode(self, ref, row):
|
||||||
return RefNode(self, None, ref, row)
|
return RefNode(self, None, ref, row)
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
from PyQt4.QtCore import Qt, QCoreApplication, QProcess, SIGNAL, QUrl
|
from PyQt4.QtCore import Qt, QCoreApplication, QProcess, SIGNAL, QUrl
|
||||||
from PyQt4.QtGui import (QMainWindow, QMenu, QPixmap, QIcon, QToolButton, QLabel, QHeaderView,
|
from PyQt4.QtGui import (QMainWindow, QMenu, QPixmap, QIcon, QToolButton, QLabel, QHeaderView,
|
||||||
QMessageBox, QInputDialog, QLineEdit, QItemSelectionModel, QDesktopServices)
|
QMessageBox, QInputDialog, QLineEdit, QDesktopServices)
|
||||||
|
|
||||||
from hsutil.misc import nonone
|
from hsutil.misc import nonone
|
||||||
|
|
||||||
@ -16,7 +16,8 @@ from core.app import NoScannableFileError, AllFilesAreRefError
|
|||||||
|
|
||||||
import dg_rc
|
import dg_rc
|
||||||
from main_window_ui import Ui_MainWindow
|
from main_window_ui import Ui_MainWindow
|
||||||
from results_model import ResultsDelegate, ResultsModel
|
from results_model import ResultsModel
|
||||||
|
from stats_label import StatsLabel
|
||||||
|
|
||||||
class MainWindow(QMainWindow, Ui_MainWindow):
|
class MainWindow(QMainWindow, Ui_MainWindow):
|
||||||
def __init__(self, app):
|
def __init__(self, app):
|
||||||
@ -24,23 +25,16 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||||||
self.app = app
|
self.app = app
|
||||||
self._last_filter = None
|
self._last_filter = None
|
||||||
self._setupUi()
|
self._setupUi()
|
||||||
self.resultsDelegate = ResultsDelegate()
|
self.resultsModel = ResultsModel(self.app, self.resultsView)
|
||||||
self.resultsModel = ResultsModel(self.app)
|
self.stats = StatsLabel(app, self.statusLabel)
|
||||||
self.resultsView.setModel(self.resultsModel)
|
|
||||||
self.resultsView.setItemDelegate(self.resultsDelegate)
|
|
||||||
self._load_columns()
|
self._load_columns()
|
||||||
self._update_column_actions_status()
|
self._update_column_actions_status()
|
||||||
self.resultsView.expandAll()
|
|
||||||
self._update_status_line()
|
|
||||||
|
|
||||||
self.connect(self.app, SIGNAL('resultsChanged()'), self.resultsChanged)
|
|
||||||
self.connect(self.app, SIGNAL('dupeMarkingChanged()'), self.dupeMarkingChanged)
|
|
||||||
self.connect(self.actionQuit, SIGNAL('triggered()'), QCoreApplication.instance().quit)
|
self.connect(self.actionQuit, SIGNAL('triggered()'), QCoreApplication.instance().quit)
|
||||||
self.connect(self.resultsView.selectionModel(), SIGNAL('selectionChanged(QItemSelection,QItemSelection)'), self.selectionChanged)
|
|
||||||
self.connect(self.menuColumns, SIGNAL('triggered(QAction*)'), self.columnToggled)
|
self.connect(self.menuColumns, SIGNAL('triggered(QAction*)'), self.columnToggled)
|
||||||
self.connect(QCoreApplication.instance(), SIGNAL('aboutToQuit()'), self.application_will_terminate)
|
self.connect(QCoreApplication.instance(), SIGNAL('aboutToQuit()'), self.application_will_terminate)
|
||||||
self.connect(self.resultsModel, SIGNAL('modelReset()'), self.resultsReset)
|
|
||||||
self.connect(self.resultsView, SIGNAL('doubleClicked()'), self.resultsDoubleClicked)
|
self.connect(self.resultsView, SIGNAL('doubleClicked()'), self.resultsDoubleClicked)
|
||||||
|
self.connect(self.resultsView, SIGNAL('spacePressed()'), self.resultsSpacePressed)
|
||||||
|
|
||||||
def _setupUi(self):
|
def _setupUi(self):
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
@ -108,11 +102,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||||||
h.setSectionHidden(index, not visible)
|
h.setSectionHidden(index, not visible)
|
||||||
h.setResizeMode(0, QHeaderView.Stretch)
|
h.setResizeMode(0, QHeaderView.Stretch)
|
||||||
|
|
||||||
def _redraw_results(self):
|
|
||||||
# HACK. this is the only way I found to update the widget without reseting everything
|
|
||||||
self.resultsView.scroll(0, 1)
|
|
||||||
self.resultsView.scroll(0, -1)
|
|
||||||
|
|
||||||
def _save_columns(self):
|
def _save_columns(self):
|
||||||
h = self.resultsView.header()
|
h = self.resultsView.header()
|
||||||
widths = []
|
widths = []
|
||||||
@ -131,9 +120,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||||||
colid = action.column_index
|
colid = action.column_index
|
||||||
action.setChecked(not h.isSectionHidden(colid))
|
action.setChecked(not h.isSectionHidden(colid))
|
||||||
|
|
||||||
def _update_status_line(self):
|
|
||||||
self.statusLabel.setText(self.app.stat_line)
|
|
||||||
|
|
||||||
#--- Actions
|
#--- Actions
|
||||||
def aboutTriggered(self):
|
def aboutTriggered(self):
|
||||||
self.app.show_about_box()
|
self.app.show_about_box()
|
||||||
@ -185,8 +171,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||||||
self.app.delete_marked()
|
self.app.delete_marked()
|
||||||
|
|
||||||
def deltaTriggered(self):
|
def deltaTriggered(self):
|
||||||
self.resultsModel.delta = self.actionDelta.isChecked()
|
self.resultsModel.delta_values = self.actionDelta.isChecked()
|
||||||
self._redraw_results()
|
|
||||||
|
|
||||||
def detailsTriggered(self):
|
def detailsTriggered(self):
|
||||||
self.app.show_details()
|
self.app.show_details()
|
||||||
@ -217,8 +202,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||||||
self.app.mark_none()
|
self.app.mark_none()
|
||||||
|
|
||||||
def markSelectedTriggered(self):
|
def markSelectedTriggered(self):
|
||||||
dupes = self.resultsView.selectedDupes()
|
self.app.toggle_selected_mark_state()
|
||||||
self.app.toggle_marking_for_dupes(dupes)
|
|
||||||
|
|
||||||
def moveTriggered(self):
|
def moveTriggered(self):
|
||||||
self.app.copy_or_move_marked(False)
|
self.app.copy_or_move_marked(False)
|
||||||
@ -245,7 +229,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||||||
title = "Remove duplicates"
|
title = "Remove duplicates"
|
||||||
msg = "You are about to remove {0} files from results. Continue?".format(count)
|
msg = "You are about to remove {0} files from results. Continue?".format(count)
|
||||||
if self._confirm(title, msg):
|
if self._confirm(title, msg):
|
||||||
self.app.remove_marked_duplicates()
|
self.app.remove_marked()
|
||||||
|
|
||||||
def removeSelectedTriggered(self):
|
def removeSelectedTriggered(self):
|
||||||
self.app.remove_selected()
|
self.app.remove_selected()
|
||||||
@ -292,25 +276,9 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||||||
def contextMenuEvent(self, event):
|
def contextMenuEvent(self, event):
|
||||||
self.actionActions.menu().exec_(event.globalPos())
|
self.actionActions.menu().exec_(event.globalPos())
|
||||||
|
|
||||||
def dupeMarkingChanged(self):
|
|
||||||
self._redraw_results()
|
|
||||||
self._update_status_line()
|
|
||||||
|
|
||||||
def resultsChanged(self):
|
|
||||||
self.resultsView.model().reset()
|
|
||||||
|
|
||||||
def resultsDoubleClicked(self):
|
def resultsDoubleClicked(self):
|
||||||
self.app.open_selected()
|
self.app.open_selected()
|
||||||
|
|
||||||
def resultsReset(self):
|
def resultsSpacePressed(self):
|
||||||
self.resultsView.expandAll()
|
self.app.toggle_selected_mark_state()
|
||||||
if self.app.selected_dupes:
|
|
||||||
[modelIndex] = self.resultsModel.indexesForDupes(self.app.selected_dupes[:1])
|
|
||||||
if modelIndex.isValid():
|
|
||||||
flags = QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows
|
|
||||||
self.resultsView.selectionModel().setCurrentIndex(modelIndex, flags)
|
|
||||||
self._update_status_line()
|
|
||||||
|
|
||||||
def selectionChanged(self, selected, deselected):
|
|
||||||
self.app.select_dupes(self.resultsView.selectedDupes())
|
|
||||||
|
|
||||||
|
@ -6,74 +6,62 @@
|
|||||||
# which should be included with this package. The terms are also available at
|
# which should be included with this package. The terms are also available at
|
||||||
# http://www.hardcoded.net/licenses/hs_license
|
# http://www.hardcoded.net/licenses/hs_license
|
||||||
|
|
||||||
from PyQt4.QtCore import SIGNAL, Qt, QAbstractItemModel, QModelIndex, QRect
|
from PyQt4.QtCore import SIGNAL, Qt
|
||||||
from PyQt4.QtGui import QBrush, QStyledItemDelegate, QFont, QTreeView, QColor
|
from PyQt4.QtGui import (QBrush, QStyledItemDelegate, QFont, QTreeView, QColor, QItemSelectionModel,
|
||||||
|
QItemSelection)
|
||||||
|
|
||||||
from qtlib.tree_model import TreeNode, TreeModel
|
from qtlib.tree_model import TreeModel, RefNode
|
||||||
|
|
||||||
class ResultNode(TreeNode):
|
|
||||||
def __init__(self, model, parent, row, dupe, group):
|
|
||||||
TreeNode.__init__(self, model, parent, row)
|
|
||||||
self.dupe = dupe
|
|
||||||
self.group = group
|
|
||||||
self._normalData = None
|
|
||||||
self._deltaData = None
|
|
||||||
|
|
||||||
def _createNode(self, ref, row):
|
|
||||||
return ResultNode(self.model, self, row, ref, self.group)
|
|
||||||
|
|
||||||
def _getChildren(self):
|
|
||||||
return self.group.dupes if self.dupe is self.group.ref else []
|
|
||||||
|
|
||||||
def invalidate(self):
|
|
||||||
self._normalData = None
|
|
||||||
self._deltaData = None
|
|
||||||
TreeNode.invalidate(self)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def normalData(self):
|
|
||||||
if self._normalData is None:
|
|
||||||
self._normalData = self.model._app._get_display_info(self.dupe, self.group, delta=False)
|
|
||||||
return self._normalData
|
|
||||||
|
|
||||||
@property
|
|
||||||
def deltaData(self):
|
|
||||||
if self._deltaData is None:
|
|
||||||
self._deltaData = self.model._app._get_display_info(self.dupe, self.group, delta=True)
|
|
||||||
return self._deltaData
|
|
||||||
|
|
||||||
|
from core.gui.result_tree import ResultTree as ResultTreeModel
|
||||||
|
|
||||||
class ResultsDelegate(QStyledItemDelegate):
|
class ResultsDelegate(QStyledItemDelegate):
|
||||||
def initStyleOption(self, option, index):
|
def initStyleOption(self, option, index):
|
||||||
QStyledItemDelegate.initStyleOption(self, option, index)
|
QStyledItemDelegate.initStyleOption(self, option, index)
|
||||||
node = index.internalPointer()
|
node = index.internalPointer()
|
||||||
if node.group.ref is node.dupe:
|
ref = node.ref
|
||||||
|
if ref._group.ref is ref._dupe:
|
||||||
newfont = QFont(option.font)
|
newfont = QFont(option.font)
|
||||||
newfont.setBold(True)
|
newfont.setBold(True)
|
||||||
option.font = newfont
|
option.font = newfont
|
||||||
|
|
||||||
|
|
||||||
class ResultsModel(TreeModel):
|
class ResultsModel(TreeModel):
|
||||||
def __init__(self, app):
|
def __init__(self, app, view):
|
||||||
|
TreeModel.__init__(self)
|
||||||
|
self.view = view
|
||||||
self._app = app
|
self._app = app
|
||||||
self._results = app.results
|
|
||||||
self._data = app.data
|
self._data = app.data
|
||||||
self._delta_columns = app.DELTA_COLUMNS
|
self._delta_columns = app.DELTA_COLUMNS
|
||||||
self.delta = False
|
self.resultsDelegate = ResultsDelegate()
|
||||||
self._power_marker = False
|
self.model = ResultTreeModel(self, app)
|
||||||
TreeModel.__init__(self)
|
self.view.setItemDelegate(self.resultsDelegate)
|
||||||
|
self.view.setModel(self)
|
||||||
|
self.model.connect()
|
||||||
|
|
||||||
|
self.connect(self.view.selectionModel(), SIGNAL('selectionChanged(QItemSelection,QItemSelection)'), self.selectionChanged)
|
||||||
|
|
||||||
def _createNode(self, ref, row):
|
def _createNode(self, ref, row):
|
||||||
if self.power_marker:
|
return RefNode(self, None, ref, row)
|
||||||
# ref is a dupe
|
|
||||||
group = self._results.get_group_of_duplicate(ref)
|
|
||||||
return ResultNode(self, None, row, ref, group)
|
|
||||||
else:
|
|
||||||
# ref is a group
|
|
||||||
return ResultNode(self, None, row, ref.ref, ref)
|
|
||||||
|
|
||||||
def _getChildren(self):
|
def _getChildren(self):
|
||||||
return self._results.dupes if self.power_marker else self._results.groups
|
return list(self.model)
|
||||||
|
|
||||||
|
def _updateSelection(self):
|
||||||
|
selectedIndexes = []
|
||||||
|
for path in self.model.selected_paths:
|
||||||
|
modelIndex = self.findIndex(path)
|
||||||
|
if modelIndex.isValid():
|
||||||
|
selectedIndexes.append(modelIndex)
|
||||||
|
if selectedIndexes:
|
||||||
|
selection = QItemSelection()
|
||||||
|
for modelIndex in selectedIndexes:
|
||||||
|
selection.select(modelIndex, modelIndex)
|
||||||
|
flags = QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows
|
||||||
|
self.view.selectionModel().select(selection, flags)
|
||||||
|
flags = QItemSelectionModel.Rows
|
||||||
|
self.view.selectionModel().setCurrentIndex(selectedIndexes[0], flags)
|
||||||
|
else:
|
||||||
|
self.view.selectionModel().clear()
|
||||||
|
|
||||||
def columnCount(self, parent):
|
def columnCount(self, parent):
|
||||||
return len(self._data.COLUMNS)
|
return len(self._data.COLUMNS)
|
||||||
@ -82,51 +70,23 @@ class ResultsModel(TreeModel):
|
|||||||
if not index.isValid():
|
if not index.isValid():
|
||||||
return None
|
return None
|
||||||
node = index.internalPointer()
|
node = index.internalPointer()
|
||||||
|
ref = node.ref
|
||||||
if role == Qt.DisplayRole:
|
if role == Qt.DisplayRole:
|
||||||
data = node.deltaData if self.delta else node.normalData
|
data = ref.data_delta if self.model.delta_values else ref.data
|
||||||
return data[index.column()]
|
return data[index.column()]
|
||||||
elif role == Qt.CheckStateRole:
|
elif role == Qt.CheckStateRole:
|
||||||
if index.column() == 0 and node.dupe is not node.group.ref:
|
if index.column() == 0 and ref.markable:
|
||||||
state = Qt.Checked if self._results.is_marked(node.dupe) else Qt.Unchecked
|
return Qt.Checked if ref.marked else Qt.Unchecked
|
||||||
return state
|
|
||||||
elif role == Qt.ForegroundRole:
|
elif role == Qt.ForegroundRole:
|
||||||
if node.dupe is node.group.ref or node.dupe.is_ref:
|
if ref._dupe is ref._group.ref or ref._dupe.is_ref:
|
||||||
return QBrush(Qt.blue)
|
return QBrush(Qt.blue)
|
||||||
elif self.delta and index.column() in self._delta_columns:
|
elif self.model.delta_values and index.column() in self._delta_columns:
|
||||||
return QBrush(QColor(255, 142, 40)) # orange
|
return QBrush(QColor(255, 142, 40)) # orange
|
||||||
elif role == Qt.EditRole:
|
elif role == Qt.EditRole:
|
||||||
if index.column() == 0:
|
if index.column() == 0:
|
||||||
return node.normalData[index.column()]
|
return ref.data[index.column()]
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def dupesForIndexes(self, indexes):
|
|
||||||
nodes = [index.internalPointer() for index in indexes]
|
|
||||||
return [node.dupe for node in nodes]
|
|
||||||
|
|
||||||
def indexesForDupes(self, dupes):
|
|
||||||
def index(dupe):
|
|
||||||
try:
|
|
||||||
if self.power_marker:
|
|
||||||
row = self._results.dupes.index(dupe)
|
|
||||||
node = self.subnodes[row]
|
|
||||||
assert node.dupe is dupe
|
|
||||||
return self.createIndex(row, 0, node)
|
|
||||||
else:
|
|
||||||
group = self._results.get_group_of_duplicate(dupe)
|
|
||||||
row = self._results.groups.index(group)
|
|
||||||
node = self.subnodes[row]
|
|
||||||
if dupe is group.ref:
|
|
||||||
assert node.dupe is dupe
|
|
||||||
return self.createIndex(row, 0, node)
|
|
||||||
subrow = group.dupes.index(dupe)
|
|
||||||
subnode = node.subnodes[subrow]
|
|
||||||
assert subnode.dupe is dupe
|
|
||||||
return self.createIndex(subrow, 0, subnode)
|
|
||||||
except ValueError: # the dupe is not there anymore
|
|
||||||
return QModelIndex()
|
|
||||||
|
|
||||||
return map(index, dupes)
|
|
||||||
|
|
||||||
def flags(self, index):
|
def flags(self, index):
|
||||||
if not index.isValid():
|
if not index.isValid():
|
||||||
return Qt.ItemIsEnabled
|
return Qt.ItemIsEnabled
|
||||||
@ -144,48 +104,61 @@ class ResultsModel(TreeModel):
|
|||||||
if not index.isValid():
|
if not index.isValid():
|
||||||
return False
|
return False
|
||||||
node = index.internalPointer()
|
node = index.internalPointer()
|
||||||
|
ref = node.ref
|
||||||
if role == Qt.CheckStateRole:
|
if role == Qt.CheckStateRole:
|
||||||
if index.column() == 0:
|
if index.column() == 0:
|
||||||
self._app.toggle_marking_for_dupes([node.dupe])
|
self._app.mark_dupe(ref._dupe, value.toBool())
|
||||||
return True
|
return True
|
||||||
if role == Qt.EditRole:
|
if role == Qt.EditRole:
|
||||||
if index.column() == 0:
|
if index.column() == 0:
|
||||||
value = unicode(value.toString())
|
value = unicode(value.toString())
|
||||||
if self._app.rename_dupe(node.dupe, value):
|
return self.model.rename_selected(value)
|
||||||
node.invalidate()
|
|
||||||
return True
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def sort(self, column, order):
|
def sort(self, column, order):
|
||||||
if self.power_marker:
|
self.model.sort(column, order == Qt.AscendingOrder)
|
||||||
self._results.sort_dupes(column, order == Qt.AscendingOrder, self.delta)
|
|
||||||
else:
|
|
||||||
self._results.sort_groups(column, order == Qt.AscendingOrder)
|
|
||||||
self.reset()
|
|
||||||
|
|
||||||
def toggleMarked(self, indexes):
|
|
||||||
assert indexes
|
|
||||||
dupes = self.dupesForIndexes(indexes)
|
|
||||||
self._app.toggle_marking_for_dupes(dupes)
|
|
||||||
|
|
||||||
#--- Properties
|
#--- Properties
|
||||||
@property
|
@property
|
||||||
def power_marker(self):
|
def power_marker(self):
|
||||||
return self._power_marker
|
return self.model.power_marker
|
||||||
|
|
||||||
@power_marker.setter
|
@power_marker.setter
|
||||||
def power_marker(self, value):
|
def power_marker(self, value):
|
||||||
if value == self._power_marker:
|
self.model.power_marker = value
|
||||||
return
|
|
||||||
self._power_marker = value
|
@property
|
||||||
|
def delta_values(self):
|
||||||
|
return self.model.delta_values
|
||||||
|
|
||||||
|
@delta_values.setter
|
||||||
|
def delta_values(self, value):
|
||||||
|
self.model.delta_values = value
|
||||||
|
|
||||||
|
#--- Events
|
||||||
|
def selectionChanged(self, selected, deselected):
|
||||||
|
indexes = self.view.selectionModel().selectedRows()
|
||||||
|
nodes = [index.internalPointer() for index in indexes]
|
||||||
|
self.model.selected_nodes = [node.ref for node in nodes]
|
||||||
|
|
||||||
|
#--- model --> view
|
||||||
|
def refresh(self):
|
||||||
self.reset()
|
self.reset()
|
||||||
|
self._updateSelection()
|
||||||
|
self.view.expandAll()
|
||||||
|
|
||||||
|
def invalidate_markings(self):
|
||||||
|
# redraw view
|
||||||
|
# HACK. this is the only way I found to update the widget without reseting everything
|
||||||
|
self.view.scroll(0, 1)
|
||||||
|
self.view.scroll(0, -1)
|
||||||
|
|
||||||
|
|
||||||
class ResultsView(QTreeView):
|
class ResultsView(QTreeView):
|
||||||
#--- Override
|
#--- Override
|
||||||
def keyPressEvent(self, event):
|
def keyPressEvent(self, event):
|
||||||
if event.text() == ' ':
|
if event.text() == ' ':
|
||||||
self.model().toggleMarked(self.selectionModel().selectedRows())
|
self.emit(SIGNAL('spacePressed()'))
|
||||||
return
|
return
|
||||||
QTreeView.keyPressEvent(self, event)
|
QTreeView.keyPressEvent(self, event)
|
||||||
|
|
||||||
@ -193,11 +166,3 @@ class ResultsView(QTreeView):
|
|||||||
self.emit(SIGNAL('doubleClicked()'))
|
self.emit(SIGNAL('doubleClicked()'))
|
||||||
# We don't call the superclass' method because the default behavior is to rename the cell.
|
# We don't call the superclass' method because the default behavior is to rename the cell.
|
||||||
|
|
||||||
def setModel(self, model):
|
|
||||||
assert isinstance(model, ResultsModel)
|
|
||||||
QTreeView.setModel(self, model)
|
|
||||||
|
|
||||||
#--- Public
|
|
||||||
def selectedDupes(self):
|
|
||||||
return self.model().dupesForIndexes(self.selectionModel().selectedRows())
|
|
||||||
|
|
||||||
|
20
qt/base/stats_label.py
Normal file
20
qt/base/stats_label.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Created By: Virgil Dupras
|
||||||
|
# Created On: 2010-02-12
|
||||||
|
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
#
|
||||||
|
# This software is licensed under the "HS" License as described in the "LICENSE" file,
|
||||||
|
# which should be included with this package. The terms are also available at
|
||||||
|
# http://www.hardcoded.net/licenses/hs_license
|
||||||
|
|
||||||
|
from core.gui.stats_label import StatsLabel as StatsLabelModel
|
||||||
|
|
||||||
|
class StatsLabel(object):
|
||||||
|
def __init__(self, app, view):
|
||||||
|
self.view = view
|
||||||
|
self.model = StatsLabelModel(self, app)
|
||||||
|
self.model.connect()
|
||||||
|
|
||||||
|
def refresh(self):
|
||||||
|
self.view.setText(self.model.display)
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user