mirror of
https://github.com/arsenetar/dupeguru.git
synced 2026-01-22 06:37:17 +00:00
Format all files with black correcting line length
This commit is contained in:
74
qt/app.py
74
qt/app.py
@@ -65,18 +65,10 @@ class DupeGuru(QObject):
|
||||
self.recentResults.mustOpenItem.connect(self.model.load_from)
|
||||
self.resultWindow = None
|
||||
if self.use_tabs:
|
||||
self.main_window = (
|
||||
TabBarWindow(self)
|
||||
if not self.prefs.tabs_default_pos
|
||||
else TabWindow(self)
|
||||
)
|
||||
self.main_window = TabBarWindow(self) if not self.prefs.tabs_default_pos else TabWindow(self)
|
||||
parent_window = self.main_window
|
||||
self.directories_dialog = self.main_window.createPage(
|
||||
"DirectoriesDialog", app=self
|
||||
)
|
||||
self.main_window.addTab(
|
||||
self.directories_dialog, tr("Directories"), switch=False
|
||||
)
|
||||
self.directories_dialog = self.main_window.createPage("DirectoriesDialog", app=self)
|
||||
self.main_window.addTab(self.directories_dialog, tr("Directories"), switch=False)
|
||||
self.actionDirectoriesWindow.setEnabled(False)
|
||||
else: # floating windows only
|
||||
self.main_window = None
|
||||
@@ -84,9 +76,7 @@ class DupeGuru(QObject):
|
||||
parent_window = self.directories_dialog
|
||||
|
||||
self.progress_window = ProgressWindow(parent_window, self.model.progress_window)
|
||||
self.problemDialog = ProblemDialog(
|
||||
parent=parent_window, model=self.model.problem_dialog
|
||||
)
|
||||
self.problemDialog = ProblemDialog(parent=parent_window, model=self.model.problem_dialog)
|
||||
if self.use_tabs:
|
||||
self.ignoreListDialog = self.main_window.createPage(
|
||||
"IgnoreListDialog",
|
||||
@@ -101,16 +91,10 @@ class DupeGuru(QObject):
|
||||
model=self.model.exclude_list_dialog,
|
||||
)
|
||||
else:
|
||||
self.ignoreListDialog = IgnoreListDialog(
|
||||
parent=parent_window, model=self.model.ignore_list_dialog
|
||||
)
|
||||
self.excludeDialog = ExcludeListDialog(
|
||||
app=self, parent=parent_window, model=self.model.exclude_list_dialog
|
||||
)
|
||||
self.ignoreListDialog = IgnoreListDialog(parent=parent_window, model=self.model.ignore_list_dialog)
|
||||
self.excludeDialog = ExcludeListDialog(app=self, parent=parent_window, model=self.model.exclude_list_dialog)
|
||||
|
||||
self.deletionOptions = DeletionOptions(
|
||||
parent=parent_window, model=self.model.deletion_options
|
||||
)
|
||||
self.deletionOptions = DeletionOptions(parent=parent_window, model=self.model.deletion_options)
|
||||
self.about_box = AboutBox(parent_window, self)
|
||||
|
||||
parent_window.show()
|
||||
@@ -174,25 +158,19 @@ class DupeGuru(QObject):
|
||||
self.model.options["mix_file_kind"] = self.prefs.mix_file_kind
|
||||
self.model.options["escape_filter_regexp"] = not self.prefs.use_regexp
|
||||
self.model.options["clean_empty_dirs"] = self.prefs.remove_empty_folders
|
||||
self.model.options[
|
||||
"ignore_hardlink_matches"
|
||||
] = self.prefs.ignore_hardlink_matches
|
||||
self.model.options["ignore_hardlink_matches"] = self.prefs.ignore_hardlink_matches
|
||||
self.model.options["copymove_dest_type"] = self.prefs.destination_type
|
||||
self.model.options["scan_type"] = self.prefs.get_scan_type(self.model.app_mode)
|
||||
self.model.options["min_match_percentage"] = self.prefs.filter_hardness
|
||||
self.model.options["word_weighting"] = self.prefs.word_weighting
|
||||
self.model.options["match_similar_words"] = self.prefs.match_similar
|
||||
threshold = (
|
||||
self.prefs.small_file_threshold if self.prefs.ignore_small_files else 0
|
||||
)
|
||||
self.model.options["size_threshold"] = (
|
||||
threshold * 1024
|
||||
) # threshold is in KB. The scanner wants bytes
|
||||
big_file_size_threshold = (
|
||||
self.prefs.big_file_size_threshold if self.prefs.big_file_partial_hashes else 0
|
||||
)
|
||||
threshold = self.prefs.small_file_threshold if self.prefs.ignore_small_files else 0
|
||||
self.model.options["size_threshold"] = threshold * 1024 # threshold is in KB. The scanner wants bytes
|
||||
big_file_size_threshold = self.prefs.big_file_size_threshold if self.prefs.big_file_partial_hashes else 0
|
||||
self.model.options["big_file_size_threshold"] = (
|
||||
big_file_size_threshold * 1024 * 1024
|
||||
big_file_size_threshold
|
||||
* 1024
|
||||
* 1024
|
||||
# threshold is in MiB. The scanner wants bytes
|
||||
)
|
||||
scanned_tags = set()
|
||||
@@ -259,9 +237,7 @@ class DupeGuru(QObject):
|
||||
if self.resultWindow is not None:
|
||||
if self.use_tabs:
|
||||
if self.main_window.indexOfWidget(self.resultWindow) < 0:
|
||||
self.main_window.addTab(
|
||||
self.resultWindow, tr("Results"), switch=True
|
||||
)
|
||||
self.main_window.addTab(self.resultWindow, tr("Results"), switch=True)
|
||||
return
|
||||
self.main_window.showTab(self.resultWindow)
|
||||
else:
|
||||
@@ -318,9 +294,7 @@ class DupeGuru(QObject):
|
||||
|
||||
def excludeListTriggered(self):
|
||||
if self.use_tabs:
|
||||
self.showTriggeredTabbedDialog(
|
||||
self.excludeListDialog, tr("Exclusion Filters")
|
||||
)
|
||||
self.showTriggeredTabbedDialog(self.excludeListDialog, tr("Exclusion Filters"))
|
||||
else: # floating windows
|
||||
self.model.exclude_list_dialog.show()
|
||||
|
||||
@@ -328,9 +302,7 @@ class DupeGuru(QObject):
|
||||
"""Add tab for dialog, name the tab with desc_string, then show it."""
|
||||
index = self.main_window.indexOfWidget(dialog)
|
||||
# Create the tab if it doesn't exist already
|
||||
if (
|
||||
index < 0
|
||||
): # or (not dialog.isVisible() and not self.main_window.isTabVisible(index)):
|
||||
if index < 0: # or (not dialog.isVisible() and not self.main_window.isTabVisible(index)):
|
||||
index = self.main_window.addTab(dialog, desc_string, switch=True)
|
||||
# Show the tab for that widget
|
||||
self.main_window.setCurrentIndex(index)
|
||||
@@ -402,13 +374,9 @@ class DupeGuru(QObject):
|
||||
if self.resultWindow is not None:
|
||||
self.resultWindow.close()
|
||||
# This is better for tabs, as it takes care of duplicate items in menu bar
|
||||
self.resultWindow.deleteLater() if self.use_tabs else self.resultWindow.setParent(
|
||||
None
|
||||
)
|
||||
self.resultWindow.deleteLater() if self.use_tabs else self.resultWindow.setParent(None)
|
||||
if self.use_tabs:
|
||||
self.resultWindow = self.main_window.createPage(
|
||||
"ResultWindow", parent=self.main_window, app=self
|
||||
)
|
||||
self.resultWindow = self.main_window.createPage("ResultWindow", parent=self.main_window, app=self)
|
||||
else: # We don't use a tab widget, regular floating QMainWindow
|
||||
self.resultWindow = ResultWindow(self.directories_dialog, self)
|
||||
self.directories_dialog._updateActionsState()
|
||||
@@ -426,9 +394,7 @@ class DupeGuru(QObject):
|
||||
|
||||
def select_dest_file(self, prompt, extension):
|
||||
files = tr("{} file (*.{})").format(extension.upper(), extension)
|
||||
destination, chosen_filter = QFileDialog.getSaveFileName(
|
||||
self.resultWindow, prompt, "", files
|
||||
)
|
||||
destination, chosen_filter = QFileDialog.getSaveFileName(self.resultWindow, prompt, "", files)
|
||||
if not destination.endswith(".{}".format(extension)):
|
||||
destination = "{}.{}".format(destination, extension)
|
||||
return destination
|
||||
|
||||
@@ -42,9 +42,7 @@ class DeletionOptions(QDialog):
|
||||
self.linkMessageLabel = QLabel(text)
|
||||
self.linkMessageLabel.setWordWrap(True)
|
||||
self.verticalLayout.addWidget(self.linkMessageLabel)
|
||||
self.linkTypeRadio = RadioBox(
|
||||
items=[tr("Symlink"), tr("Hardlink")], spread=False
|
||||
)
|
||||
self.linkTypeRadio = RadioBox(items=[tr("Symlink"), tr("Hardlink")], spread=False)
|
||||
self.verticalLayout.addWidget(self.linkTypeRadio)
|
||||
if not self.model.supports_links():
|
||||
self.linkCheckbox.setEnabled(False)
|
||||
|
||||
@@ -31,8 +31,7 @@ class DetailsDialog(QDockWidget):
|
||||
self.model.view = self
|
||||
self.app.willSavePrefs.connect(self.appWillSavePrefs)
|
||||
# self.setAttribute(Qt.WA_DeleteOnClose)
|
||||
parent.addDockWidget(
|
||||
area if self._wasDocked else Qt.BottomDockWidgetArea, self)
|
||||
parent.addDockWidget(area if self._wasDocked else Qt.BottomDockWidgetArea, self)
|
||||
|
||||
def _setupUi(self): # Virtual
|
||||
pass
|
||||
|
||||
@@ -34,9 +34,11 @@ class DetailsModel(QAbstractTableModel):
|
||||
row = index.row()
|
||||
|
||||
ignored_fields = ["Dupe Count"]
|
||||
if (self.model.row(row)[0] in ignored_fields
|
||||
or self.model.row(row)[1] == "---"
|
||||
or self.model.row(row)[2] == "---"):
|
||||
if (
|
||||
self.model.row(row)[0] in ignored_fields
|
||||
or self.model.row(row)[1] == "---"
|
||||
or self.model.row(row)[2] == "---"
|
||||
):
|
||||
if role != Qt.DisplayRole:
|
||||
return None
|
||||
return self.model.row(row)[column]
|
||||
@@ -52,17 +54,9 @@ class DetailsModel(QAbstractTableModel):
|
||||
return None # QVariant()
|
||||
|
||||
def headerData(self, section, orientation, role):
|
||||
if (
|
||||
orientation == Qt.Horizontal
|
||||
and role == Qt.DisplayRole
|
||||
and section < len(HEADER)
|
||||
):
|
||||
if orientation == Qt.Horizontal and role == Qt.DisplayRole and section < len(HEADER):
|
||||
return HEADER[section]
|
||||
elif (
|
||||
orientation == Qt.Vertical
|
||||
and role == Qt.DisplayRole
|
||||
and section < self.model.row_count()
|
||||
):
|
||||
elif orientation == Qt.Vertical and role == Qt.DisplayRole and section < self.model.row_count():
|
||||
# Read "Attribute" cell for horizontal header
|
||||
return self.model.row(section)[0]
|
||||
return None
|
||||
|
||||
@@ -45,9 +45,7 @@ class DirectoriesDialog(QMainWindow):
|
||||
self.recentFolders = Recent(self.app, "recentFolders")
|
||||
self._setupUi()
|
||||
self._updateScanTypeList()
|
||||
self.directoriesModel = DirectoriesModel(
|
||||
self.app.model.directory_tree, view=self.treeView
|
||||
)
|
||||
self.directoriesModel = DirectoriesModel(self.app.model.directory_tree, view=self.treeView)
|
||||
self.directoriesDelegate = DirectoriesDelegate()
|
||||
self.treeView.setItemDelegate(self.directoriesDelegate)
|
||||
self._setupColumns()
|
||||
@@ -170,9 +168,7 @@ class DirectoriesDialog(QMainWindow):
|
||||
label = QLabel(tr("Application Mode:"), self)
|
||||
label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
||||
hl.addWidget(label)
|
||||
self.appModeRadioBox = RadioBox(
|
||||
self, items=[tr("Standard"), tr("Music"), tr("Picture")], spread=False
|
||||
)
|
||||
self.appModeRadioBox = RadioBox(self, items=[tr("Standard"), tr("Music"), tr("Picture")], spread=False)
|
||||
hl.addWidget(self.appModeRadioBox)
|
||||
self.verticalLayout.addLayout(hl)
|
||||
hl = QHBoxLayout()
|
||||
@@ -181,27 +177,21 @@ class DirectoriesDialog(QMainWindow):
|
||||
label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
||||
hl.addWidget(label)
|
||||
self.scanTypeComboBox = QComboBox(self)
|
||||
self.scanTypeComboBox.setSizePolicy(
|
||||
QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
||||
)
|
||||
self.scanTypeComboBox.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed))
|
||||
self.scanTypeComboBox.setMaximumWidth(400)
|
||||
hl.addWidget(self.scanTypeComboBox)
|
||||
self.showPreferencesButton = QPushButton(tr("More Options"), self.centralwidget)
|
||||
self.showPreferencesButton.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
||||
hl.addWidget(self.showPreferencesButton)
|
||||
self.verticalLayout.addLayout(hl)
|
||||
self.promptLabel = QLabel(
|
||||
tr('Select folders to scan and press "Scan".'), self.centralwidget
|
||||
)
|
||||
self.promptLabel = QLabel(tr('Select folders to scan and press "Scan".'), self.centralwidget)
|
||||
self.verticalLayout.addWidget(self.promptLabel)
|
||||
self.treeView = QTreeView(self.centralwidget)
|
||||
self.treeView.setSelectionMode(QAbstractItemView.ExtendedSelection)
|
||||
self.treeView.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
self.treeView.setAcceptDrops(True)
|
||||
triggers = (
|
||||
QAbstractItemView.DoubleClicked
|
||||
| QAbstractItemView.EditKeyPressed
|
||||
| QAbstractItemView.SelectedClicked
|
||||
QAbstractItemView.DoubleClicked | QAbstractItemView.EditKeyPressed | QAbstractItemView.SelectedClicked
|
||||
)
|
||||
self.treeView.setEditTriggers(triggers)
|
||||
self.treeView.setDragDropOverwriteMode(True)
|
||||
@@ -267,9 +257,7 @@ class DirectoriesDialog(QMainWindow):
|
||||
|
||||
def _updateScanTypeList(self):
|
||||
try:
|
||||
self.scanTypeComboBox.currentIndexChanged[int].disconnect(
|
||||
self.scanTypeChanged
|
||||
)
|
||||
self.scanTypeComboBox.currentIndexChanged[int].disconnect(self.scanTypeChanged)
|
||||
except TypeError:
|
||||
# Not connected, ignore
|
||||
pass
|
||||
@@ -299,9 +287,7 @@ class DirectoriesDialog(QMainWindow):
|
||||
def addFolderTriggered(self):
|
||||
title = tr("Select a folder to add to the scanning list")
|
||||
flags = QFileDialog.ShowDirsOnly
|
||||
dirpath = str(
|
||||
QFileDialog.getExistingDirectory(self, title, self.lastAddedFolder, flags)
|
||||
)
|
||||
dirpath = str(QFileDialog.getExistingDirectory(self, title, self.lastAddedFolder, flags))
|
||||
if not dirpath:
|
||||
return
|
||||
self.lastAddedFolder = dirpath
|
||||
@@ -362,9 +348,7 @@ class DirectoriesDialog(QMainWindow):
|
||||
|
||||
def scanTypeChanged(self, index):
|
||||
scan_options = self.app.model.SCANNER_CLASS.get_scan_options()
|
||||
self.app.prefs.set_scan_type(
|
||||
self.app.model.app_mode, scan_options[index].scan_type
|
||||
)
|
||||
self.app.prefs.set_scan_type(self.app.model.app_mode, scan_options[index].scan_type)
|
||||
self.app._update_options()
|
||||
|
||||
def selectionChanged(self, selected, deselected):
|
||||
|
||||
@@ -44,9 +44,7 @@ class DirectoriesDelegate(QStyledItemDelegate):
|
||||
# On OS X (with Qt4.6.0), adding State_Enabled to the flags causes the whole drawing to
|
||||
# fail (draw nothing), but it's an OS X only glitch. On Windows, it works alright.
|
||||
cboption.state |= QStyle.State_Enabled
|
||||
QApplication.style().drawComplexControl(
|
||||
QStyle.CC_ComboBox, cboption, painter
|
||||
)
|
||||
QApplication.style().drawComplexControl(QStyle.CC_ComboBox, cboption, painter)
|
||||
painter.setBrush(option.palette.text())
|
||||
rect = QRect(option.rect)
|
||||
rect.setLeft(rect.left() + 4)
|
||||
@@ -75,9 +73,7 @@ class DirectoriesModel(TreeModel):
|
||||
self.view = view
|
||||
self.view.setModel(self)
|
||||
|
||||
self.view.selectionModel().selectionChanged[
|
||||
(QItemSelection, QItemSelection)
|
||||
].connect(self.selectionChanged)
|
||||
self.view.selectionModel().selectionChanged[(QItemSelection, QItemSelection)].connect(self.selectionChanged)
|
||||
|
||||
def _createNode(self, ref, row):
|
||||
return RefNode(self, None, ref, row)
|
||||
@@ -155,10 +151,7 @@ class DirectoriesModel(TreeModel):
|
||||
|
||||
# --- Events
|
||||
def selectionChanged(self, selected, deselected):
|
||||
newNodes = [
|
||||
modelIndex.internalPointer().ref
|
||||
for modelIndex in self.view.selectionModel().selectedRows()
|
||||
]
|
||||
newNodes = [modelIndex.internalPointer().ref for modelIndex in self.view.selectionModel().selectedRows()]
|
||||
self.model.selected_nodes = newNodes
|
||||
|
||||
# --- Signals
|
||||
|
||||
@@ -5,13 +5,22 @@
|
||||
import re
|
||||
from PyQt5.QtCore import Qt, pyqtSlot
|
||||
from PyQt5.QtWidgets import (
|
||||
QPushButton, QLineEdit, QVBoxLayout, QGridLayout, QDialog,
|
||||
QTableView, QAbstractItemView, QSpacerItem, QSizePolicy, QHeaderView
|
||||
QPushButton,
|
||||
QLineEdit,
|
||||
QVBoxLayout,
|
||||
QGridLayout,
|
||||
QDialog,
|
||||
QTableView,
|
||||
QAbstractItemView,
|
||||
QSpacerItem,
|
||||
QSizePolicy,
|
||||
QHeaderView,
|
||||
)
|
||||
from .exclude_list_table import ExcludeListTable
|
||||
|
||||
from core.exclude import AlreadyThereException
|
||||
from hscommon.trans import trget
|
||||
|
||||
tr = trget("ui")
|
||||
|
||||
|
||||
@@ -51,9 +60,7 @@ class ExcludeListDialog(QDialog):
|
||||
self.testLine = QLineEdit()
|
||||
self.tableView = QTableView()
|
||||
triggers = (
|
||||
QAbstractItemView.DoubleClicked
|
||||
| QAbstractItemView.EditKeyPressed
|
||||
| QAbstractItemView.SelectedClicked
|
||||
QAbstractItemView.DoubleClicked | QAbstractItemView.EditKeyPressed | QAbstractItemView.SelectedClicked
|
||||
)
|
||||
self.tableView.setEditTriggers(triggers)
|
||||
self.tableView.setSelectionMode(QTableView.ExtendedSelection)
|
||||
@@ -150,7 +157,9 @@ class ExcludeListDialog(QDialog):
|
||||
self.table.refresh()
|
||||
|
||||
def display_help_message(self):
|
||||
self.app.show_message(tr("""\
|
||||
self.app.show_message(
|
||||
tr(
|
||||
"""\
|
||||
These (case sensitive) python regular expressions will filter out files during scans.<br>\
|
||||
Directores will also have their <strong>default state</strong> set to Excluded \
|
||||
in the Directories tab if their name happens to match one of the selected regular expressions.<br>\
|
||||
@@ -163,4 +172,6 @@ You can test the regular expression with the "test string" button after pasting
|
||||
<code>C:\\\\User\\My Pictures\\test.png</code><br><br>
|
||||
Matching regular expressions will be highlighted.<br>\
|
||||
If there is at least one highlight, the path or filename tested will be ignored during scans.<br><br>\
|
||||
Directories and files starting with a period '.' are filtered out by default.<br><br>"""))
|
||||
Directories and files starting with a period '.' are filtered out by default.<br><br>"""
|
||||
)
|
||||
)
|
||||
|
||||
@@ -8,15 +8,14 @@ from PyQt5.QtGui import QFont, QFontMetrics, QIcon, QColor
|
||||
from qtlib.column import Column
|
||||
from qtlib.table import Table
|
||||
from hscommon.trans import trget
|
||||
|
||||
tr = trget("ui")
|
||||
|
||||
|
||||
class ExcludeListTable(Table):
|
||||
"""Model for exclude list"""
|
||||
COLUMNS = [
|
||||
Column("marked", defaultWidth=15),
|
||||
Column("regex", defaultWidth=230)
|
||||
]
|
||||
|
||||
COLUMNS = [Column("marked", defaultWidth=15), Column("regex", defaultWidth=230)]
|
||||
|
||||
def __init__(self, app, view, **kwargs):
|
||||
model = app.model.exclude_list_dialog.exclude_list_table # pointer to GUITable
|
||||
|
||||
@@ -56,9 +56,7 @@ class IgnoreListDialog(QDialog):
|
||||
self.clearButton = QPushButton(tr("Clear"))
|
||||
self.closeButton = QPushButton(tr("Close"))
|
||||
self.verticalLayout.addLayout(
|
||||
horizontalWrap(
|
||||
[self.removeSelectedButton, self.clearButton, None, self.closeButton]
|
||||
)
|
||||
horizontalWrap([self.removeSelectedButton, self.clearButton, None, self.closeButton])
|
||||
)
|
||||
|
||||
# --- model --> view
|
||||
|
||||
@@ -10,7 +10,7 @@ from qtlib.table import Table
|
||||
|
||||
|
||||
class IgnoreListTable(Table):
|
||||
""" Ignore list model"""
|
||||
"""Ignore list model"""
|
||||
|
||||
COLUMNS = [
|
||||
Column("path1", defaultWidth=230),
|
||||
|
||||
@@ -59,13 +59,9 @@ class PreferencesDialog(PreferencesDialogBase):
|
||||
self.widgetsVLayout.addWidget(self.matchSimilarBox)
|
||||
self._setupAddCheckbox("mixFileKindBox", tr("Can mix file kind"))
|
||||
self.widgetsVLayout.addWidget(self.mixFileKindBox)
|
||||
self._setupAddCheckbox(
|
||||
"useRegexpBox", tr("Use regular expressions when filtering")
|
||||
)
|
||||
self._setupAddCheckbox("useRegexpBox", tr("Use regular expressions when filtering"))
|
||||
self.widgetsVLayout.addWidget(self.useRegexpBox)
|
||||
self._setupAddCheckbox(
|
||||
"removeEmptyFoldersBox", tr("Remove empty folders on delete or move")
|
||||
)
|
||||
self._setupAddCheckbox("removeEmptyFoldersBox", tr("Remove empty folders on delete or move"))
|
||||
self.widgetsVLayout.addWidget(self.removeEmptyFoldersBox)
|
||||
self._setupAddCheckbox(
|
||||
"ignoreHardlinkMatches",
|
||||
|
||||
@@ -5,14 +5,13 @@
|
||||
# http://www.gnu.org/licenses/gpl-3.0.html
|
||||
|
||||
from PyQt5.QtCore import Qt, QSize, pyqtSignal, pyqtSlot
|
||||
from PyQt5.QtWidgets import (
|
||||
QAbstractItemView, QSizePolicy, QGridLayout, QSplitter, QFrame)
|
||||
from PyQt5.QtWidgets import QAbstractItemView, QSizePolicy, QGridLayout, QSplitter, QFrame
|
||||
from PyQt5.QtGui import QResizeEvent
|
||||
from hscommon.trans import trget
|
||||
from ..details_dialog import DetailsDialog as DetailsDialogBase
|
||||
from ..details_table import DetailsTable
|
||||
from .image_viewer import (
|
||||
ViewerToolBar, ScrollAreaImageViewer, ScrollAreaController)
|
||||
from .image_viewer import ViewerToolBar, ScrollAreaImageViewer, ScrollAreaController
|
||||
|
||||
tr = trget("ui")
|
||||
|
||||
|
||||
@@ -70,8 +69,7 @@ class DetailsDialog(DetailsDialogBase):
|
||||
self.splitter.addWidget(self.tableView)
|
||||
self.splitter.setStretchFactor(1, 1)
|
||||
# Late population needed here for connections to the toolbar
|
||||
self.vController.setupViewers(
|
||||
self.selectedImageViewer, self.referenceImageViewer)
|
||||
self.vController.setupViewers(self.selectedImageViewer, self.referenceImageViewer)
|
||||
# self.setCentralWidget(self.splitter) # only as QMainWindow
|
||||
self.setWidget(self.splitter) # only as QDockWidget
|
||||
|
||||
@@ -103,11 +101,11 @@ class DetailsDialog(DetailsDialogBase):
|
||||
# Give the splitter a maximum height to reach. This is assuming that
|
||||
# all rows below their headers have the same height
|
||||
self.tableView.setMaximumHeight(
|
||||
self.tableView.rowHeight(1)
|
||||
* self.tableModel.model.row_count()
|
||||
self.tableView.rowHeight(1) * self.tableModel.model.row_count()
|
||||
+ self.tableView.verticalHeader().sectionSize(0)
|
||||
# looks like the handle is taken into account by the splitter
|
||||
+ self.splitter.handle(1).size().height())
|
||||
+ self.splitter.handle(1).size().height()
|
||||
)
|
||||
DetailsDialogBase.show(self)
|
||||
self.ensure_same_sizes()
|
||||
self._update()
|
||||
@@ -138,6 +136,7 @@ class DetailsDialog(DetailsDialogBase):
|
||||
|
||||
class EmittingFrame(QFrame):
|
||||
"""Emits a signal whenever is resized"""
|
||||
|
||||
resized = pyqtSignal(QResizeEvent)
|
||||
|
||||
def resizeEvent(self, event):
|
||||
|
||||
@@ -2,15 +2,24 @@
|
||||
# which should be included with this package. The terms are also available at
|
||||
# http://www.gnu.org/licenses/gpl-3.0.html
|
||||
|
||||
from PyQt5.QtCore import (
|
||||
QObject, Qt, QSize, QRectF, QPointF, QPoint, pyqtSlot, pyqtSignal, QEvent)
|
||||
from PyQt5.QtCore import QObject, Qt, QSize, QRectF, QPointF, QPoint, pyqtSlot, pyqtSignal, QEvent
|
||||
from PyQt5.QtGui import QPixmap, QPainter, QPalette, QCursor, QIcon, QKeySequence
|
||||
from PyQt5.QtWidgets import (
|
||||
QGraphicsView, QGraphicsScene, QGraphicsPixmapItem,
|
||||
QToolBar, QToolButton, QAction, QWidget, QScrollArea,
|
||||
QApplication, QAbstractScrollArea, QStyle)
|
||||
QGraphicsView,
|
||||
QGraphicsScene,
|
||||
QGraphicsPixmapItem,
|
||||
QToolBar,
|
||||
QToolButton,
|
||||
QAction,
|
||||
QWidget,
|
||||
QScrollArea,
|
||||
QApplication,
|
||||
QAbstractScrollArea,
|
||||
QStyle,
|
||||
)
|
||||
from hscommon.trans import trget
|
||||
from hscommon.plat import ISLINUX
|
||||
|
||||
tr = trget("ui")
|
||||
|
||||
MAX_SCALE = 12.0
|
||||
@@ -50,8 +59,7 @@ class ViewerToolBar(QToolBar):
|
||||
"actionZoomIn",
|
||||
QKeySequence.ZoomIn,
|
||||
QIcon.fromTheme("zoom-in")
|
||||
if ISLINUX
|
||||
and not self.parent.app.prefs.details_dialog_override_theme_icons
|
||||
if ISLINUX and not self.parent.app.prefs.details_dialog_override_theme_icons
|
||||
else QIcon(QPixmap(":/" + "zoom_in")),
|
||||
tr("Increase zoom"),
|
||||
controller.zoomIn,
|
||||
@@ -60,8 +68,7 @@ class ViewerToolBar(QToolBar):
|
||||
"actionZoomOut",
|
||||
QKeySequence.ZoomOut,
|
||||
QIcon.fromTheme("zoom-out")
|
||||
if ISLINUX
|
||||
and not self.parent.app.prefs.details_dialog_override_theme_icons
|
||||
if ISLINUX and not self.parent.app.prefs.details_dialog_override_theme_icons
|
||||
else QIcon(QPixmap(":/" + "zoom_out")),
|
||||
tr("Decrease zoom"),
|
||||
controller.zoomOut,
|
||||
@@ -70,8 +77,7 @@ class ViewerToolBar(QToolBar):
|
||||
"actionNormalSize",
|
||||
tr("Ctrl+/"),
|
||||
QIcon.fromTheme("zoom-original")
|
||||
if ISLINUX
|
||||
and not self.parent.app.prefs.details_dialog_override_theme_icons
|
||||
if ISLINUX and not self.parent.app.prefs.details_dialog_override_theme_icons
|
||||
else QIcon(QPixmap(":/" + "zoom_original")),
|
||||
tr("Normal size"),
|
||||
controller.zoomNormalSize,
|
||||
@@ -80,12 +86,11 @@ class ViewerToolBar(QToolBar):
|
||||
"actionBestFit",
|
||||
tr("Ctrl+*"),
|
||||
QIcon.fromTheme("zoom-best-fit")
|
||||
if ISLINUX
|
||||
and not self.parent.app.prefs.details_dialog_override_theme_icons
|
||||
if ISLINUX and not self.parent.app.prefs.details_dialog_override_theme_icons
|
||||
else QIcon(QPixmap(":/" + "zoom_best_fit")),
|
||||
tr("Best fit"),
|
||||
controller.zoomBestFit,
|
||||
)
|
||||
),
|
||||
]
|
||||
# TODO try with QWidgetAction() instead in order to have
|
||||
# the popup menu work in the toolbar (if resized below minimum height)
|
||||
@@ -95,13 +100,12 @@ class ViewerToolBar(QToolBar):
|
||||
self.buttonImgSwap = QToolButton(self)
|
||||
self.buttonImgSwap.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
||||
self.buttonImgSwap.setIcon(
|
||||
QIcon.fromTheme('view-refresh',
|
||||
self.style().standardIcon(QStyle.SP_BrowserReload))
|
||||
if ISLINUX
|
||||
and not self.parent.app.prefs.details_dialog_override_theme_icons
|
||||
else QIcon(QPixmap(":/" + "exchange")))
|
||||
self.buttonImgSwap.setText('Swap images')
|
||||
self.buttonImgSwap.setToolTip('Swap images')
|
||||
QIcon.fromTheme("view-refresh", self.style().standardIcon(QStyle.SP_BrowserReload))
|
||||
if ISLINUX and not self.parent.app.prefs.details_dialog_override_theme_icons
|
||||
else QIcon(QPixmap(":/" + "exchange"))
|
||||
)
|
||||
self.buttonImgSwap.setText("Swap images")
|
||||
self.buttonImgSwap.setToolTip("Swap images")
|
||||
self.buttonImgSwap.pressed.connect(self.controller.swapImages)
|
||||
self.buttonImgSwap.released.connect(self.controller.swapImages)
|
||||
|
||||
@@ -207,11 +211,11 @@ class BaseController(QObject):
|
||||
# than the ReferenceImageViewer by one pixel, which distorts the
|
||||
# scaled down pixmap for the reference, hence we'll reuse its size here.
|
||||
selected_size = self._updateImage(
|
||||
self.selectedPixmap, self.scaledSelectedPixmap,
|
||||
self.selectedViewer, None, same_group)
|
||||
self.selectedPixmap, self.scaledSelectedPixmap, self.selectedViewer, None, same_group
|
||||
)
|
||||
self._updateImage(
|
||||
self.referencePixmap, self.scaledReferencePixmap,
|
||||
self.referenceViewer, selected_size, same_group)
|
||||
self.referencePixmap, self.scaledReferencePixmap, self.referenceViewer, selected_size, same_group
|
||||
)
|
||||
if ignore_update:
|
||||
self.selectedViewer.ignore_signal = False
|
||||
|
||||
@@ -229,12 +233,10 @@ class BaseController(QObject):
|
||||
return target_size
|
||||
# zoomed in state, expand
|
||||
# only if not same_group, we need full update
|
||||
scaledpixmap = pixmap.scaled(
|
||||
target_size, Qt.KeepAspectRatioByExpanding, Qt.FastTransformation)
|
||||
scaledpixmap = pixmap.scaled(target_size, Qt.KeepAspectRatioByExpanding, Qt.FastTransformation)
|
||||
else:
|
||||
# best fit, keep ratio always
|
||||
scaledpixmap = pixmap.scaled(
|
||||
target_size, Qt.KeepAspectRatio, Qt.FastTransformation)
|
||||
scaledpixmap = pixmap.scaled(target_size, Qt.KeepAspectRatio, Qt.FastTransformation)
|
||||
viewer.setImage(scaledpixmap)
|
||||
return target_size
|
||||
|
||||
@@ -347,12 +349,8 @@ class BaseController(QObject):
|
||||
self.selectedViewer.resetCenter()
|
||||
self.referenceViewer.resetCenter()
|
||||
|
||||
target_size = self._updateImage(
|
||||
self.selectedPixmap, self.scaledSelectedPixmap,
|
||||
self.selectedViewer, None, True)
|
||||
self._updateImage(
|
||||
self.referencePixmap, self.scaledReferencePixmap,
|
||||
self.referenceViewer, target_size, True)
|
||||
target_size = self._updateImage(self.selectedPixmap, self.scaledSelectedPixmap, self.selectedViewer, None, True)
|
||||
self._updateImage(self.referencePixmap, self.scaledReferencePixmap, self.referenceViewer, target_size, True)
|
||||
self.centerViews()
|
||||
|
||||
self.parent.verticalToolBar.buttonZoomIn.setEnabled(False)
|
||||
@@ -402,6 +400,7 @@ class BaseController(QObject):
|
||||
|
||||
class QWidgetController(BaseController):
|
||||
"""Specialized version for QWidget-based viewers."""
|
||||
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
|
||||
@@ -430,6 +429,7 @@ class QWidgetController(BaseController):
|
||||
|
||||
class ScrollAreaController(BaseController):
|
||||
"""Specialized version fro QLabel-based viewers."""
|
||||
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
|
||||
@@ -442,10 +442,8 @@ class ScrollAreaController(BaseController):
|
||||
super().updateBothImages(same_group)
|
||||
if not self.referenceViewer.isEnabled():
|
||||
return
|
||||
self.referenceViewer._horizontalScrollBar.setValue(
|
||||
self.selectedViewer._horizontalScrollBar.value())
|
||||
self.referenceViewer._verticalScrollBar.setValue(
|
||||
self.selectedViewer._verticalScrollBar.value())
|
||||
self.referenceViewer._horizontalScrollBar.setValue(self.selectedViewer._horizontalScrollBar.value())
|
||||
self.referenceViewer._verticalScrollBar.setValue(self.selectedViewer._verticalScrollBar.value())
|
||||
|
||||
@pyqtSlot(QPoint)
|
||||
def onDraggedMouse(self, delta):
|
||||
@@ -518,6 +516,7 @@ class ScrollAreaController(BaseController):
|
||||
|
||||
class GraphicsViewController(BaseController):
|
||||
"""Specialized version fro QGraphicsView-based viewers."""
|
||||
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
|
||||
@@ -625,10 +624,8 @@ class GraphicsViewController(BaseController):
|
||||
if ignore_update:
|
||||
self.selectedViewer.ignore_signal = True
|
||||
|
||||
self._updateFitImage(
|
||||
self.selectedPixmap, self.selectedViewer)
|
||||
self._updateFitImage(
|
||||
self.referencePixmap, self.referenceViewer)
|
||||
self._updateFitImage(self.selectedPixmap, self.selectedViewer)
|
||||
self._updateFitImage(self.referencePixmap, self.referenceViewer)
|
||||
|
||||
if ignore_update:
|
||||
self.selectedViewer.ignore_signal = False
|
||||
@@ -699,6 +696,7 @@ class GraphicsViewController(BaseController):
|
||||
|
||||
class QWidgetImageViewer(QWidget):
|
||||
"""Use a QPixmap, but no scrollbars and no keyboard key sequence for navigation."""
|
||||
|
||||
# FIXME: panning while zoomed-in is broken (due to delta not interpolated right?
|
||||
mouseDragged = pyqtSignal(QPointF)
|
||||
mouseWheeled = pyqtSignal(float)
|
||||
@@ -720,15 +718,13 @@ class QWidgetImageViewer(QWidget):
|
||||
self.setMouseTracking(False)
|
||||
|
||||
def __repr__(self):
|
||||
return f'{self._instance_name}'
|
||||
return f"{self._instance_name}"
|
||||
|
||||
def connectMouseSignals(self):
|
||||
if not self._dragConnection:
|
||||
self._dragConnection = self.mouseDragged.connect(
|
||||
self.controller.onDraggedMouse)
|
||||
self._dragConnection = self.mouseDragged.connect(self.controller.onDraggedMouse)
|
||||
if not self._wheelConnection:
|
||||
self._wheelConnection = self.mouseWheeled.connect(
|
||||
self.controller.scaleImagesBy)
|
||||
self._wheelConnection = self.mouseWheeled.connect(self.controller.scaleImagesBy)
|
||||
|
||||
def disconnectMouseSignals(self):
|
||||
if self._dragConnection:
|
||||
@@ -746,7 +742,7 @@ class QWidgetImageViewer(QWidget):
|
||||
painter.drawPixmap(self._rect.topLeft(), self._pixmap)
|
||||
|
||||
def resetCenter(self):
|
||||
""" Resets origin """
|
||||
"""Resets origin"""
|
||||
# Make sure we are not still panning around
|
||||
self._mousePanningDelta = QPointF()
|
||||
self.update()
|
||||
@@ -783,8 +779,7 @@ class QWidgetImageViewer(QWidget):
|
||||
event.ignore()
|
||||
return
|
||||
|
||||
self._mousePanningDelta += (
|
||||
event.pos() - self._lastMouseClickPoint) * 1.0 / self.current_scale
|
||||
self._mousePanningDelta += (event.pos() - self._lastMouseClickPoint) * 1.0 / self.current_scale
|
||||
self._lastMouseClickPoint = event.pos()
|
||||
if self._drag:
|
||||
self.mouseDragged.emit(self._mousePanningDelta)
|
||||
@@ -860,6 +855,7 @@ class QWidgetImageViewer(QWidget):
|
||||
|
||||
class ScalablePixmap(QWidget):
|
||||
"""Container for a pixmap that scales up very fast, used in ScrollAreaImageViewer."""
|
||||
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
self._pixmap = QPixmap()
|
||||
@@ -881,6 +877,7 @@ class ScalablePixmap(QWidget):
|
||||
|
||||
class ScrollAreaImageViewer(QScrollArea):
|
||||
"""Implementation using a pixmap container in a simple scroll area."""
|
||||
|
||||
mouseDragged = pyqtSignal(QPoint)
|
||||
mouseWheeled = pyqtSignal(float, QPointF)
|
||||
|
||||
@@ -921,7 +918,7 @@ class ScrollAreaImageViewer(QScrollArea):
|
||||
self.setVisible(True)
|
||||
|
||||
def __repr__(self):
|
||||
return f'{self._instance_name}'
|
||||
return f"{self._instance_name}"
|
||||
|
||||
def toggleScrollBars(self, forceOn=False):
|
||||
if not self.prefs.details_dialog_viewers_show_scrollbars:
|
||||
@@ -938,11 +935,9 @@ class ScrollAreaImageViewer(QScrollArea):
|
||||
|
||||
def connectMouseSignals(self):
|
||||
if not self._dragConnection:
|
||||
self._dragConnection = self.mouseDragged.connect(
|
||||
self.controller.onDraggedMouse)
|
||||
self._dragConnection = self.mouseDragged.connect(self.controller.onDraggedMouse)
|
||||
if not self._wheelConnection:
|
||||
self._wheelConnection = self.mouseWheeled.connect(
|
||||
self.controller.onMouseWheel)
|
||||
self._wheelConnection = self.mouseWheeled.connect(self.controller.onMouseWheel)
|
||||
|
||||
def disconnectMouseSignals(self):
|
||||
if self._dragConnection:
|
||||
@@ -955,10 +950,8 @@ class ScrollAreaImageViewer(QScrollArea):
|
||||
def connectScrollBars(self):
|
||||
"""Only call once controller is connected."""
|
||||
# Cyclic connections are handled by Qt
|
||||
self._verticalScrollBar.valueChanged.connect(
|
||||
self.controller.onVScrollBarChanged, Qt.UniqueConnection)
|
||||
self._horizontalScrollBar.valueChanged.connect(
|
||||
self.controller.onHScrollBarChanged, Qt.UniqueConnection)
|
||||
self._verticalScrollBar.valueChanged.connect(self.controller.onVScrollBarChanged, Qt.UniqueConnection)
|
||||
self._horizontalScrollBar.valueChanged.connect(self.controller.onHScrollBarChanged, Qt.UniqueConnection)
|
||||
|
||||
def contextMenuEvent(self, event):
|
||||
"""Block parent's (main window) context menu on right click."""
|
||||
@@ -987,7 +980,7 @@ class ScrollAreaImageViewer(QScrollArea):
|
||||
event.ignore()
|
||||
return
|
||||
if self._drag:
|
||||
delta = (event.pos() - self._lastMouseClickPoint)
|
||||
delta = event.pos() - self._lastMouseClickPoint
|
||||
self._lastMouseClickPoint = event.pos()
|
||||
self.mouseDragged.emit(delta)
|
||||
super().mouseMoveEvent(event)
|
||||
@@ -1064,35 +1057,29 @@ class ScrollAreaImageViewer(QScrollArea):
|
||||
"""After scaling, no mouse position, default to center."""
|
||||
# scrollBar.setMaximum(scrollBar.maximum() - scrollBar.minimum() + scrollBar.pageStep())
|
||||
self._horizontalScrollBar.setValue(
|
||||
int(factor * self._horizontalScrollBar.value()
|
||||
+ ((factor - 1) * self._horizontalScrollBar.pageStep() / 2)))
|
||||
int(factor * self._horizontalScrollBar.value() + ((factor - 1) * self._horizontalScrollBar.pageStep() / 2))
|
||||
)
|
||||
self._verticalScrollBar.setValue(
|
||||
int(factor * self._verticalScrollBar.value()
|
||||
+ ((factor - 1) * self._verticalScrollBar.pageStep() / 2)))
|
||||
int(factor * self._verticalScrollBar.value() + ((factor - 1) * self._verticalScrollBar.pageStep() / 2))
|
||||
)
|
||||
|
||||
def adjustScrollBarsScaled(self, delta):
|
||||
"""After scaling with the mouse, update relative to mouse position."""
|
||||
self._horizontalScrollBar.setValue(
|
||||
self._horizontalScrollBar.value() + delta.x())
|
||||
self._verticalScrollBar.setValue(
|
||||
self._verticalScrollBar.value() + delta.y())
|
||||
self._horizontalScrollBar.setValue(self._horizontalScrollBar.value() + delta.x())
|
||||
self._verticalScrollBar.setValue(self._verticalScrollBar.value() + delta.y())
|
||||
|
||||
def adjustScrollBarsAuto(self):
|
||||
"""After panning, update accordingly."""
|
||||
self.horizontalScrollBar().setValue(
|
||||
self.horizontalScrollBar().value() - self._mousePanningDelta.x())
|
||||
self.verticalScrollBar().setValue(
|
||||
self.verticalScrollBar().value() - self._mousePanningDelta.y())
|
||||
self.horizontalScrollBar().setValue(self.horizontalScrollBar().value() - self._mousePanningDelta.x())
|
||||
self.verticalScrollBar().setValue(self.verticalScrollBar().value() - self._mousePanningDelta.y())
|
||||
|
||||
def adjustScrollBarCentered(self):
|
||||
"""Just center in the middle."""
|
||||
self._horizontalScrollBar.setValue(
|
||||
int(self._horizontalScrollBar.maximum() / 2))
|
||||
self._verticalScrollBar.setValue(
|
||||
int(self._verticalScrollBar.maximum() / 2))
|
||||
self._horizontalScrollBar.setValue(int(self._horizontalScrollBar.maximum() / 2))
|
||||
self._verticalScrollBar.setValue(int(self._verticalScrollBar.maximum() / 2))
|
||||
|
||||
def resetCenter(self):
|
||||
""" Resets origin """
|
||||
"""Resets origin"""
|
||||
self._mousePanningDelta = QPoint()
|
||||
self.current_scale = 1.0
|
||||
# self.scaleAt(1.0)
|
||||
@@ -1127,6 +1114,7 @@ class ScrollAreaImageViewer(QScrollArea):
|
||||
|
||||
class GraphicsViewViewer(QGraphicsView):
|
||||
"""Re-Implementation a full-fledged GraphicsView but is a bit buggy."""
|
||||
|
||||
mouseDragged = pyqtSignal()
|
||||
mouseWheeled = pyqtSignal(float, QPointF)
|
||||
|
||||
@@ -1178,11 +1166,9 @@ class GraphicsViewViewer(QGraphicsView):
|
||||
|
||||
def connectMouseSignals(self):
|
||||
if not self._dragConnection:
|
||||
self._dragConnection = self.mouseDragged.connect(
|
||||
self.controller.syncCenters)
|
||||
self._dragConnection = self.mouseDragged.connect(self.controller.syncCenters)
|
||||
if not self._wheelConnection:
|
||||
self._wheelConnection = self.mouseWheeled.connect(
|
||||
self.controller.onMouseWheel)
|
||||
self._wheelConnection = self.mouseWheeled.connect(self.controller.onMouseWheel)
|
||||
|
||||
def disconnectMouseSignals(self):
|
||||
if self._dragConnection:
|
||||
@@ -1195,10 +1181,8 @@ class GraphicsViewViewer(QGraphicsView):
|
||||
def connectScrollBars(self):
|
||||
"""Only call once controller is connected."""
|
||||
# Cyclic connections are handled by Qt
|
||||
self._verticalScrollBar.valueChanged.connect(
|
||||
self.controller.onVScrollBarChanged, Qt.UniqueConnection)
|
||||
self._horizontalScrollBar.valueChanged.connect(
|
||||
self.controller.onHScrollBarChanged, Qt.UniqueConnection)
|
||||
self._verticalScrollBar.valueChanged.connect(self.controller.onVScrollBarChanged, Qt.UniqueConnection)
|
||||
self._horizontalScrollBar.valueChanged.connect(self.controller.onHScrollBarChanged, Qt.UniqueConnection)
|
||||
|
||||
def toggleScrollBars(self, forceOn=False):
|
||||
if not self.prefs.details_dialog_viewers_show_scrollbars:
|
||||
@@ -1298,7 +1282,7 @@ class GraphicsViewViewer(QGraphicsView):
|
||||
self.centerOn(self._centerPoint)
|
||||
|
||||
def resetCenter(self):
|
||||
""" Resets origin """
|
||||
"""Resets origin"""
|
||||
self._mousePanningDelta = QPointF()
|
||||
self.current_scale = 1.0
|
||||
|
||||
@@ -1345,10 +1329,8 @@ class GraphicsViewViewer(QGraphicsView):
|
||||
|
||||
def adjustScrollBarsScaled(self, delta):
|
||||
"""After scaling with the mouse, update relative to mouse position."""
|
||||
self._horizontalScrollBar.setValue(
|
||||
self._horizontalScrollBar.value() + delta.x())
|
||||
self._verticalScrollBar.setValue(
|
||||
self._verticalScrollBar.value() + delta.y())
|
||||
self._horizontalScrollBar.setValue(self._horizontalScrollBar.value() + delta.x())
|
||||
self._verticalScrollBar.setValue(self._verticalScrollBar.value() + delta.y())
|
||||
|
||||
def sizeHint(self):
|
||||
return self.viewport().rect().size()
|
||||
@@ -1356,15 +1338,13 @@ class GraphicsViewViewer(QGraphicsView):
|
||||
def adjustScrollBarsFactor(self, factor):
|
||||
"""After scaling, no mouse position, default to center."""
|
||||
self._horizontalScrollBar.setValue(
|
||||
int(factor * self._horizontalScrollBar.value()
|
||||
+ ((factor - 1) * self._horizontalScrollBar.pageStep() / 2)))
|
||||
int(factor * self._horizontalScrollBar.value() + ((factor - 1) * self._horizontalScrollBar.pageStep() / 2))
|
||||
)
|
||||
self._verticalScrollBar.setValue(
|
||||
int(factor * self._verticalScrollBar.value()
|
||||
+ ((factor - 1) * self._verticalScrollBar.pageStep() / 2)))
|
||||
int(factor * self._verticalScrollBar.value() + ((factor - 1) * self._verticalScrollBar.pageStep() / 2))
|
||||
)
|
||||
|
||||
def adjustScrollBarsAuto(self):
|
||||
"""After panning, update accordingly."""
|
||||
self.horizontalScrollBar().setValue(
|
||||
self.horizontalScrollBar().value() - self._mousePanningDelta.x())
|
||||
self.verticalScrollBar().setValue(
|
||||
self.verticalScrollBar().value() - self._mousePanningDelta.y())
|
||||
self.horizontalScrollBar().setValue(self.horizontalScrollBar().value() - self._mousePanningDelta.x())
|
||||
self.verticalScrollBar().setValue(self.verticalScrollBar().value() - self._mousePanningDelta.y())
|
||||
|
||||
@@ -30,13 +30,15 @@ class File(PhotoBase):
|
||||
image = QImage(str(self.path))
|
||||
image = image.convertToFormat(QImage.Format_RGB888)
|
||||
if type(orientation) == str:
|
||||
logging.warning("Orientation for file '%s' was a str '%s', not an int.",
|
||||
str(self.path), orientation)
|
||||
logging.warning("Orientation for file '%s' was a str '%s', not an int.", str(self.path), orientation)
|
||||
try:
|
||||
orientation = int(orientation)
|
||||
except Exception as e:
|
||||
logging.exception("Skipping transformation because could not \
|
||||
convert str to int. %s", e)
|
||||
logging.exception(
|
||||
"Skipping transformation because could not \
|
||||
convert str to int. %s",
|
||||
e,
|
||||
)
|
||||
return getblocks(image, block_count_per_side)
|
||||
# MYSTERY TO SOLVE: For reasons I cannot explain, orientations 5 and 7 don't work for
|
||||
# duplicate scanning. The transforms seems to work fine (if I try to save the image after
|
||||
|
||||
@@ -21,19 +21,13 @@ class PreferencesDialog(PreferencesDialogBase):
|
||||
def _setupPreferenceWidgets(self):
|
||||
self._setupFilterHardnessBox()
|
||||
self.widgetsVLayout.addLayout(self.filterHardnessHLayout)
|
||||
self._setupAddCheckbox(
|
||||
"matchScaledBox", tr("Match pictures of different dimensions")
|
||||
)
|
||||
self._setupAddCheckbox("matchScaledBox", tr("Match pictures of different dimensions"))
|
||||
self.widgetsVLayout.addWidget(self.matchScaledBox)
|
||||
self._setupAddCheckbox("mixFileKindBox", tr("Can mix file kind"))
|
||||
self.widgetsVLayout.addWidget(self.mixFileKindBox)
|
||||
self._setupAddCheckbox(
|
||||
"useRegexpBox", tr("Use regular expressions when filtering")
|
||||
)
|
||||
self._setupAddCheckbox("useRegexpBox", tr("Use regular expressions when filtering"))
|
||||
self.widgetsVLayout.addWidget(self.useRegexpBox)
|
||||
self._setupAddCheckbox(
|
||||
"removeEmptyFoldersBox", tr("Remove empty folders on delete or move")
|
||||
)
|
||||
self._setupAddCheckbox("removeEmptyFoldersBox", tr("Remove empty folders on delete or move"))
|
||||
self.widgetsVLayout.addWidget(self.removeEmptyFoldersBox)
|
||||
self._setupAddCheckbox(
|
||||
"ignoreHardlinkMatches",
|
||||
@@ -52,45 +46,37 @@ class PreferencesDialog(PreferencesDialogBase):
|
||||
|
||||
def _setupDisplayPage(self):
|
||||
super()._setupDisplayPage()
|
||||
self._setupAddCheckbox("details_dialog_override_theme_icons",
|
||||
tr("Override theme icons in viewer toolbar"))
|
||||
self._setupAddCheckbox("details_dialog_override_theme_icons", tr("Override theme icons in viewer toolbar"))
|
||||
self.details_dialog_override_theme_icons.setToolTip(
|
||||
tr("Use our own internal icons instead of those provided by the theme engine"))
|
||||
tr("Use our own internal icons instead of those provided by the theme engine")
|
||||
)
|
||||
# Prevent changing this on platforms where themes are unpredictable
|
||||
self.details_dialog_override_theme_icons.setEnabled(False if not ISLINUX else True)
|
||||
# Insert this right after the vertical title bar option
|
||||
index = self.details_groupbox_layout.indexOf(self.details_dialog_vertical_titlebar)
|
||||
self.details_groupbox_layout.insertWidget(
|
||||
index + 1, self.details_dialog_override_theme_icons)
|
||||
self._setupAddCheckbox("details_dialog_viewers_show_scrollbars",
|
||||
tr("Show scrollbars in image viewers"))
|
||||
self.details_groupbox_layout.insertWidget(index + 1, self.details_dialog_override_theme_icons)
|
||||
self._setupAddCheckbox("details_dialog_viewers_show_scrollbars", tr("Show scrollbars in image viewers"))
|
||||
self.details_dialog_viewers_show_scrollbars.setToolTip(
|
||||
tr("When the image displayed doesn't fit the viewport, \
|
||||
show scrollbars to span the view around"))
|
||||
self.details_groupbox_layout.insertWidget(
|
||||
index + 2, self.details_dialog_viewers_show_scrollbars)
|
||||
tr(
|
||||
"When the image displayed doesn't fit the viewport, \
|
||||
show scrollbars to span the view around"
|
||||
)
|
||||
)
|
||||
self.details_groupbox_layout.insertWidget(index + 2, self.details_dialog_viewers_show_scrollbars)
|
||||
|
||||
def _load(self, prefs, setchecked, section):
|
||||
setchecked(self.matchScaledBox, prefs.match_scaled)
|
||||
self.cacheTypeRadio.selected_index = (
|
||||
1 if prefs.picture_cache_type == "shelve" else 0
|
||||
)
|
||||
self.cacheTypeRadio.selected_index = 1 if prefs.picture_cache_type == "shelve" else 0
|
||||
|
||||
# Update UI state based on selected scan type
|
||||
scan_type = prefs.get_scan_type(AppMode.Picture)
|
||||
fuzzy_scan = scan_type == ScanType.FuzzyBlock
|
||||
self.filterHardnessSlider.setEnabled(fuzzy_scan)
|
||||
setchecked(self.details_dialog_override_theme_icons,
|
||||
prefs.details_dialog_override_theme_icons)
|
||||
setchecked(self.details_dialog_viewers_show_scrollbars,
|
||||
prefs.details_dialog_viewers_show_scrollbars)
|
||||
setchecked(self.details_dialog_override_theme_icons, prefs.details_dialog_override_theme_icons)
|
||||
setchecked(self.details_dialog_viewers_show_scrollbars, prefs.details_dialog_viewers_show_scrollbars)
|
||||
|
||||
def _save(self, prefs, ischecked):
|
||||
prefs.match_scaled = ischecked(self.matchScaledBox)
|
||||
prefs.picture_cache_type = (
|
||||
"shelve" if self.cacheTypeRadio.selected_index == 1 else "sqlite"
|
||||
)
|
||||
prefs.details_dialog_override_theme_icons =\
|
||||
ischecked(self.details_dialog_override_theme_icons)
|
||||
prefs.details_dialog_viewers_show_scrollbars =\
|
||||
ischecked(self.details_dialog_viewers_show_scrollbars)
|
||||
prefs.picture_cache_type = "shelve" if self.cacheTypeRadio.selected_index == 1 else "sqlite"
|
||||
prefs.details_dialog_override_theme_icons = ischecked(self.details_dialog_override_theme_icons)
|
||||
prefs.details_dialog_viewers_show_scrollbars = ischecked(self.details_dialog_viewers_show_scrollbars)
|
||||
|
||||
@@ -20,9 +20,7 @@ class Preferences(PreferencesBase):
|
||||
get = self.get_value
|
||||
self.filter_hardness = get("FilterHardness", self.filter_hardness)
|
||||
self.mix_file_kind = get("MixFileKind", self.mix_file_kind)
|
||||
self.ignore_hardlink_matches = get(
|
||||
"IgnoreHardlinkMatches", self.ignore_hardlink_matches
|
||||
)
|
||||
self.ignore_hardlink_matches = get("IgnoreHardlinkMatches", self.ignore_hardlink_matches)
|
||||
self.use_regexp = get("UseRegexp", self.use_regexp)
|
||||
self.remove_empty_folders = get("RemoveEmptyFolders", self.remove_empty_folders)
|
||||
self.debug_mode = get("DebugMode", self.debug_mode)
|
||||
@@ -34,37 +32,36 @@ class Preferences(PreferencesBase):
|
||||
|
||||
self.tableFontSize = get("TableFontSize", self.tableFontSize)
|
||||
self.reference_bold_font = get("ReferenceBoldFont", self.reference_bold_font)
|
||||
self.details_dialog_titlebar_enabled = get("DetailsDialogTitleBarEnabled",
|
||||
self.details_dialog_titlebar_enabled)
|
||||
self.details_dialog_vertical_titlebar = get("DetailsDialogVerticalTitleBar",
|
||||
self.details_dialog_vertical_titlebar)
|
||||
self.details_dialog_titlebar_enabled = get("DetailsDialogTitleBarEnabled", self.details_dialog_titlebar_enabled)
|
||||
self.details_dialog_vertical_titlebar = get(
|
||||
"DetailsDialogVerticalTitleBar", self.details_dialog_vertical_titlebar
|
||||
)
|
||||
# On Windows and MacOS, use internal icons by default
|
||||
self.details_dialog_override_theme_icons =\
|
||||
get("DetailsDialogOverrideThemeIcons",
|
||||
self.details_dialog_override_theme_icons) if ISLINUX else True
|
||||
self.details_table_delta_foreground_color =\
|
||||
get("DetailsTableDeltaForegroundColor", self.details_table_delta_foreground_color)
|
||||
self.details_dialog_viewers_show_scrollbars =\
|
||||
get("DetailsDialogViewersShowScrollbars", self.details_dialog_viewers_show_scrollbars)
|
||||
|
||||
self.result_table_ref_foreground_color =\
|
||||
get("ResultTableRefForegroundColor", self.result_table_ref_foreground_color)
|
||||
self.result_table_ref_background_color =\
|
||||
get("ResultTableRefBackgroundColor", self.result_table_ref_background_color)
|
||||
self.result_table_delta_foreground_color =\
|
||||
get("ResultTableDeltaForegroundColor", self.result_table_delta_foreground_color)
|
||||
|
||||
self.resultWindowIsMaximized = get(
|
||||
"ResultWindowIsMaximized", self.resultWindowIsMaximized
|
||||
self.details_dialog_override_theme_icons = (
|
||||
get("DetailsDialogOverrideThemeIcons", self.details_dialog_override_theme_icons) if ISLINUX else True
|
||||
)
|
||||
self.details_table_delta_foreground_color = get(
|
||||
"DetailsTableDeltaForegroundColor", self.details_table_delta_foreground_color
|
||||
)
|
||||
self.details_dialog_viewers_show_scrollbars = get(
|
||||
"DetailsDialogViewersShowScrollbars", self.details_dialog_viewers_show_scrollbars
|
||||
)
|
||||
|
||||
self.result_table_ref_foreground_color = get(
|
||||
"ResultTableRefForegroundColor", self.result_table_ref_foreground_color
|
||||
)
|
||||
self.result_table_ref_background_color = get(
|
||||
"ResultTableRefBackgroundColor", self.result_table_ref_background_color
|
||||
)
|
||||
self.result_table_delta_foreground_color = get(
|
||||
"ResultTableDeltaForegroundColor", self.result_table_delta_foreground_color
|
||||
)
|
||||
|
||||
self.resultWindowIsMaximized = get("ResultWindowIsMaximized", self.resultWindowIsMaximized)
|
||||
self.resultWindowRect = self.get_rect("ResultWindowRect", self.resultWindowRect)
|
||||
self.mainWindowIsMaximized = get(
|
||||
"MainWindowIsMaximized", self.mainWindowIsMaximized
|
||||
)
|
||||
self.mainWindowIsMaximized = get("MainWindowIsMaximized", self.mainWindowIsMaximized)
|
||||
self.mainWindowRect = self.get_rect("MainWindowRect", self.mainWindowRect)
|
||||
self.directoriesWindowRect = self.get_rect(
|
||||
"DirectoriesWindowRect", self.directoriesWindowRect
|
||||
)
|
||||
self.directoriesWindowRect = self.get_rect("DirectoriesWindowRect", self.directoriesWindowRect)
|
||||
|
||||
self.recentResults = get("RecentResults", self.recentResults)
|
||||
self.recentFolders = get("RecentFolders", self.recentFolders)
|
||||
|
||||
@@ -79,12 +79,8 @@ class PrioritizeDialog(QDialog):
|
||||
super().__init__(parent, flags, **kwargs)
|
||||
self._setupUi()
|
||||
self.model = PrioritizeDialogModel(app=app.model)
|
||||
self.categoryList = ComboboxModel(
|
||||
model=self.model.category_list, view=self.categoryCombobox
|
||||
)
|
||||
self.criteriaList = ListviewModel(
|
||||
model=self.model.criteria_list, view=self.criteriaListView
|
||||
)
|
||||
self.categoryList = ComboboxModel(model=self.model.category_list, view=self.categoryCombobox)
|
||||
self.criteriaList = ListviewModel(model=self.model.criteria_list, view=self.criteriaListView)
|
||||
self.prioritizationList = PrioritizationList(
|
||||
model=self.model.prioritization_list, view=self.prioritizationListView
|
||||
)
|
||||
@@ -112,12 +108,8 @@ class PrioritizeDialog(QDialog):
|
||||
self.categoryCombobox = QComboBox()
|
||||
self.criteriaListView = QListView()
|
||||
self.criteriaListView.setSelectionMode(QAbstractItemView.ExtendedSelection)
|
||||
self.addCriteriaButton = QPushButton(
|
||||
self.style().standardIcon(QStyle.SP_ArrowRight), ""
|
||||
)
|
||||
self.removeCriteriaButton = QPushButton(
|
||||
self.style().standardIcon(QStyle.SP_ArrowLeft), ""
|
||||
)
|
||||
self.addCriteriaButton = QPushButton(self.style().standardIcon(QStyle.SP_ArrowRight), "")
|
||||
self.removeCriteriaButton = QPushButton(self.style().standardIcon(QStyle.SP_ArrowLeft), "")
|
||||
self.prioritizationListView = QListView()
|
||||
self.prioritizationListView.setAcceptDrops(True)
|
||||
self.prioritizationListView.setDragEnabled(True)
|
||||
|
||||
@@ -295,9 +295,7 @@ class ResultWindow(QMainWindow):
|
||||
if menu.actions():
|
||||
menu.clear()
|
||||
self._column_actions = []
|
||||
for index, (display, visible) in enumerate(
|
||||
self.app.model.result_table.columns.menu_items()
|
||||
):
|
||||
for index, (display, visible) in enumerate(self.app.model.result_table.columns.menu_items()):
|
||||
action = menu.addAction(display)
|
||||
action.setCheckable(True)
|
||||
action.setChecked(visible)
|
||||
|
||||
@@ -34,15 +34,11 @@ class PreferencesDialog(PreferencesDialogBase):
|
||||
self.verticalLayout_4 = QVBoxLayout(self.widget)
|
||||
self._setupAddCheckbox("wordWeightingBox", tr("Word weighting"), self.widget)
|
||||
self.verticalLayout_4.addWidget(self.wordWeightingBox)
|
||||
self._setupAddCheckbox(
|
||||
"matchSimilarBox", tr("Match similar words"), self.widget
|
||||
)
|
||||
self._setupAddCheckbox("matchSimilarBox", tr("Match similar words"), self.widget)
|
||||
self.verticalLayout_4.addWidget(self.matchSimilarBox)
|
||||
self._setupAddCheckbox("mixFileKindBox", tr("Can mix file kind"), self.widget)
|
||||
self.verticalLayout_4.addWidget(self.mixFileKindBox)
|
||||
self._setupAddCheckbox(
|
||||
"useRegexpBox", tr("Use regular expressions when filtering"), self.widget
|
||||
)
|
||||
self._setupAddCheckbox("useRegexpBox", tr("Use regular expressions when filtering"), self.widget)
|
||||
self.verticalLayout_4.addWidget(self.useRegexpBox)
|
||||
self._setupAddCheckbox(
|
||||
"removeEmptyFoldersBox",
|
||||
@@ -51,17 +47,13 @@ class PreferencesDialog(PreferencesDialogBase):
|
||||
)
|
||||
self.verticalLayout_4.addWidget(self.removeEmptyFoldersBox)
|
||||
self.horizontalLayout_2 = QHBoxLayout()
|
||||
self._setupAddCheckbox(
|
||||
"ignoreSmallFilesBox", tr("Ignore files smaller than"), self.widget
|
||||
)
|
||||
self._setupAddCheckbox("ignoreSmallFilesBox", tr("Ignore files smaller than"), self.widget)
|
||||
self.horizontalLayout_2.addWidget(self.ignoreSmallFilesBox)
|
||||
self.sizeThresholdSpinBox = QSpinBox(self.widget)
|
||||
sizePolicy = QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(
|
||||
self.sizeThresholdSpinBox.sizePolicy().hasHeightForWidth()
|
||||
)
|
||||
sizePolicy.setHeightForWidth(self.sizeThresholdSpinBox.sizePolicy().hasHeightForWidth())
|
||||
self.sizeThresholdSpinBox.setSizePolicy(sizePolicy)
|
||||
self.sizeThresholdSpinBox.setMaximumSize(QSize(100, 16777215))
|
||||
self.sizeThresholdSpinBox.setRange(0, 1000000)
|
||||
@@ -96,9 +88,7 @@ class PreferencesDialog(PreferencesDialogBase):
|
||||
self.widget,
|
||||
)
|
||||
self.verticalLayout_4.addWidget(self.ignoreHardlinkMatches)
|
||||
self._setupAddCheckbox(
|
||||
"debugModeBox", tr("Debug mode (restart required)"), self.widget
|
||||
)
|
||||
self._setupAddCheckbox("debugModeBox", tr("Debug mode (restart required)"), self.widget)
|
||||
self.verticalLayout_4.addWidget(self.debugModeBox)
|
||||
self.widgetsVLayout.addWidget(self.widget)
|
||||
self._setupBottomPart()
|
||||
|
||||
@@ -19,6 +19,7 @@ from .directories_dialog import DirectoriesDialog
|
||||
from .result_window import ResultWindow
|
||||
from .ignore_list_dialog import IgnoreListDialog
|
||||
from .exclude_list_dialog import ExcludeListDialog
|
||||
|
||||
tr = trget("ui")
|
||||
|
||||
|
||||
@@ -135,16 +136,15 @@ class TabWindow(QMainWindow):
|
||||
action.setEnabled(True)
|
||||
|
||||
self.app.directories_dialog.actionShowResultsWindow.setEnabled(
|
||||
False if page_type == "ResultWindow"
|
||||
else self.app.resultWindow is not None)
|
||||
False if page_type == "ResultWindow" else self.app.resultWindow is not None
|
||||
)
|
||||
self.app.actionIgnoreList.setEnabled(
|
||||
True if self.app.ignoreListDialog is not None
|
||||
and not page_type == "IgnoreListDialog" else False)
|
||||
self.app.actionDirectoriesWindow.setEnabled(
|
||||
False if page_type == "DirectoriesDialog" else True)
|
||||
True if self.app.ignoreListDialog is not None and not page_type == "IgnoreListDialog" else False
|
||||
)
|
||||
self.app.actionDirectoriesWindow.setEnabled(False if page_type == "DirectoriesDialog" else True)
|
||||
self.app.actionExcludeList.setEnabled(
|
||||
True if self.app.excludeListDialog is not None
|
||||
and not page_type == "ExcludeListDialog" else False)
|
||||
True if self.app.excludeListDialog is not None and not page_type == "ExcludeListDialog" else False
|
||||
)
|
||||
|
||||
self.previous_widget_actions = active_widget.specific_actions
|
||||
self.last_index = current_index
|
||||
@@ -176,8 +176,7 @@ class TabWindow(QMainWindow):
|
||||
index = self.tabWidget.addTab(page, title)
|
||||
# index = self.tabWidget.insertTab(-1, page, title)
|
||||
if isinstance(page, DirectoriesDialog):
|
||||
self.tabWidget.tabBar().setTabButton(
|
||||
index, QTabBar.RightSide, None)
|
||||
self.tabWidget.tabBar().setTabButton(index, QTabBar.RightSide, None)
|
||||
if switch:
|
||||
self.setCurrentIndex(index)
|
||||
return index
|
||||
@@ -250,6 +249,7 @@ class TabWindow(QMainWindow):
|
||||
class TabBarWindow(TabWindow):
|
||||
"""Implementation which uses a separate QTabBar and QStackedWidget.
|
||||
The Tab bar is placed next to the menu bar to save real estate."""
|
||||
|
||||
def __init__(self, app, **kwargs):
|
||||
super().__init__(app, **kwargs)
|
||||
|
||||
@@ -286,8 +286,7 @@ class TabBarWindow(TabWindow):
|
||||
self.tabBar.insertTab(stack_index, title)
|
||||
|
||||
if isinstance(page, DirectoriesDialog):
|
||||
self.tabBar.setTabButton(
|
||||
stack_index, QTabBar.RightSide, None)
|
||||
self.tabBar.setTabButton(stack_index, QTabBar.RightSide, None)
|
||||
if switch: # switch to the added tab immediately upon creation
|
||||
self.setTabIndex(stack_index)
|
||||
return stack_index
|
||||
|
||||
Reference in New Issue
Block a user