Merge pull request #683 from glubsy/details_dialog_improvements
Add image comparison features to details dialog
2
.gitignore
vendored
@ -23,3 +23,5 @@ cocoa/autogen
|
|||||||
*.pyd
|
*.pyd
|
||||||
*.exe
|
*.exe
|
||||||
*.spec
|
*.spec
|
||||||
|
|
||||||
|
.vscode
|
||||||
|
2
CREDITS
@ -1,6 +1,8 @@
|
|||||||
To know who contributed to dupeGuru, you can look at the commit log, but not all contributions
|
To know who contributed to dupeGuru, you can look at the commit log, but not all contributions
|
||||||
result in a commit. This file lists contributors who don't necessarily appear in the commit log.
|
result in a commit. This file lists contributors who don't necessarily appear in the commit log.
|
||||||
|
|
||||||
|
* Jason Cho, Exchange icon
|
||||||
|
* schollidesign (https://findicons.com/pack/1035/human_o2), Zoom-in, Zoom-out, Zoom-best-fit, Zoom-original icons
|
||||||
* Jérôme Cantin, Main icon
|
* Jérôme Cantin, Main icon
|
||||||
* Gregor Tätzner, German localization
|
* Gregor Tätzner, German localization
|
||||||
* Frank Weber, German localization
|
* Frank Weber, German localization
|
||||||
|
BIN
images/exchange.icns
Normal file
BIN
images/exchange.ico
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
images/exchange.png
Normal file
After Width: | Height: | Size: 797 B |
BIN
images/exchange_purple.png
Normal file
After Width: | Height: | Size: 685 B |
BIN
images/exchange_purple_upscaled.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
images/exchange_purple_waifu_s4_tta8.png
Normal file
After Width: | Height: | Size: 7.0 KiB |
BIN
images/exchange_purple_waifu_s4_tta8.xcf
Normal file
BIN
images/exchange_waifu_s4_tta8.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
images/old_zoom_best_fit.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
images/old_zoom_in.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
images/old_zoom_original.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
images/old_zoom_out.png
Normal file
After Width: | Height: | Size: 11 KiB |
28
qt/app.py
@ -7,7 +7,7 @@
|
|||||||
import sys
|
import sys
|
||||||
import os.path as op
|
import os.path as op
|
||||||
|
|
||||||
from PyQt5.QtCore import QTimer, QObject, QUrl, pyqtSignal
|
from PyQt5.QtCore import QTimer, QObject, QUrl, pyqtSignal, Qt
|
||||||
from PyQt5.QtGui import QDesktopServices
|
from PyQt5.QtGui import QDesktopServices
|
||||||
from PyQt5.QtWidgets import QApplication, QFileDialog, QDialog, QMessageBox
|
from PyQt5.QtWidgets import QApplication, QFileDialog, QDialog, QMessageBox
|
||||||
|
|
||||||
@ -58,11 +58,11 @@ class DupeGuru(QObject):
|
|||||||
def _setup(self):
|
def _setup(self):
|
||||||
core.pe.photo.PLAT_SPECIFIC_PHOTO_CLASS = PlatSpecificPhoto
|
core.pe.photo.PLAT_SPECIFIC_PHOTO_CLASS = PlatSpecificPhoto
|
||||||
self._setupActions()
|
self._setupActions()
|
||||||
|
self.details_dialog = None
|
||||||
self._update_options()
|
self._update_options()
|
||||||
self.recentResults = Recent(self, "recentResults")
|
self.recentResults = Recent(self, "recentResults")
|
||||||
self.recentResults.mustOpenItem.connect(self.model.load_from)
|
self.recentResults.mustOpenItem.connect(self.model.load_from)
|
||||||
self.resultWindow = None
|
self.resultWindow = None
|
||||||
self.details_dialog = None
|
|
||||||
if self.use_tabs:
|
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
|
parent_window = self.main_window
|
||||||
@ -177,6 +177,9 @@ class DupeGuru(QObject):
|
|||||||
self.model.options["match_scaled"] = self.prefs.match_scaled
|
self.model.options["match_scaled"] = self.prefs.match_scaled
|
||||||
self.model.options["picture_cache_type"] = self.prefs.picture_cache_type
|
self.model.options["picture_cache_type"] = self.prefs.picture_cache_type
|
||||||
|
|
||||||
|
if self.details_dialog:
|
||||||
|
self.details_dialog.update_options()
|
||||||
|
|
||||||
# --- Private
|
# --- Private
|
||||||
def _get_details_dialog_class(self):
|
def _get_details_dialog_class(self):
|
||||||
if self.model.app_mode == AppMode.Picture:
|
if self.model.app_mode == AppMode.Picture:
|
||||||
@ -212,22 +215,22 @@ class DupeGuru(QObject):
|
|||||||
|
|
||||||
def show_details(self):
|
def show_details(self):
|
||||||
if self.details_dialog is not None:
|
if self.details_dialog is not None:
|
||||||
|
if not self.details_dialog.isVisible():
|
||||||
self.details_dialog.show()
|
self.details_dialog.show()
|
||||||
|
else:
|
||||||
|
self.details_dialog.hide()
|
||||||
|
|
||||||
def showResultsWindow(self):
|
def showResultsWindow(self):
|
||||||
if self.resultWindow is not None:
|
if self.resultWindow is not None:
|
||||||
if self.use_tabs:
|
if self.use_tabs:
|
||||||
self.main_window.addTab(
|
self.main_window.showTab(self.resultWindow)
|
||||||
self.resultWindow, "Results", switch=True)
|
|
||||||
else:
|
else:
|
||||||
self.resultWindow.show()
|
self.resultWindow.show()
|
||||||
|
|
||||||
def showDirectoriesWindow(self):
|
def showDirectoriesWindow(self):
|
||||||
if self.directories_dialog is not None:
|
if self.directories_dialog is not None:
|
||||||
if self.use_tabs:
|
if self.use_tabs:
|
||||||
index = self.main_window.indexOfWidget(self.directories_dialog)
|
self.main_window.showTab(self.directories_dialog)
|
||||||
self.main_window.setTabVisible(index, True)
|
|
||||||
self.main_window.setCurrentIndex(index)
|
|
||||||
else:
|
else:
|
||||||
self.directories_dialog.show()
|
self.directories_dialog.show()
|
||||||
|
|
||||||
@ -295,6 +298,9 @@ class DupeGuru(QObject):
|
|||||||
preferences_dialog.setParent(None)
|
preferences_dialog.setParent(None)
|
||||||
|
|
||||||
def quitTriggered(self):
|
def quitTriggered(self):
|
||||||
|
if self.details_dialog is not None:
|
||||||
|
self.details_dialog.close()
|
||||||
|
|
||||||
if self.main_window:
|
if self.main_window:
|
||||||
self.main_window.close()
|
self.main_window.close()
|
||||||
else:
|
else:
|
||||||
@ -333,14 +339,20 @@ class DupeGuru(QObject):
|
|||||||
"""Creates resultWindow and details_dialog depending on the selected ``app_mode``.
|
"""Creates resultWindow and details_dialog depending on the selected ``app_mode``.
|
||||||
"""
|
"""
|
||||||
if self.details_dialog is not None:
|
if self.details_dialog is not None:
|
||||||
|
# The object is not deleted entirely, avoid saving its geometry in the future
|
||||||
|
# self.willSavePrefs.disconnect(self.details_dialog.appWillSavePrefs)
|
||||||
|
# or simply delete it on close which is probably cleaner:
|
||||||
|
self.details_dialog.setAttribute(Qt.WA_DeleteOnClose)
|
||||||
self.details_dialog.close()
|
self.details_dialog.close()
|
||||||
self.details_dialog.setParent(None)
|
# self.details_dialog.setParent(None) # seems unnecessary
|
||||||
if self.resultWindow is not None:
|
if self.resultWindow is not None:
|
||||||
self.resultWindow.close()
|
self.resultWindow.close()
|
||||||
self.resultWindow.setParent(None)
|
self.resultWindow.setParent(None)
|
||||||
if self.use_tabs:
|
if self.use_tabs:
|
||||||
self.resultWindow = self.main_window.createPage(
|
self.resultWindow = self.main_window.createPage(
|
||||||
"ResultWindow", parent=self.main_window, app=self)
|
"ResultWindow", parent=self.main_window, app=self)
|
||||||
|
self.main_window.addTab(
|
||||||
|
self.resultWindow, "Results", switch=False)
|
||||||
else: # We don't use a tab widget, regular floating QMainWindow
|
else: # We don't use a tab widget, regular floating QMainWindow
|
||||||
self.resultWindow = ResultWindow(self.directories_dialog, self)
|
self.resultWindow = ResultWindow(self.directories_dialog, self)
|
||||||
self.directories_dialog._updateActionsState()
|
self.directories_dialog._updateActionsState()
|
||||||
|
@ -7,34 +7,63 @@
|
|||||||
# http://www.gnu.org/licenses/gpl-3.0.html
|
# http://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtWidgets import QDialog
|
from PyQt5.QtWidgets import QDockWidget, QWidget
|
||||||
|
|
||||||
from .details_table import DetailsModel
|
from .details_table import DetailsModel
|
||||||
|
from hscommon.plat import ISLINUX
|
||||||
|
|
||||||
|
|
||||||
class DetailsDialog(QDialog):
|
class DetailsDialog(QDockWidget):
|
||||||
def __init__(self, parent, app, **kwargs):
|
def __init__(self, parent, app, **kwargs):
|
||||||
super().__init__(parent, Qt.Tool, **kwargs)
|
super().__init__(parent, Qt.Tool, **kwargs)
|
||||||
|
self.parent = parent
|
||||||
self.app = app
|
self.app = app
|
||||||
self.model = app.model.details_panel
|
self.model = app.model.details_panel
|
||||||
|
self.setAllowedAreas(Qt.AllDockWidgetAreas)
|
||||||
self._setupUi()
|
self._setupUi()
|
||||||
# To avoid saving uninitialized geometry on appWillSavePrefs, we track whether our dialog
|
# To avoid saving uninitialized geometry on appWillSavePrefs, we track whether our dialog
|
||||||
# has been shown. If it has, we know that our geometry should be saved.
|
# has been shown. If it has, we know that our geometry should be saved.
|
||||||
self._shown_once = False
|
self._shown_once = False
|
||||||
self.app.prefs.restoreGeometry("DetailsWindowRect", self)
|
self._wasDocked, area = self.app.prefs.restoreGeometry("DetailsWindowRect", self)
|
||||||
self.tableModel = DetailsModel(self.model)
|
self.tableModel = DetailsModel(self.model, app)
|
||||||
# tableView is defined in subclasses
|
# tableView is defined in subclasses
|
||||||
self.tableView.setModel(self.tableModel)
|
self.tableView.setModel(self.tableModel)
|
||||||
self.model.view = self
|
self.model.view = self
|
||||||
|
|
||||||
self.app.willSavePrefs.connect(self.appWillSavePrefs)
|
self.app.willSavePrefs.connect(self.appWillSavePrefs)
|
||||||
|
# self.setAttribute(Qt.WA_DeleteOnClose)
|
||||||
|
parent.addDockWidget(
|
||||||
|
area if self._wasDocked else Qt.BottomDockWidgetArea, self)
|
||||||
|
|
||||||
def _setupUi(self): # Virtual
|
def _setupUi(self): # Virtual
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def show(self):
|
def show(self):
|
||||||
|
if not self._shown_once and self._wasDocked:
|
||||||
|
self.setFloating(False)
|
||||||
self._shown_once = True
|
self._shown_once = True
|
||||||
super().show()
|
super().show()
|
||||||
|
self.update_options()
|
||||||
|
|
||||||
|
def update_options(self):
|
||||||
|
# This disables the title bar (if we had not set one before already)
|
||||||
|
# essentially making it a simple floating window, not dockable anymore
|
||||||
|
if not self.app.prefs.details_dialog_titlebar_enabled:
|
||||||
|
if not self.titleBarWidget(): # default title bar
|
||||||
|
self.setTitleBarWidget(QWidget()) # disables title bar
|
||||||
|
# Windows (and MacOS?) users cannot move a floating window which
|
||||||
|
# has not native decoration so we force it to dock for now
|
||||||
|
if not ISLINUX:
|
||||||
|
self.setFloating(False)
|
||||||
|
elif self.titleBarWidget() is not None: # title bar is disabled
|
||||||
|
self.setTitleBarWidget(None) # resets to the default title bar
|
||||||
|
elif not self.titleBarWidget() and not self.app.prefs.details_dialog_titlebar_enabled:
|
||||||
|
self.setTitleBarWidget(QWidget())
|
||||||
|
|
||||||
|
features = self.features()
|
||||||
|
if self.app.prefs.details_dialog_vertical_titlebar:
|
||||||
|
self.setFeatures(features | QDockWidget.DockWidgetVerticalTitleBar)
|
||||||
|
elif features & QDockWidget.DockWidgetVerticalTitleBar:
|
||||||
|
self.setFeatures(features ^ QDockWidget.DockWidgetVerticalTitleBar)
|
||||||
|
|
||||||
# --- Events
|
# --- Events
|
||||||
def appWillSavePrefs(self):
|
def appWillSavePrefs(self):
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
from PyQt5.QtCore import Qt, QAbstractTableModel
|
from PyQt5.QtCore import Qt, QAbstractTableModel
|
||||||
from PyQt5.QtWidgets import QHeaderView, QTableView
|
from PyQt5.QtWidgets import QHeaderView, QTableView
|
||||||
from PyQt5.QtGui import QFont, QBrush, QColor
|
from PyQt5.QtGui import QFont, QBrush
|
||||||
|
|
||||||
from hscommon.trans import trget
|
from hscommon.trans import trget
|
||||||
|
|
||||||
@ -18,9 +18,10 @@ HEADER = [tr("Selected"), tr("Reference")]
|
|||||||
|
|
||||||
|
|
||||||
class DetailsModel(QAbstractTableModel):
|
class DetailsModel(QAbstractTableModel):
|
||||||
def __init__(self, model, **kwargs):
|
def __init__(self, model, app, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.model = model
|
self.model = model
|
||||||
|
self.prefs = app.prefs
|
||||||
|
|
||||||
def columnCount(self, parent):
|
def columnCount(self, parent):
|
||||||
return len(HEADER)
|
return len(HEADER)
|
||||||
@ -43,7 +44,7 @@ class DetailsModel(QAbstractTableModel):
|
|||||||
if role == Qt.DisplayRole:
|
if role == Qt.DisplayRole:
|
||||||
return self.model.row(row)[column]
|
return self.model.row(row)[column]
|
||||||
if role == Qt.ForegroundRole and self.model.row(row)[1] != self.model.row(row)[2]:
|
if role == Qt.ForegroundRole and self.model.row(row)[1] != self.model.row(row)[2]:
|
||||||
return QBrush(QColor(250, 20, 20)) # red
|
return QBrush(self.prefs.details_table_delta_foreground_color)
|
||||||
if role == Qt.FontRole and self.model.row(row)[1] != self.model.row(row)[2]:
|
if role == Qt.FontRole and self.model.row(row)[1] != self.model.row(row)[2]:
|
||||||
font = QFont(self.model.view.font()) # or simply QFont()
|
font = QFont(self.model.view.font()) # or simply QFont()
|
||||||
font.setBold(True)
|
font.setBold(True)
|
||||||
@ -85,8 +86,6 @@ class DetailsTable(QTableView):
|
|||||||
# The model needs to be set to set header stuff
|
# The model needs to be set to set header stuff
|
||||||
hheader = self.horizontalHeader()
|
hheader = self.horizontalHeader()
|
||||||
hheader.setHighlightSections(False)
|
hheader.setHighlightSections(False)
|
||||||
hheader.setStretchLastSection(False)
|
|
||||||
hheader.resizeSection(0, 100)
|
|
||||||
hheader.setSectionResizeMode(0, QHeaderView.Stretch)
|
hheader.setSectionResizeMode(0, QHeaderView.Stretch)
|
||||||
hheader.setSectionResizeMode(1, QHeaderView.Stretch)
|
hheader.setSectionResizeMode(1, QHeaderView.Stretch)
|
||||||
vheader = self.verticalHeader()
|
vheader = self.verticalHeader()
|
||||||
|
@ -5,5 +5,10 @@
|
|||||||
<file alias="plus">../images/plus_8.png</file>
|
<file alias="plus">../images/plus_8.png</file>
|
||||||
<file alias="minus">../images/minus_8.png</file>
|
<file alias="minus">../images/minus_8.png</file>
|
||||||
<file alias="search_clear_13">../qtlib/images/search_clear_13.png</file>
|
<file alias="search_clear_13">../qtlib/images/search_clear_13.png</file>
|
||||||
|
<file alias="exchange">../images/exchange_purple_upscaled.png</file>
|
||||||
|
<file alias="zoom_in">../images/old_zoom_in.png</file>
|
||||||
|
<file alias="zoom_out">../images/old_zoom_out.png</file>
|
||||||
|
<file alias="zoom_original">../images/old_zoom_original.png</file>
|
||||||
|
<file alias="zoom_best_fit">../images/old_zoom_best_fit.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
# http://www.gnu.org/licenses/gpl-3.0.html
|
# http://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
|
||||||
from PyQt5.QtCore import QSize
|
from PyQt5.QtCore import QSize
|
||||||
from PyQt5.QtWidgets import QVBoxLayout, QAbstractItemView
|
from PyQt5.QtWidgets import QAbstractItemView
|
||||||
|
|
||||||
from hscommon.trans import trget
|
from hscommon.trans import trget
|
||||||
from ..details_dialog import DetailsDialog as DetailsDialogBase
|
from ..details_dialog import DetailsDialog as DetailsDialogBase
|
||||||
@ -19,11 +19,8 @@ class DetailsDialog(DetailsDialogBase):
|
|||||||
self.setWindowTitle(tr("Details"))
|
self.setWindowTitle(tr("Details"))
|
||||||
self.resize(502, 295)
|
self.resize(502, 295)
|
||||||
self.setMinimumSize(QSize(250, 250))
|
self.setMinimumSize(QSize(250, 250))
|
||||||
self.verticalLayout = QVBoxLayout(self)
|
|
||||||
self.verticalLayout.setSpacing(0)
|
|
||||||
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
|
|
||||||
self.tableView = DetailsTable(self)
|
self.tableView = DetailsTable(self)
|
||||||
self.tableView.setAlternatingRowColors(True)
|
self.tableView.setAlternatingRowColors(True)
|
||||||
self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows)
|
self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||||
self.tableView.setShowGrid(False)
|
self.tableView.setShowGrid(False)
|
||||||
self.verticalLayout.addWidget(self.tableView)
|
self.setWidget(self.tableView)
|
||||||
|
@ -76,7 +76,7 @@ class PreferencesDialog(PreferencesDialogBase):
|
|||||||
self.widgetsVLayout.addWidget(self.debugModeBox)
|
self.widgetsVLayout.addWidget(self.debugModeBox)
|
||||||
self._setupBottomPart()
|
self._setupBottomPart()
|
||||||
|
|
||||||
def _load(self, prefs, setchecked):
|
def _load(self, prefs, setchecked, section):
|
||||||
setchecked(self.tagTrackBox, prefs.scan_tag_track)
|
setchecked(self.tagTrackBox, prefs.scan_tag_track)
|
||||||
setchecked(self.tagArtistBox, prefs.scan_tag_artist)
|
setchecked(self.tagArtistBox, prefs.scan_tag_artist)
|
||||||
setchecked(self.tagAlbumBox, prefs.scan_tag_album)
|
setchecked(self.tagAlbumBox, prefs.scan_tag_album)
|
||||||
|
@ -4,115 +4,143 @@
|
|||||||
# 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.gnu.org/licenses/gpl-3.0.html
|
# http://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, QSize
|
from PyQt5.QtCore import Qt, QSize, pyqtSignal, pyqtSlot
|
||||||
from PyQt5.QtGui import QPixmap
|
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QVBoxLayout,
|
QAbstractItemView, QSizePolicy, QGridLayout, QSplitter, QFrame)
|
||||||
QAbstractItemView,
|
from PyQt5.QtGui import QResizeEvent
|
||||||
QHBoxLayout,
|
|
||||||
QLabel,
|
|
||||||
QSizePolicy,
|
|
||||||
)
|
|
||||||
|
|
||||||
from hscommon.trans import trget
|
from hscommon.trans import trget
|
||||||
|
from hscommon.plat import ISWINDOWS
|
||||||
from ..details_dialog import DetailsDialog as DetailsDialogBase
|
from ..details_dialog import DetailsDialog as DetailsDialogBase
|
||||||
from ..details_table import DetailsTable
|
from ..details_table import DetailsTable
|
||||||
|
from .image_viewer import (
|
||||||
|
ViewerToolBar, ScrollAreaImageViewer, ScrollAreaController)
|
||||||
tr = trget("ui")
|
tr = trget("ui")
|
||||||
|
|
||||||
|
|
||||||
class DetailsDialog(DetailsDialogBase):
|
class DetailsDialog(DetailsDialogBase):
|
||||||
def __init__(self, parent, app):
|
def __init__(self, parent, app):
|
||||||
DetailsDialogBase.__init__(self, parent, app)
|
self.vController = None
|
||||||
self.selectedPixmap = None
|
self.app = app
|
||||||
self.referencePixmap = None
|
super().__init__(parent, app)
|
||||||
|
|
||||||
def _setupUi(self):
|
def _setupUi(self):
|
||||||
self.setWindowTitle(tr("Details"))
|
self.setWindowTitle(tr("Details"))
|
||||||
self.resize(502, 295)
|
self.resize(502, 502)
|
||||||
self.setMinimumSize(QSize(250, 250))
|
self.setMinimumSize(QSize(250, 250))
|
||||||
self.verticalLayout = QVBoxLayout(self)
|
self.splitter = QSplitter(Qt.Vertical)
|
||||||
self.verticalLayout.setSpacing(0)
|
self.topFrame = EmittingFrame()
|
||||||
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
|
self.topFrame.setFrameShape(QFrame.StyledPanel)
|
||||||
self.horizontalLayout = QHBoxLayout()
|
self.horizontalLayout = QGridLayout()
|
||||||
self.horizontalLayout.setSpacing(4)
|
# Minimum width for the toolbar in the middle:
|
||||||
self.selectedImage = QLabel(self)
|
self.horizontalLayout.setColumnMinimumWidth(1, 10)
|
||||||
sizePolicy = QSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
|
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
self.horizontalLayout.setColumnStretch(0, 32)
|
||||||
sizePolicy.setVerticalStretch(0)
|
# Smaller value for the toolbar in the middle to avoid excessive resize
|
||||||
sizePolicy.setHeightForWidth(
|
self.horizontalLayout.setColumnStretch(1, 2)
|
||||||
self.selectedImage.sizePolicy().hasHeightForWidth()
|
self.horizontalLayout.setColumnStretch(2, 32)
|
||||||
)
|
# This avoids toolbar getting incorrectly partially hidden when window resizes
|
||||||
self.selectedImage.setSizePolicy(sizePolicy)
|
self.horizontalLayout.setRowStretch(0, 1)
|
||||||
self.selectedImage.setScaledContents(False)
|
self.horizontalLayout.setRowStretch(1, 24)
|
||||||
self.selectedImage.setAlignment(Qt.AlignCenter)
|
self.horizontalLayout.setRowStretch(2, 1)
|
||||||
self.horizontalLayout.addWidget(self.selectedImage)
|
self.horizontalLayout.setSpacing(1) # probably not important
|
||||||
self.referenceImage = QLabel(self)
|
|
||||||
sizePolicy = QSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
|
self.selectedImageViewer = ScrollAreaImageViewer(self, "selectedImage")
|
||||||
sizePolicy.setHorizontalStretch(0)
|
self.horizontalLayout.addWidget(self.selectedImageViewer, 0, 0, 3, 1)
|
||||||
sizePolicy.setVerticalStretch(0)
|
# Use a specific type of controller depending on the underlying viewer type
|
||||||
sizePolicy.setHeightForWidth(
|
self.vController = ScrollAreaController(self)
|
||||||
self.referenceImage.sizePolicy().hasHeightForWidth()
|
|
||||||
)
|
self.verticalToolBar = ViewerToolBar(self, self.vController)
|
||||||
self.referenceImage.setSizePolicy(sizePolicy)
|
self.verticalToolBar.setOrientation(Qt.Orientation(Qt.Vertical))
|
||||||
self.referenceImage.setAlignment(Qt.AlignCenter)
|
self.horizontalLayout.addWidget(self.verticalToolBar, 1, 1, 1, 1, Qt.AlignCenter)
|
||||||
self.horizontalLayout.addWidget(self.referenceImage)
|
|
||||||
self.verticalLayout.addLayout(self.horizontalLayout)
|
self.referenceImageViewer = ScrollAreaImageViewer(self, "referenceImage")
|
||||||
|
self.horizontalLayout.addWidget(self.referenceImageViewer, 0, 2, 3, 1)
|
||||||
|
self.topFrame.setLayout(self.horizontalLayout)
|
||||||
|
self.splitter.addWidget(self.topFrame)
|
||||||
|
self.splitter.setStretchFactor(0, 8)
|
||||||
|
|
||||||
self.tableView = DetailsTable(self)
|
self.tableView = DetailsTable(self)
|
||||||
sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
sizePolicy.setHeightForWidth(self.tableView.sizePolicy().hasHeightForWidth())
|
|
||||||
self.tableView.setSizePolicy(sizePolicy)
|
self.tableView.setSizePolicy(sizePolicy)
|
||||||
self.tableView.setMinimumSize(QSize(0, 188))
|
# self.tableView.setMinimumSize(QSize(0, 190))
|
||||||
self.tableView.setMaximumSize(QSize(16777215, 190))
|
# self.tableView.setMaximumSize(QSize(16777215, 190))
|
||||||
self.tableView.setAlternatingRowColors(True)
|
self.tableView.setAlternatingRowColors(True)
|
||||||
self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows)
|
self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||||
self.tableView.setShowGrid(False)
|
self.tableView.setShowGrid(False)
|
||||||
self.verticalLayout.addWidget(self.tableView)
|
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.setCentralWidget(self.splitter) # only as QMainWindow
|
||||||
|
self.setWidget(self.splitter) # only as QDockWidget
|
||||||
|
|
||||||
|
self.topFrame.resized.connect(self.resizeEvent)
|
||||||
|
|
||||||
def _update(self):
|
def _update(self):
|
||||||
|
if self.vController is None: # Not yet constructed!
|
||||||
|
return
|
||||||
if not self.app.model.selected_dupes:
|
if not self.app.model.selected_dupes:
|
||||||
|
# No item from the model, disable and clear everything.
|
||||||
|
self.vController.resetViewersState()
|
||||||
return
|
return
|
||||||
dupe = self.app.model.selected_dupes[0]
|
dupe = self.app.model.selected_dupes[0]
|
||||||
group = self.app.model.results.get_group_of_duplicate(dupe)
|
group = self.app.model.results.get_group_of_duplicate(dupe)
|
||||||
ref = group.ref
|
ref = group.ref
|
||||||
|
|
||||||
self.selectedPixmap = QPixmap(str(dupe.path))
|
self.vController.updateView(ref, dupe, group)
|
||||||
if ref is dupe:
|
|
||||||
self.referencePixmap = None
|
|
||||||
else:
|
|
||||||
self.referencePixmap = QPixmap(str(ref.path))
|
|
||||||
self._updateImages()
|
|
||||||
|
|
||||||
def _updateImages(self):
|
|
||||||
if self.selectedPixmap is not None:
|
|
||||||
target_size = self.selectedImage.size()
|
|
||||||
scaledPixmap = self.selectedPixmap.scaled(
|
|
||||||
target_size, Qt.KeepAspectRatio, Qt.SmoothTransformation
|
|
||||||
)
|
|
||||||
self.selectedImage.setPixmap(scaledPixmap)
|
|
||||||
else:
|
|
||||||
self.selectedImage.setPixmap(QPixmap())
|
|
||||||
if self.referencePixmap is not None:
|
|
||||||
target_size = self.referenceImage.size()
|
|
||||||
scaledPixmap = self.referencePixmap.scaled(
|
|
||||||
target_size, Qt.KeepAspectRatio, Qt.SmoothTransformation
|
|
||||||
)
|
|
||||||
self.referenceImage.setPixmap(scaledPixmap)
|
|
||||||
else:
|
|
||||||
self.referenceImage.setPixmap(QPixmap())
|
|
||||||
|
|
||||||
# --- Override
|
# --- Override
|
||||||
|
@pyqtSlot(QResizeEvent)
|
||||||
def resizeEvent(self, event):
|
def resizeEvent(self, event):
|
||||||
self._updateImages()
|
self.ensure_same_sizes()
|
||||||
|
if self.vController is None or not self.vController.bestFit:
|
||||||
|
return
|
||||||
|
# Only update the scaled down pixmaps
|
||||||
|
self.vController.updateBothImages()
|
||||||
|
|
||||||
def show(self):
|
def show(self):
|
||||||
|
# Compute the maximum size the table view can reach
|
||||||
|
# Assuming all rows below headers have the same height
|
||||||
|
self.tableView.setMaximumHeight(
|
||||||
|
self.tableView.rowHeight(1)
|
||||||
|
* self.tableModel.model.row_count()
|
||||||
|
+ self.tableView.verticalHeader().sectionSize(0)
|
||||||
|
# Windows seems to add a few pixels more to the table somehow
|
||||||
|
+ (5 if ISWINDOWS else 0))
|
||||||
DetailsDialogBase.show(self)
|
DetailsDialogBase.show(self)
|
||||||
|
self.ensure_same_sizes()
|
||||||
self._update()
|
self._update()
|
||||||
|
|
||||||
|
def ensure_same_sizes(self):
|
||||||
|
# HACK This ensures same size while shrinking.
|
||||||
|
# ReferenceViewer might be 1 pixel shorter in width
|
||||||
|
# due to the toolbar in the middle keeping the same width,
|
||||||
|
# so resizing in the GridLayout's engine leads to not enough space
|
||||||
|
# left for the panel on the right.
|
||||||
|
# This work as a QMainWindow, but doesn't work as a QDockWidget:
|
||||||
|
# resize can only grow. Might need some custom sizeHint somewhere...
|
||||||
|
# self.horizontalLayout.setColumnMinimumWidth(
|
||||||
|
# 0, self.selectedImageViewer.size().width())
|
||||||
|
# self.horizontalLayout.setColumnMinimumWidth(
|
||||||
|
# 2, self.selectedImageViewer.size().width())
|
||||||
|
|
||||||
|
# This works when expanding but it's ugly:
|
||||||
|
if self.selectedImageViewer.size().width() > self.referenceImageViewer.size().width():
|
||||||
|
self.selectedImageViewer.resize(self.referenceImageViewer.size())
|
||||||
|
|
||||||
# model --> view
|
# model --> view
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
DetailsDialogBase.refresh(self)
|
DetailsDialogBase.refresh(self)
|
||||||
if self.isVisible():
|
if self.isVisible():
|
||||||
self._update()
|
self._update()
|
||||||
|
|
||||||
|
|
||||||
|
class EmittingFrame(QFrame):
|
||||||
|
"""Emits a signal whenever is resized"""
|
||||||
|
resized = pyqtSignal(QResizeEvent)
|
||||||
|
|
||||||
|
def resizeEvent(self, event):
|
||||||
|
self.resized.emit(event)
|
||||||
|
1370
qt/pe/image_viewer.py
Normal file
@ -4,8 +4,10 @@
|
|||||||
# 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.gnu.org/licenses/gpl-3.0.html
|
# http://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
|
||||||
from PyQt5.QtWidgets import QLabel
|
from PyQt5.QtWidgets import QFormLayout
|
||||||
|
from PyQt5.QtCore import Qt
|
||||||
from hscommon.trans import trget
|
from hscommon.trans import trget
|
||||||
|
from hscommon.plat import ISLINUX
|
||||||
from qtlib.radio_box import RadioBox
|
from qtlib.radio_box import RadioBox
|
||||||
from core.scanner import ScanType
|
from core.scanner import ScanType
|
||||||
from core.app import AppMode
|
from core.app import AppMode
|
||||||
@ -40,12 +42,35 @@ class PreferencesDialog(PreferencesDialogBase):
|
|||||||
self.widgetsVLayout.addWidget(self.ignoreHardlinkMatches)
|
self.widgetsVLayout.addWidget(self.ignoreHardlinkMatches)
|
||||||
self._setupAddCheckbox("debugModeBox", tr("Debug mode (restart required)"))
|
self._setupAddCheckbox("debugModeBox", tr("Debug mode (restart required)"))
|
||||||
self.widgetsVLayout.addWidget(self.debugModeBox)
|
self.widgetsVLayout.addWidget(self.debugModeBox)
|
||||||
self.widgetsVLayout.addWidget(QLabel(tr("Picture cache mode:")))
|
|
||||||
self.cacheTypeRadio = RadioBox(self, items=["Sqlite", "Shelve"], spread=False)
|
self.cacheTypeRadio = RadioBox(self, items=["Sqlite", "Shelve"], spread=False)
|
||||||
self.widgetsVLayout.addWidget(self.cacheTypeRadio)
|
cache_form = QFormLayout()
|
||||||
|
cache_form.setLabelAlignment(Qt.AlignLeft)
|
||||||
|
cache_form.addRow(tr("Picture cache mode:"), self.cacheTypeRadio)
|
||||||
|
self.widgetsVLayout.addLayout(cache_form)
|
||||||
self._setupBottomPart()
|
self._setupBottomPart()
|
||||||
|
|
||||||
def _load(self, prefs, setchecked):
|
def _setupDisplayPage(self):
|
||||||
|
super()._setupDisplayPage()
|
||||||
|
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"))
|
||||||
|
# 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_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)
|
||||||
|
|
||||||
|
def _load(self, prefs, setchecked, section):
|
||||||
setchecked(self.matchScaledBox, prefs.match_scaled)
|
setchecked(self.matchScaledBox, prefs.match_scaled)
|
||||||
self.cacheTypeRadio.selected_index = (
|
self.cacheTypeRadio.selected_index = (
|
||||||
1 if prefs.picture_cache_type == "shelve" else 0
|
1 if prefs.picture_cache_type == "shelve" else 0
|
||||||
@ -55,9 +80,17 @@ class PreferencesDialog(PreferencesDialogBase):
|
|||||||
scan_type = prefs.get_scan_type(AppMode.Picture)
|
scan_type = prefs.get_scan_type(AppMode.Picture)
|
||||||
fuzzy_scan = scan_type == ScanType.FuzzyBlock
|
fuzzy_scan = scan_type == ScanType.FuzzyBlock
|
||||||
self.filterHardnessSlider.setEnabled(fuzzy_scan)
|
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)
|
||||||
|
|
||||||
def _save(self, prefs, ischecked):
|
def _save(self, prefs, ischecked):
|
||||||
prefs.match_scaled = ischecked(self.matchScaledBox)
|
prefs.match_scaled = ischecked(self.matchScaledBox)
|
||||||
prefs.picture_cache_type = (
|
prefs.picture_cache_type = (
|
||||||
"shelve" if self.cacheTypeRadio.selected_index == 1 else "sqlite"
|
"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)
|
||||||
|
@ -5,8 +5,11 @@
|
|||||||
# http://www.gnu.org/licenses/gpl-3.0.html
|
# http://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
|
||||||
from PyQt5.QtWidgets import QApplication
|
from PyQt5.QtWidgets import QApplication
|
||||||
|
from PyQt5.QtCore import Qt
|
||||||
|
from PyQt5.QtGui import QColor
|
||||||
|
|
||||||
from hscommon import trans
|
from hscommon import trans
|
||||||
|
from hscommon.plat import ISLINUX
|
||||||
from core.app import AppMode
|
from core.app import AppMode
|
||||||
from core.scanner import ScanType
|
from core.scanner import ScanType
|
||||||
from qtlib.preferences import Preferences as PreferencesBase
|
from qtlib.preferences import Preferences as PreferencesBase
|
||||||
@ -30,7 +33,25 @@ class Preferences(PreferencesBase):
|
|||||||
self.language = trans.installed_lang
|
self.language = trans.installed_lang
|
||||||
|
|
||||||
self.tableFontSize = get("TableFontSize", self.tableFontSize)
|
self.tableFontSize = get("TableFontSize", self.tableFontSize)
|
||||||
self.reference_bold_font = get('ReferenceBoldFont', self.reference_bold_font)
|
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)
|
||||||
|
# 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_delta_foreground_color =\
|
||||||
|
get("ResultTableDeltaForegroundColor", self.result_table_delta_foreground_color)
|
||||||
|
|
||||||
self.resultWindowIsMaximized = get(
|
self.resultWindowIsMaximized = get(
|
||||||
"ResultWindowIsMaximized", self.resultWindowIsMaximized
|
"ResultWindowIsMaximized", self.resultWindowIsMaximized
|
||||||
)
|
)
|
||||||
@ -72,6 +93,14 @@ class Preferences(PreferencesBase):
|
|||||||
|
|
||||||
self.tableFontSize = QApplication.font().pointSize()
|
self.tableFontSize = QApplication.font().pointSize()
|
||||||
self.reference_bold_font = True
|
self.reference_bold_font = True
|
||||||
|
self.details_dialog_titlebar_enabled = True
|
||||||
|
self.details_dialog_vertical_titlebar = True
|
||||||
|
self.details_table_delta_foreground_color = QColor(250, 20, 20) # red
|
||||||
|
# By default use internal icons on platforms other than Linux for now
|
||||||
|
self.details_dialog_override_theme_icons = False if not ISLINUX else True
|
||||||
|
self.details_dialog_viewers_show_scrollbars = True
|
||||||
|
self.result_table_ref_foreground_color = QColor(Qt.blue)
|
||||||
|
self.result_table_delta_foreground_color = QColor(255, 142, 40) # orange
|
||||||
self.resultWindowIsMaximized = False
|
self.resultWindowIsMaximized = False
|
||||||
self.resultWindowRect = None
|
self.resultWindowRect = None
|
||||||
self.directoriesWindowRect = None
|
self.directoriesWindowRect = None
|
||||||
@ -107,7 +136,14 @@ class Preferences(PreferencesBase):
|
|||||||
set_("Language", self.language)
|
set_("Language", self.language)
|
||||||
|
|
||||||
set_("TableFontSize", self.tableFontSize)
|
set_("TableFontSize", self.tableFontSize)
|
||||||
set_('ReferenceBoldFont', self.reference_bold_font)
|
set_("ReferenceBoldFont", self.reference_bold_font)
|
||||||
|
set_("DetailsDialogTitleBarEnabled", self.details_dialog_titlebar_enabled)
|
||||||
|
set_("DetailsDialogVerticalTitleBar", self.details_dialog_vertical_titlebar)
|
||||||
|
set_("DetailsDialogOverrideThemeIcons", self.details_dialog_override_theme_icons)
|
||||||
|
set_("DetailsDialogViewersShowScrollbars", self.details_dialog_viewers_show_scrollbars)
|
||||||
|
set_("DetailsTableDeltaForegroundColor", self.details_table_delta_foreground_color)
|
||||||
|
set_("ResultTableRefForegroundColor", self.result_table_ref_foreground_color)
|
||||||
|
set_("ResultTableDeltaForegroundColor", self.result_table_delta_foreground_color)
|
||||||
set_("ResultWindowIsMaximized", self.resultWindowIsMaximized)
|
set_("ResultWindowIsMaximized", self.resultWindowIsMaximized)
|
||||||
set_("MainWindowIsMaximized", self.mainWindowIsMaximized)
|
set_("MainWindowIsMaximized", self.mainWindowIsMaximized)
|
||||||
self.set_rect("ResultWindowRect", self.resultWindowRect)
|
self.set_rect("ResultWindowRect", self.resultWindowRect)
|
||||||
|
@ -4,12 +4,13 @@
|
|||||||
# 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.gnu.org/licenses/gpl-3.0.html
|
# http://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, QSize
|
from PyQt5.QtCore import Qt, QSize, pyqtSlot
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QDialog,
|
QDialog,
|
||||||
QDialogButtonBox,
|
QDialogButtonBox,
|
||||||
QVBoxLayout,
|
QVBoxLayout,
|
||||||
QHBoxLayout,
|
QHBoxLayout,
|
||||||
|
QGridLayout,
|
||||||
QLabel,
|
QLabel,
|
||||||
QComboBox,
|
QComboBox,
|
||||||
QSlider,
|
QSlider,
|
||||||
@ -20,11 +21,20 @@ from PyQt5.QtWidgets import (
|
|||||||
QMessageBox,
|
QMessageBox,
|
||||||
QSpinBox,
|
QSpinBox,
|
||||||
QLayout,
|
QLayout,
|
||||||
|
QTabWidget,
|
||||||
|
QWidget,
|
||||||
|
QColorDialog,
|
||||||
|
QPushButton,
|
||||||
|
QGroupBox,
|
||||||
|
QFormLayout,
|
||||||
)
|
)
|
||||||
|
from PyQt5.QtGui import QPixmap, QIcon
|
||||||
|
|
||||||
from hscommon.trans import trget
|
from hscommon.trans import trget
|
||||||
|
from hscommon.plat import ISLINUX
|
||||||
from qtlib.util import horizontalWrap
|
from qtlib.util import horizontalWrap
|
||||||
from qtlib.preferences import get_langnames
|
from qtlib.preferences import get_langnames
|
||||||
|
from enum import Flag, auto
|
||||||
|
|
||||||
from .preferences import Preferences
|
from .preferences import Preferences
|
||||||
|
|
||||||
@ -50,6 +60,13 @@ SUPPORTED_LANGUAGES = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Sections(Flag):
|
||||||
|
"""Filter blocks of preferences when reset or loaded"""
|
||||||
|
GENERAL = auto()
|
||||||
|
DISPLAY = auto()
|
||||||
|
ALL = GENERAL | DISPLAY
|
||||||
|
|
||||||
|
|
||||||
class PreferencesDialogBase(QDialog):
|
class PreferencesDialogBase(QDialog):
|
||||||
def __init__(self, parent, app, **kwargs):
|
def __init__(self, parent, app, **kwargs):
|
||||||
flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint
|
flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint
|
||||||
@ -111,25 +128,6 @@ class PreferencesDialogBase(QDialog):
|
|||||||
|
|
||||||
def _setupBottomPart(self):
|
def _setupBottomPart(self):
|
||||||
# The bottom part of the pref panel is always the same in all editions.
|
# The bottom part of the pref panel is always the same in all editions.
|
||||||
self.fontSizeLabel = QLabel(tr("Font size:"))
|
|
||||||
self.fontSizeSpinBox = QSpinBox()
|
|
||||||
self.fontSizeSpinBox.setMinimum(5)
|
|
||||||
self.widgetsVLayout.addLayout(
|
|
||||||
horizontalWrap([self.fontSizeLabel, self.fontSizeSpinBox, None])
|
|
||||||
)
|
|
||||||
self._setupAddCheckbox("reference_bold_font", tr("Bold font for reference"))
|
|
||||||
self.widgetsVLayout.addWidget(self.reference_bold_font)
|
|
||||||
self._setupAddCheckbox("tabs_default_pos", tr("Use default position for tab bar (requires restart)"))
|
|
||||||
self.tabs_default_pos.setToolTip(tr("Place the tab bar below the main menu instead of next to it"))
|
|
||||||
self.widgetsVLayout.addWidget(self.tabs_default_pos)
|
|
||||||
|
|
||||||
self.languageLabel = QLabel(tr("Language:"), self)
|
|
||||||
self.languageComboBox = QComboBox(self)
|
|
||||||
for lang in self.supportedLanguages:
|
|
||||||
self.languageComboBox.addItem(get_langnames()[lang])
|
|
||||||
self.widgetsVLayout.addLayout(
|
|
||||||
horizontalWrap([self.languageLabel, self.languageComboBox, None])
|
|
||||||
)
|
|
||||||
self.copyMoveLabel = QLabel(self)
|
self.copyMoveLabel = QLabel(self)
|
||||||
self.copyMoveLabel.setText(tr("Copy and Move:"))
|
self.copyMoveLabel.setText(tr("Copy and Move:"))
|
||||||
self.widgetsVLayout.addWidget(self.copyMoveLabel)
|
self.widgetsVLayout.addWidget(self.copyMoveLabel)
|
||||||
@ -146,6 +144,74 @@ class PreferencesDialogBase(QDialog):
|
|||||||
self.customCommandEdit = QLineEdit(self)
|
self.customCommandEdit = QLineEdit(self)
|
||||||
self.widgetsVLayout.addWidget(self.customCommandEdit)
|
self.widgetsVLayout.addWidget(self.customCommandEdit)
|
||||||
|
|
||||||
|
def _setupDisplayPage(self):
|
||||||
|
self.ui_groupbox = QGroupBox("&General Interface")
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
self.languageLabel = QLabel(tr("Language:"), self)
|
||||||
|
self.languageComboBox = QComboBox(self)
|
||||||
|
for lang in self.supportedLanguages:
|
||||||
|
self.languageComboBox.addItem(get_langnames()[lang])
|
||||||
|
layout.addLayout(horizontalWrap([self.languageLabel, self.languageComboBox, None]))
|
||||||
|
self._setupAddCheckbox("tabs_default_pos",
|
||||||
|
tr("Use default position for tab bar (requires restart)"))
|
||||||
|
self.tabs_default_pos.setToolTip(
|
||||||
|
tr("Place the tab bar below the main menu instead of next to it\n\
|
||||||
|
On MacOS, the tab bar will fill up the window's width instead."))
|
||||||
|
layout.addWidget(self.tabs_default_pos)
|
||||||
|
self.ui_groupbox.setLayout(layout)
|
||||||
|
self.displayVLayout.addWidget(self.ui_groupbox)
|
||||||
|
|
||||||
|
gridlayout = QFormLayout()
|
||||||
|
result_groupbox = QGroupBox("&Result Table")
|
||||||
|
self.fontSizeSpinBox = QSpinBox()
|
||||||
|
self.fontSizeSpinBox.setMinimum(5)
|
||||||
|
gridlayout.addRow(tr("Font size:"), self.fontSizeSpinBox)
|
||||||
|
self._setupAddCheckbox("reference_bold_font",
|
||||||
|
tr("Use bold font for references"))
|
||||||
|
gridlayout.addRow(self.reference_bold_font)
|
||||||
|
|
||||||
|
self.result_table_ref_foreground_color = ColorPickerButton(self)
|
||||||
|
gridlayout.addRow(tr("Reference foreground color:"),
|
||||||
|
self.result_table_ref_foreground_color)
|
||||||
|
self.result_table_delta_foreground_color = ColorPickerButton(self)
|
||||||
|
gridlayout.addRow(tr("Delta foreground color:"),
|
||||||
|
self.result_table_delta_foreground_color)
|
||||||
|
gridlayout.setLabelAlignment(Qt.AlignLeft)
|
||||||
|
|
||||||
|
# Keep same vertical spacing as parent layout for consistency
|
||||||
|
gridlayout.setVerticalSpacing(self.displayVLayout.spacing())
|
||||||
|
result_groupbox.setLayout(gridlayout)
|
||||||
|
self.displayVLayout.addWidget(result_groupbox)
|
||||||
|
|
||||||
|
details_groupbox = QGroupBox("&Details Window")
|
||||||
|
self.details_groupbox_layout = QVBoxLayout()
|
||||||
|
self._setupAddCheckbox("details_dialog_titlebar_enabled",
|
||||||
|
tr("Show the title bar and can be docked"))
|
||||||
|
self.details_dialog_titlebar_enabled.setToolTip(
|
||||||
|
tr("While the title bar is hidden, \
|
||||||
|
use the modifier key to drag the floating window around") if ISLINUX else
|
||||||
|
tr("The title bar can only be disabled while the window is docked"))
|
||||||
|
self.details_groupbox_layout.addWidget(self.details_dialog_titlebar_enabled)
|
||||||
|
self._setupAddCheckbox("details_dialog_vertical_titlebar",
|
||||||
|
tr("Vertical title bar"))
|
||||||
|
self.details_dialog_vertical_titlebar.setToolTip(
|
||||||
|
tr("Change the title bar from horizontal on top, to vertical on the left side"))
|
||||||
|
self.details_groupbox_layout.addWidget(self.details_dialog_vertical_titlebar)
|
||||||
|
self.details_dialog_vertical_titlebar.setEnabled(
|
||||||
|
self.details_dialog_titlebar_enabled.isChecked())
|
||||||
|
self.details_dialog_titlebar_enabled.stateChanged.connect(
|
||||||
|
self.details_dialog_vertical_titlebar.setEnabled)
|
||||||
|
gridlayout = QGridLayout()
|
||||||
|
self.details_table_delta_foreground_color_label = QLabel(tr("Delta foreground color:"))
|
||||||
|
gridlayout.addWidget(self.details_table_delta_foreground_color_label, 4, 0)
|
||||||
|
self.details_table_delta_foreground_color = ColorPickerButton(self)
|
||||||
|
gridlayout.addWidget(self.details_table_delta_foreground_color, 4, 2, 1, 1, Qt.AlignLeft)
|
||||||
|
gridlayout.setColumnStretch(1, 1)
|
||||||
|
gridlayout.setColumnStretch(3, 4)
|
||||||
|
self.details_groupbox_layout.addLayout(gridlayout)
|
||||||
|
details_groupbox.setLayout(self.details_groupbox_layout)
|
||||||
|
self.displayVLayout.addWidget(details_groupbox)
|
||||||
|
|
||||||
def _setupAddCheckbox(self, name, label, parent=None):
|
def _setupAddCheckbox(self, name, label, parent=None):
|
||||||
if parent is None:
|
if parent is None:
|
||||||
parent = self
|
parent = self
|
||||||
@ -162,19 +228,32 @@ class PreferencesDialogBase(QDialog):
|
|||||||
self.setSizeGripEnabled(False)
|
self.setSizeGripEnabled(False)
|
||||||
self.setModal(True)
|
self.setModal(True)
|
||||||
self.mainVLayout = QVBoxLayout(self)
|
self.mainVLayout = QVBoxLayout(self)
|
||||||
|
self.tabwidget = QTabWidget()
|
||||||
|
self.page_general = QWidget()
|
||||||
|
self.page_display = QWidget()
|
||||||
self.widgetsVLayout = QVBoxLayout()
|
self.widgetsVLayout = QVBoxLayout()
|
||||||
|
self.page_general.setLayout(self.widgetsVLayout)
|
||||||
|
self.displayVLayout = QVBoxLayout()
|
||||||
|
self.displayVLayout.setSpacing(5) # arbitrary value, might conflict with style
|
||||||
|
self.page_display.setLayout(self.displayVLayout)
|
||||||
self._setupPreferenceWidgets()
|
self._setupPreferenceWidgets()
|
||||||
self.mainVLayout.addLayout(self.widgetsVLayout)
|
self._setupDisplayPage()
|
||||||
|
# self.mainVLayout.addLayout(self.widgetsVLayout)
|
||||||
self.buttonBox = QDialogButtonBox(self)
|
self.buttonBox = QDialogButtonBox(self)
|
||||||
self.buttonBox.setStandardButtons(
|
self.buttonBox.setStandardButtons(
|
||||||
QDialogButtonBox.Cancel
|
QDialogButtonBox.Cancel
|
||||||
| QDialogButtonBox.Ok
|
| QDialogButtonBox.Ok
|
||||||
| QDialogButtonBox.RestoreDefaults
|
| QDialogButtonBox.RestoreDefaults
|
||||||
)
|
)
|
||||||
|
self.mainVLayout.addWidget(self.tabwidget)
|
||||||
self.mainVLayout.addWidget(self.buttonBox)
|
self.mainVLayout.addWidget(self.buttonBox)
|
||||||
self.layout().setSizeConstraint(QLayout.SetFixedSize)
|
self.layout().setSizeConstraint(QLayout.SetFixedSize)
|
||||||
|
self.tabwidget.addTab(self.page_general, "General")
|
||||||
|
self.tabwidget.addTab(self.page_display, "Display")
|
||||||
|
self.displayVLayout.addStretch(0)
|
||||||
|
self.widgetsVLayout.addStretch(0)
|
||||||
|
|
||||||
def _load(self, prefs, setchecked):
|
def _load(self, prefs, setchecked, section):
|
||||||
# Edition-specific
|
# Edition-specific
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -182,28 +261,40 @@ class PreferencesDialogBase(QDialog):
|
|||||||
# Edition-specific
|
# Edition-specific
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def load(self, prefs=None):
|
def load(self, prefs=None, section=Sections.ALL):
|
||||||
if prefs is None:
|
if prefs is None:
|
||||||
prefs = self.app.prefs
|
prefs = self.app.prefs
|
||||||
|
setchecked = lambda cb, b: cb.setCheckState(Qt.Checked if b else Qt.Unchecked)
|
||||||
|
if section & Sections.GENERAL:
|
||||||
self.filterHardnessSlider.setValue(prefs.filter_hardness)
|
self.filterHardnessSlider.setValue(prefs.filter_hardness)
|
||||||
self.filterHardnessLabel.setNum(prefs.filter_hardness)
|
self.filterHardnessLabel.setNum(prefs.filter_hardness)
|
||||||
setchecked = lambda cb, b: cb.setCheckState(Qt.Checked if b else Qt.Unchecked)
|
|
||||||
setchecked(self.mixFileKindBox, prefs.mix_file_kind)
|
setchecked(self.mixFileKindBox, prefs.mix_file_kind)
|
||||||
setchecked(self.useRegexpBox, prefs.use_regexp)
|
setchecked(self.useRegexpBox, prefs.use_regexp)
|
||||||
setchecked(self.removeEmptyFoldersBox, prefs.remove_empty_folders)
|
setchecked(self.removeEmptyFoldersBox, prefs.remove_empty_folders)
|
||||||
setchecked(self.ignoreHardlinkMatches, prefs.ignore_hardlink_matches)
|
setchecked(self.ignoreHardlinkMatches, prefs.ignore_hardlink_matches)
|
||||||
setchecked(self.debugModeBox, prefs.debug_mode)
|
setchecked(self.debugModeBox, prefs.debug_mode)
|
||||||
setchecked(self.reference_bold_font, prefs.reference_bold_font)
|
|
||||||
setchecked(self.tabs_default_pos, prefs.tabs_default_pos)
|
|
||||||
self.copyMoveDestinationComboBox.setCurrentIndex(prefs.destination_type)
|
self.copyMoveDestinationComboBox.setCurrentIndex(prefs.destination_type)
|
||||||
self.customCommandEdit.setText(prefs.custom_command)
|
self.customCommandEdit.setText(prefs.custom_command)
|
||||||
|
if section & Sections.DISPLAY:
|
||||||
|
setchecked(self.reference_bold_font, prefs.reference_bold_font)
|
||||||
|
setchecked(self.tabs_default_pos, prefs.tabs_default_pos)
|
||||||
|
setchecked(self.details_dialog_titlebar_enabled,
|
||||||
|
prefs.details_dialog_titlebar_enabled)
|
||||||
|
setchecked(self.details_dialog_vertical_titlebar,
|
||||||
|
prefs.details_dialog_vertical_titlebar)
|
||||||
self.fontSizeSpinBox.setValue(prefs.tableFontSize)
|
self.fontSizeSpinBox.setValue(prefs.tableFontSize)
|
||||||
|
self.details_table_delta_foreground_color.setColor(
|
||||||
|
prefs.details_table_delta_foreground_color)
|
||||||
|
self.result_table_ref_foreground_color.setColor(
|
||||||
|
prefs.result_table_ref_foreground_color)
|
||||||
|
self.result_table_delta_foreground_color.setColor(
|
||||||
|
prefs.result_table_delta_foreground_color)
|
||||||
try:
|
try:
|
||||||
langindex = self.supportedLanguages.index(self.app.prefs.language)
|
langindex = self.supportedLanguages.index(self.app.prefs.language)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
langindex = 0
|
langindex = 0
|
||||||
self.languageComboBox.setCurrentIndex(langindex)
|
self.languageComboBox.setCurrentIndex(langindex)
|
||||||
self._load(prefs, setchecked)
|
self._load(prefs, setchecked, section)
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
prefs = self.app.prefs
|
prefs = self.app.prefs
|
||||||
@ -215,6 +306,11 @@ class PreferencesDialogBase(QDialog):
|
|||||||
prefs.ignore_hardlink_matches = ischecked(self.ignoreHardlinkMatches)
|
prefs.ignore_hardlink_matches = ischecked(self.ignoreHardlinkMatches)
|
||||||
prefs.debug_mode = ischecked(self.debugModeBox)
|
prefs.debug_mode = ischecked(self.debugModeBox)
|
||||||
prefs.reference_bold_font = ischecked(self.reference_bold_font)
|
prefs.reference_bold_font = ischecked(self.reference_bold_font)
|
||||||
|
prefs.details_dialog_titlebar_enabled = ischecked(self.details_dialog_titlebar_enabled)
|
||||||
|
prefs.details_dialog_vertical_titlebar = ischecked(self.details_dialog_vertical_titlebar)
|
||||||
|
prefs.details_table_delta_foreground_color = self.details_table_delta_foreground_color.color
|
||||||
|
prefs.result_table_ref_foreground_color = self.result_table_ref_foreground_color.color
|
||||||
|
prefs.result_table_delta_foreground_color = self.result_table_delta_foreground_color.color
|
||||||
prefs.destination_type = self.copyMoveDestinationComboBox.currentIndex()
|
prefs.destination_type = self.copyMoveDestinationComboBox.currentIndex()
|
||||||
prefs.custom_command = str(self.customCommandEdit.text())
|
prefs.custom_command = str(self.customCommandEdit.text())
|
||||||
prefs.tableFontSize = self.fontSizeSpinBox.value()
|
prefs.tableFontSize = self.fontSizeSpinBox.value()
|
||||||
@ -232,11 +328,45 @@ class PreferencesDialogBase(QDialog):
|
|||||||
self.app.prefs.language = lang
|
self.app.prefs.language = lang
|
||||||
self._save(prefs, ischecked)
|
self._save(prefs, ischecked)
|
||||||
|
|
||||||
def resetToDefaults(self):
|
def resetToDefaults(self, section_to_update):
|
||||||
self.load(Preferences())
|
self.load(Preferences(), section_to_update)
|
||||||
|
|
||||||
# --- Events
|
# --- Events
|
||||||
def buttonClicked(self, button):
|
def buttonClicked(self, button):
|
||||||
role = self.buttonBox.buttonRole(button)
|
role = self.buttonBox.buttonRole(button)
|
||||||
if role == QDialogButtonBox.ResetRole:
|
if role == QDialogButtonBox.ResetRole:
|
||||||
self.resetToDefaults()
|
current_tab = self.tabwidget.currentWidget()
|
||||||
|
section_to_update = Sections.ALL
|
||||||
|
if current_tab is self.page_general:
|
||||||
|
section_to_update = Sections.GENERAL
|
||||||
|
if current_tab is self.page_display:
|
||||||
|
section_to_update = Sections.DISPLAY
|
||||||
|
self.resetToDefaults(section_to_update)
|
||||||
|
|
||||||
|
|
||||||
|
class ColorPickerButton(QPushButton):
|
||||||
|
def __init__(self, parent):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.parent = parent
|
||||||
|
self.color = None
|
||||||
|
self.clicked.connect(self.onClicked)
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def onClicked(self):
|
||||||
|
color = QColorDialog.getColor(
|
||||||
|
self.color if self.color is not None else Qt.white,
|
||||||
|
self.parent)
|
||||||
|
self.setColor(color)
|
||||||
|
|
||||||
|
def setColor(self, color):
|
||||||
|
size = QSize(16, 16)
|
||||||
|
px = QPixmap(size)
|
||||||
|
if color is None:
|
||||||
|
size.width = 0
|
||||||
|
size.height = 0
|
||||||
|
elif not color.isValid():
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
self.color = color
|
||||||
|
px.fill(color)
|
||||||
|
self.setIcon(QIcon(px))
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
# http://www.gnu.org/licenses/gpl-3.0.html
|
# http://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, pyqtSignal, QModelIndex
|
from PyQt5.QtCore import Qt, pyqtSignal, QModelIndex
|
||||||
from PyQt5.QtGui import QBrush, QFont, QFontMetrics, QColor
|
from PyQt5.QtGui import QBrush, QFont, QFontMetrics
|
||||||
from PyQt5.QtWidgets import QTableView
|
from PyQt5.QtWidgets import QTableView
|
||||||
|
|
||||||
from qtlib.table import Table
|
from qtlib.table import Table
|
||||||
@ -20,7 +20,7 @@ class ResultsModel(Table):
|
|||||||
view.horizontalHeader().setSortIndicator(1, Qt.AscendingOrder)
|
view.horizontalHeader().setSortIndicator(1, Qt.AscendingOrder)
|
||||||
font = view.font()
|
font = view.font()
|
||||||
font.setPointSize(app.prefs.tableFontSize)
|
font.setPointSize(app.prefs.tableFontSize)
|
||||||
self.view.setFont(font)
|
view.setFont(font)
|
||||||
fm = QFontMetrics(font)
|
fm = QFontMetrics(font)
|
||||||
view.verticalHeader().setDefaultSectionSize(fm.height() + 2)
|
view.verticalHeader().setDefaultSectionSize(fm.height() + 2)
|
||||||
|
|
||||||
@ -37,9 +37,9 @@ class ResultsModel(Table):
|
|||||||
return data[column.name]
|
return data[column.name]
|
||||||
elif role == Qt.ForegroundRole:
|
elif role == Qt.ForegroundRole:
|
||||||
if row.isref:
|
if row.isref:
|
||||||
return QBrush(Qt.blue)
|
return QBrush(self.prefs.result_table_ref_foreground_color)
|
||||||
elif row.is_cell_delta(column.name):
|
elif row.is_cell_delta(column.name):
|
||||||
return QBrush(QColor(255, 142, 40)) # orange
|
return QBrush(self.prefs.result_table_delta_foreground_color)
|
||||||
elif role == Qt.FontRole:
|
elif role == Qt.FontRole:
|
||||||
font = QFont(self.view.font())
|
font = QFont(self.view.font())
|
||||||
if self.prefs.reference_bold_font:
|
if self.prefs.reference_bold_font:
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
# http://www.gnu.org/licenses/gpl-3.0.html
|
# http://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
|
||||||
from PyQt5.QtCore import QSize
|
from PyQt5.QtCore import QSize
|
||||||
from PyQt5.QtWidgets import QVBoxLayout, QAbstractItemView
|
from PyQt5.QtWidgets import QAbstractItemView
|
||||||
|
|
||||||
from hscommon.trans import trget
|
from hscommon.trans import trget
|
||||||
from ..details_dialog import DetailsDialog as DetailsDialogBase
|
from ..details_dialog import DetailsDialog as DetailsDialogBase
|
||||||
@ -19,11 +19,8 @@ class DetailsDialog(DetailsDialogBase):
|
|||||||
self.setWindowTitle(tr("Details"))
|
self.setWindowTitle(tr("Details"))
|
||||||
self.resize(502, 186)
|
self.resize(502, 186)
|
||||||
self.setMinimumSize(QSize(200, 0))
|
self.setMinimumSize(QSize(200, 0))
|
||||||
self.verticalLayout = QVBoxLayout(self)
|
|
||||||
self.verticalLayout.setSpacing(0)
|
|
||||||
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
|
|
||||||
self.tableView = DetailsTable(self)
|
self.tableView = DetailsTable(self)
|
||||||
self.tableView.setAlternatingRowColors(True)
|
self.tableView.setAlternatingRowColors(True)
|
||||||
self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows)
|
self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||||
self.tableView.setShowGrid(False)
|
self.tableView.setShowGrid(False)
|
||||||
self.verticalLayout.addWidget(self.tableView)
|
self.setWidget(self.tableView)
|
||||||
|
@ -85,7 +85,7 @@ class PreferencesDialog(PreferencesDialogBase):
|
|||||||
self.widgetsVLayout.addWidget(self.widget)
|
self.widgetsVLayout.addWidget(self.widget)
|
||||||
self._setupBottomPart()
|
self._setupBottomPart()
|
||||||
|
|
||||||
def _load(self, prefs, setchecked):
|
def _load(self, prefs, setchecked, section):
|
||||||
setchecked(self.matchSimilarBox, prefs.match_similar)
|
setchecked(self.matchSimilarBox, prefs.match_similar)
|
||||||
setchecked(self.wordWeightingBox, prefs.word_weighting)
|
setchecked(self.wordWeightingBox, prefs.word_weighting)
|
||||||
setchecked(self.ignoreSmallFilesBox, prefs.ignore_small_files)
|
setchecked(self.ignoreSmallFilesBox, prefs.ignore_small_files)
|
||||||
|
@ -171,6 +171,11 @@ class TabWindow(QMainWindow):
|
|||||||
self.setCurrentIndex(index)
|
self.setCurrentIndex(index)
|
||||||
return index
|
return index
|
||||||
|
|
||||||
|
def showTab(self, page):
|
||||||
|
index = self.indexOfWidget(page)
|
||||||
|
self.setTabVisible(index, True)
|
||||||
|
self.setCurrentIndex(index)
|
||||||
|
|
||||||
def indexOfWidget(self, widget):
|
def indexOfWidget(self, widget):
|
||||||
return self.tabWidget.indexOf(widget)
|
return self.tabWidget.indexOf(widget)
|
||||||
|
|
||||||
@ -302,7 +307,7 @@ class TabBarWindow(TabWindow):
|
|||||||
|
|
||||||
@pyqtSlot(int)
|
@pyqtSlot(int)
|
||||||
def setTabIndex(self, index):
|
def setTabIndex(self, index):
|
||||||
if not index:
|
if index is None:
|
||||||
return
|
return
|
||||||
self.tabBar.setCurrentIndex(index)
|
self.tabBar.setCurrentIndex(index)
|
||||||
|
|
||||||
@ -344,6 +349,11 @@ class TabBarWindow(TabWindow):
|
|||||||
@pyqtSlot(int)
|
@pyqtSlot(int)
|
||||||
def onTabCloseRequested(self, index):
|
def onTabCloseRequested(self, index):
|
||||||
current_widget = self.getWidgetAtIndex(index)
|
current_widget = self.getWidgetAtIndex(index)
|
||||||
|
if isinstance(current_widget, DirectoriesDialog):
|
||||||
|
# On MacOS, the tab has a close button even though we explicitely
|
||||||
|
# set it to None in order to hide it. This should prevent
|
||||||
|
# the "Directories" tab from closing by mistake.
|
||||||
|
return
|
||||||
current_widget.close()
|
current_widget.close()
|
||||||
self.stackedWidget.removeWidget(current_widget)
|
self.stackedWidget.removeWidget(current_widget)
|
||||||
# In this case the signal will take care of the tab itself after removing the widget
|
# In this case the signal will take care of the tab itself after removing the widget
|
||||||
|
@ -42,7 +42,7 @@ class AboutBox(QDialog):
|
|||||||
self.setWindowTitle(
|
self.setWindowTitle(
|
||||||
tr("About {}").format(QCoreApplication.instance().applicationName())
|
tr("About {}").format(QCoreApplication.instance().applicationName())
|
||||||
)
|
)
|
||||||
self.resize(400, 190)
|
self.resize(400, 290)
|
||||||
sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
# http://www.gnu.org/licenses/gpl-3.0.html
|
# http://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, QSettings, QRect, QObject, pyqtSignal
|
from PyQt5.QtCore import Qt, QSettings, QRect, QObject, pyqtSignal
|
||||||
|
from PyQt5.QtWidgets import QDockWidget
|
||||||
|
|
||||||
from hscommon.trans import trget
|
from hscommon.trans import trget
|
||||||
from hscommon.util import tryint
|
from hscommon.util import tryint
|
||||||
@ -120,19 +121,27 @@ class Preferences(QObject):
|
|||||||
self._settings.setValue(name, normalize_for_serialization(value))
|
self._settings.setValue(name, normalize_for_serialization(value))
|
||||||
|
|
||||||
def saveGeometry(self, name, widget):
|
def saveGeometry(self, name, widget):
|
||||||
# We save geometry under a 5-sized int array: first item is a flag for whether the widget
|
# We save geometry under a 7-sized int array: first item is a flag
|
||||||
# is maximized and the other 4 are (x, y, w, h).
|
# for whether the widget is maximized, second item is a flag for whether
|
||||||
|
# the widget is docked, third item is a Qt::DockWidgetArea enum value,
|
||||||
|
# and the other 4 are (x, y, w, h).
|
||||||
m = 1 if widget.isMaximized() else 0
|
m = 1 if widget.isMaximized() else 0
|
||||||
|
d = 1 if isinstance(widget, QDockWidget) and not widget.isFloating() else 0
|
||||||
|
area = widget.parent.dockWidgetArea(widget) if d else 0
|
||||||
r = widget.geometry()
|
r = widget.geometry()
|
||||||
rectAsList = [r.x(), r.y(), r.width(), r.height()]
|
rectAsList = [r.x(), r.y(), r.width(), r.height()]
|
||||||
self.set_value(name, [m] + rectAsList)
|
self.set_value(name, [m, d, area] + rectAsList)
|
||||||
|
|
||||||
def restoreGeometry(self, name, widget):
|
def restoreGeometry(self, name, widget):
|
||||||
geometry = self.get_value(name)
|
geometry = self.get_value(name)
|
||||||
if geometry and len(geometry) == 5:
|
if geometry and len(geometry) == 7:
|
||||||
m, x, y, w, h = geometry
|
m, d, area, x, y, w, h = geometry
|
||||||
if m:
|
if m:
|
||||||
widget.setWindowState(Qt.WindowMaximized)
|
widget.setWindowState(Qt.WindowMaximized)
|
||||||
else:
|
else:
|
||||||
r = QRect(x, y, w, h)
|
r = QRect(x, y, w, h)
|
||||||
widget.setGeometry(r)
|
widget.setGeometry(r)
|
||||||
|
if isinstance(widget, QDockWidget):
|
||||||
|
# Inform of the previous dock state and the area used
|
||||||
|
return bool(d), area
|
||||||
|
return False, 0
|
||||||
|