1
0
mirror of https://github.com/arsenetar/dupeguru.git synced 2025-08-30 12:39:42 +00:00

Compare commits

..

74 Commits

Author SHA1 Message Date
1d05f8910d
Merge pull request #701 from glubsy/PR_ref_row_background_color
Change reference row background color
2020-10-27 21:53:53 -05:00
glubsy
bd09b30468 Merge branch 'master' into PR_ref_row_background_color 2020-10-28 03:50:13 +01:00
8d9933d035
Merge pull request #683 from glubsy/details_dialog_improvements
Add image comparison features to details dialog
2020-10-27 21:28:23 -05:00
glubsy
cf5ba038d7 Remove icon credits from about box
* Moved credits to CREDITS file
* Updated exchange icon with higher hue contrast for better visibility on dark backgrounds
2020-10-28 02:18:41 +01:00
glubsy
59ce740369 Remove print debug statements 2020-10-28 01:50:49 +01:00
glubsy
92feba5f08 Remove obsolete UI setup code 2020-10-28 01:48:39 +01:00
glubsy
a265b71d36 Improve comment reflecting modification of function 2020-10-28 01:45:03 +01:00
glubsy
b8af2a4eb5 Don't show parent window's context menu on viewers
* When right clicking on image viewers while they are docked, the context menu of the Results window showed up.
* This also enables capture of right click and middle click buttons to drag around images, which solves a conflict with some theme engines that enable left mouse button click to drag a window's position regardless of where the event happens, hence blocking the panning.
* Probably unnecessary to check which button is released.
2020-09-03 01:44:01 +02:00
glubsy
21e62b7374 Colorize background for reference row
As per issue #647, highlight background color for reference for better readability.
2020-08-12 21:37:29 +02:00
glubsy
089f00adb8 Fix typo in class member reference 2020-08-03 16:18:15 +02:00
glubsy
76fbfc2822 Fix adding new Result tab if already existed
* Whenever the Result Window already existed and its tab was in second position, and if the ignore list tab was in 3rd position, asking to show the Result window through the View menu would add a new tab and push the Result tab to the third position (ignore list tab would then become 2nd position).
* Fix view menu Directories entry not switching to index "0" in custom tab bar.
2020-08-02 16:12:47 +02:00
glubsy
866bf996cf Prevent Directories tab from closing on MacOS
* The close button on custom tabs cannot be hidden on MacOS for some reason.
* Prevent the directories tab from closing if the close button was clicked by mistake
2020-08-01 19:35:12 +02:00
glubsy
0104d8922c Fix alignment for combo box's label 2020-08-01 19:11:37 +02:00
glubsy
fbd7c4fe5f Tweak visuals for cache selection item 2020-08-01 19:07:45 +02:00
glubsy
de5e61293b Add stretch to bottom of General pref tab 2020-08-01 19:02:04 +02:00
glubsy
a3e402a3af Group general interface options together
* Use QGroupBox to keep items together on the display tab in the preference dialog just like for the other options.
* It is probably not be necessary to keep these as class members
2020-08-01 18:50:44 +02:00
glubsy
056fa819cc Revert stretching last section in Result window
* It seems that stretching the last section automatically is a bit inconvenient on MacOS as it will grow beyond the window border.
* Keep it as it was before for now until a better solution is devised.
2020-08-01 18:42:46 +02:00
glubsy
3be1ee87c6 Merge branch 'master' into details_dialog_improvements 2020-08-01 18:29:22 +02:00
glubsy
628d772766 Use FormLayout instead of GridLayout
QFormLayout should adhere to each platform's style better. It also simplifies the code a bit since we don't have to setup the labels, etc.
2020-08-01 17:40:31 +02:00
glubsy
acdeb01206 Tweak preference layout for better readability
* We use GroupBoxes to group items together and surround them in a frame
* Remove separator lines to avoid cluttering
* Adjust columns and their stretch factors for better alignment of buttons
2020-08-01 16:42:14 +02:00
glubsy
d2cdcc989b Fix 1 pixel sized color in color picker buttons
* On Linux, even with 1 pixel size, the button is filled entirely with the color selected
* On MacOS, the color pixmap stays at 1 pixel so we hard code the size to 16x16
2020-08-01 02:09:38 +02:00
glubsy
2620d0080c Fix layout error
* Avoid attempting to add a QLayout to DetailsDialog which already has a layout by removing superfluous layout setup.
2020-07-31 22:37:18 +02:00
glubsy
11254381a8 Save dock panel position on quit
* Restore the details dialog dock position if it was previously docked (i.e. not floating).
* Since the details_dialog instance was not deleted after closing by default, the previous instances were still saving their own geometry. We now delete them explicitely if we have to recreate a new instance to avoid the signal triggering the callback to save the geometry.
* Since restoreGeometry() and saveGeometry() are only called in our QDockWidget, it should be safe to modify the methods for the Preferences class (in qtlib).
2020-07-30 20:25:20 +02:00
glubsy
23642815f6 Remove unused properties in details table headers 2020-07-30 15:38:37 +02:00
glubsy
7e4f371841 Avoid crash when quitting
* If details dialog failed to be created for some reason, avoid crashing by dereferencing a null pointer
2020-07-30 15:30:09 +02:00
glubsy
9b8637ffc8 Stretch last header section in Result window 2020-07-30 15:16:31 +02:00
glubsy
79613f9b1e Fix crash quitting while details dialog active
* While the details dialog is opened, if quit is triggered, the error message "'DetailsPanel' object has no attribute '_table'" is reported
* A workaround is to cleanly close the dialog before tear down
2020-07-30 03:22:13 +02:00
glubsy
fa54e93236 Add preference to turn off scrollbars in viewers
Refactor preference Display page to only include PE specific preferences in the PE mode.
2020-07-30 03:13:58 +02:00
glubsy
eab5003e61 Add color preference for delta in details table 2020-07-29 21:43:45 +02:00
glubsy
da8c493c9f Toggle visibility of details dialog
* When using the Ctrl+I shortcut or the "Details" button in the Results window, toggle the details dialog on/off.
* This works also while it is docked.
2020-07-29 20:43:18 +02:00
glubsy
9795f14176 Fix title bar toggling on/off when dialog 2020-07-29 20:00:27 +02:00
glubsy
1937120ad7 Fix toggling details view via menu or shortcut
* Using Ctrl+I would toggle the title bar on/off
2020-07-29 04:51:03 +02:00
glubsy
1823575af4 Fix swapping table view columns
We now have only two columns to swap, not 3.
2020-07-29 04:26:40 +02:00
glubsy
7dc9f25b06 Merge branch 'master' into details_dialog_improvements 2020-07-29 04:20:16 +02:00
glubsy
730fadf63f Merge branch 'preferences_tabs' into details_dialog_improvements 2020-07-22 22:41:22 +02:00
glubsy
9ae0d7e5cf Add color picker buttons to preferences dialog
* Buttons display the color currently in use
* Result table uses selected colors accordingly
* Keep items aligned with GridLayouts in preference dialog
* Reordering of items in a more logical manner*
2020-07-22 22:12:46 +02:00
glubsy
cf64565012 Add option to use internal icons in details dialog
* On Windows and MacOS, no idea how themes work so only allow Linux to use their theme icons
* Internal icons are used by default on non-Linux platforms
2020-07-21 03:52:15 +02:00
glubsy
298f659f6e Fix Restore Default Preferences button
* When clicking the "Restore Default" in the preferences dialog, only affect the preferences displayed in the current tab. The hidden tab should not be affected by this button.
2020-07-20 05:04:25 +02:00
glubsy
3539263437 Add tabs to preference dialog. 2020-07-20 03:10:06 +02:00
glubsy
6213d50670 Squashed commit of the following:
commit ac941037ff51158b64daa65c244df26346af10cf
Author: glubsy <glubsy@users.noreply.github.com>
Date:   Thu Jul 16 22:21:24 2020 +0200

    Fix resize of top frame not updating scaled pixmap

    * Also limit viewing features such as zoom levels when files have different dimensions
    * GraphicsViewImageViewer is still a bit buggy: the scrollbars are toggled on when the pixmap is null in the reference viewer (we do not use that class right anyway)

commit 733b3b0ed4fbd6de908c968402af03879df3336f
Author: glubsy <glubsy@users.noreply.github.com>
Date:   Thu Jul 16 01:31:24 2020 +0200

    Prevent zoom for images of differing dimensions

    * If images are not the same size, prevent zooming features from being used by disabling the normal size button, only enable swap

commit 9168d72f38faaf0a12230cd544f14190cd29fca4
Author: glubsy <glubsy@users.noreply.github.com>
Date:   Wed Jul 15 22:47:32 2020 +0200

    Update preferences on show(), not in constructor

    * If the dialog window shouldn't have a titlebar during construction, update accordingly only when showing to fix Windows displaying a window without titlebar on first show
    * Only save geometry if the window is floating. Otherwise geometry while docked is saved whih gives weird results on subsequent starts, since it may be floating by default anyway (at least on Linux where titlebar being disabled is allowed while floating)
    * Vertical title bar doesn't seem to work on Windows, add note in preferences dialog

commit 75621cc816120597f493e0debc6d88e2e0bbd30a
Author: glubsy <glubsy@users.noreply.github.com>
Date:   Wed Jul 15 22:04:19 2020 +0200

    Prevent Windows from floating if no decoration

    * Windows users cannot move a window which has no native decorations. Toggling a dock widget's titlebar off also removes native decorations on a floating window. Until we implement a replacement titlebar by overriding paintEvents, simply force the floating window to go back to docked state after we toggled the titlebar off.

commit 3c816b2f11ddc66a78cdc6327ee102df46d1a552
Author: glubsy <glubsy@users.noreply.github.com>
Date:   Wed Jul 15 21:43:01 2020 +0200

    Fix computing and setting offset to 0 for tableview

commit 85d6e05cd406b999e8f6ae421a9746a0c9f146bb
Merge: 66127d02 3eddeb6a
Author: glubsy <glubsy@users.noreply.github.com>
Date:   Wed Jul 15 21:25:44 2020 +0200

    Merge branch 'dockable_windows' into details_dialog_improvements_dev

commit 66127d025e9a497ee13126f955166946acdb35a8
Author: glubsy <glubsy@users.noreply.github.com>
Date:   Wed Jul 15 20:22:13 2020 +0200

    Add credit for icons used, upscale exchange icon

    * Jason Cho gave his express permission to use the icon (it was made 10 years ago and he doesn't have the source files anymore)
    * Used waifu2x to upscale the icon
    * Used GIMP to draw dark outline around the icon
    * Source files are included

commit 58c675d1fa90a7247233d9887a460cf5a8e4cbf5
Author: glubsy <glubsy@users.noreply.github.com>
Date:   Wed Jul 15 05:25:47 2020 +0200

    Add custom icons

    * Use custom icons on platforms which do not provide theme
    * Old zoom icons credits to "schollidesign" from icon pack Office and Entertainment (GPL licence).
    * Exchange icon credit to Jason Cho (Unknown license).
    * Use hack to resize viewers on first show() as well

commit 95b8406c7b97aab170d127b466ff506b724def3c
Author: glubsy <glubsy@users.noreply.github.com>
Date:   Wed Jul 15 04:14:24 2020 +0200

    Fix scrollbar displayed while splitter maxed out

    * For some reason the table's height is a few pixel longer on Windows so we work around the issue by adding a small offset to the maximum height hint.
    * No idea about MacOS yet but this might need the same treatment.

commit 3eddeb6aebc99126e62eb05af60333ba3bd22e82
Author: glubsy <glubsy@users.noreply.github.com>
Date:   Tue Jul 14 17:37:48 2020 +0200

    Fix ME/SE details dialogs, add preferences

    * Fix ME and SE versions of details dialog not displaying their content properly after change to QDockWidget
    * Add option to toggle titlebar and orientation of titlebar in preferences dialog
    * Fix setting layout on PE details dialog window while layout already set, by removing the self (parent) reference in constructing the QSplitter

commit 56912a71084415eac2f447650279d833d9857686
Author: glubsy <glubsy@users.noreply.github.com>
Date:   Mon Jul 13 05:06:04 2020 +0200

    Make details dialog dockable
2020-07-16 22:31:54 +02:00
glubsy
ac941037ff Fix resize of top frame not updating scaled pixmap
* Also limit viewing features such as zoom levels when files have different dimensions
* GraphicsViewImageViewer is still a bit buggy: the scrollbars are toggled on when the pixmap is null in the reference viewer (we do not use that class right anyway)
2020-07-16 22:21:24 +02:00
glubsy
733b3b0ed4 Prevent zoom for images of differing dimensions
* If images are not the same size, prevent zooming features from being used by disabling the normal size button, only enable swap
2020-07-16 01:31:24 +02:00
glubsy
9168d72f38 Update preferences on show(), not in constructor
* If the dialog window shouldn't have a titlebar during construction, update accordingly only when showing to fix Windows displaying a window without titlebar on first show
* Only save geometry if the window is floating. Otherwise geometry while docked is saved whih gives weird results on subsequent starts, since it may be floating by default anyway (at least on Linux where titlebar being disabled is allowed while floating)
* Vertical title bar doesn't seem to work on Windows, add note in preferences dialog
2020-07-15 23:00:55 +02:00
glubsy
75621cc816 Prevent Windows from floating if no decoration
* Windows users cannot move a window which has no native decorations. Toggling a dock widget's titlebar off also removes native decorations on a floating window. Until we implement a replacement titlebar by overriding paintEvents, simply force the floating window to go back to docked state after we toggled the titlebar off.
2020-07-15 22:12:19 +02:00
glubsy
3c816b2f11 Fix computing and setting offset to 0 for tableview 2020-07-15 21:48:11 +02:00
glubsy
85d6e05cd4 Merge branch 'dockable_windows' into details_dialog_improvements_dev 2020-07-15 21:25:44 +02:00
glubsy
66127d025e Add credit for icons used, upscale exchange icon
* Jason Cho gave his express permission to use the icon (it was made 10 years ago and he doesn't have the source files anymore)
* Used waifu2x to upscale the icon
* Used GIMP to draw dark outline around the icon
* Source files are included
2020-07-15 20:22:13 +02:00
glubsy
58c675d1fa Add custom icons
* Use custom icons on platforms which do not provide theme
* Old zoom icons credits to "schollidesign" from icon pack Office and Entertainment (GPL licence).
* Exchange icon credit to Jason Cho (Unknown license).
* Use hack to resize viewers on first show() as well
2020-07-15 05:25:47 +02:00
glubsy
95b8406c7b Fix scrollbar displayed while splitter maxed out
* For some reason the table's height is a few pixel longer on Windows so we work around the issue by adding a small offset to the maximum height hint.
* No idea about MacOS yet but this might need the same treatment.
2020-07-15 04:14:24 +02:00
glubsy
3eddeb6aeb Fix ME/SE details dialogs, add preferences
* Fix ME and SE versions of details dialog not displaying their content properly after change to QDockWidget
* Add option to toggle titlebar and orientation of titlebar in preferences dialog
* Fix setting layout on PE details dialog window while layout already set, by removing the self (parent) reference in constructing the QSplitter
2020-07-14 17:37:48 +02:00
glubsy
56912a7108 Make details dialog dockable 2020-07-13 05:06:04 +02:00
glubsy
7ab299874d Merge commit 'b0a256f0' 2020-07-12 17:54:51 +02:00
glubsy
b0a256f0d4 Fix flake8 minor issues 2020-07-02 23:09:02 +02:00
glubsy
4ee9479a5f Add image comparison features to details dialog
* Add splitter in order to hide the details table.
* Add a toolbar to the Details Dialog window to allow for better image
comparisons: zoom in/out, swap pixmaps in place, best-fit-to-viewport.
Scrollbars and viewports are synchronized.
2020-07-02 22:52:47 +02:00
glubsy
e7b3252534 Cleanup of details table 2020-07-02 22:36:57 +02:00
glubsy
36ab84423a Move buttons into the toolbar class.
* Moved the QToolbar into the image viewer's  translation unit.
* QAction are still attached to the dialog window for shortcuts to work
2020-07-02 22:36:57 +02:00
glubsy
370b582c9b Add working zoom functions to GraphicsView viewers. 2020-07-02 22:36:57 +02:00
glubsy
9f15139d5f Fix view resetting when selecting reference only.
* Needed to ignore the scrollbar changes in the disabled
panel, sine a null pixmap would reset the bars to 0 and affect
the selected viewer.
* Keep view as same scale accross entries from the same group.
2020-07-02 22:36:57 +02:00
glubsy
011939f5ee Keep scale accross files of the same dupe group.
* Also fix scaled down pixmap when updating pixmap in the same group
* Fix ignoring mouse wheel event when max scale has been reached
* Fix toggle scrollbars when asking for normal size
2020-07-02 22:36:57 +02:00
glubsy
977c20f7c4 Add QSplitter to hide TableView in DetailsDialog 2020-07-02 22:36:57 +02:00
glubsy
aa79b31aae Work around resizing down offset by 1 pixel. 2020-07-02 22:36:57 +02:00
glubsy
970bb5e19d Add mostly working ScrollArea imge viewers
* Work around flickering of scrollbars due to
GridLayout resizing on odd pixels by disabling
the scrollbars while BestFit is active
* Also setting minimum column width to work around
the issue above.
* Avoid updating scrollbar values twice by using a
simple boolean lock
2020-07-02 22:36:57 +02:00
glubsy
a706d0ebe5 Implement mostly working ScrollArea viewer
Using a QWidget inside the QScrollArea mostly works
but we only move around the pixmap inside the QWidget,
not the QWidget itself, which doesn't update scrollbars.
Need a better implementation.
2020-07-02 22:36:57 +02:00
glubsy
b7abcf2989 Use native QPixmap swap() method instead of manual setPixmap()
When swapping images, use getters to hopefully get a reference to
each pixmap and swap them within a single slot.
2020-07-02 22:36:57 +02:00
glubsy
8103cb3664 Disable unused methods from controller
* setPixmap() now disables the QWidget automatically if the pixmap passed is null.
* the controller relays repaint events to the other widget
2020-07-02 22:36:57 +02:00
glubsy
c3797918d2 Controller class to decouple from the dialog class
The controller singleton acts as a proxy to relay
signals from each widget to the other
It should help encapsulating things better if we need to
use a different class for image viewers in the future.
2020-07-02 22:36:57 +02:00
glubsy
60ddb9b596 Working synchronized views. 2020-07-02 22:36:57 +02:00
glubsy
a29f3fb407 only update delta when mouse is being dragged to reduce paint events 2020-07-02 22:36:57 +02:00
glubsy
c6162914ed working synchronized panning 2020-07-02 22:36:57 +02:00
glubsy
02bd822ca0 working zoom functions, mouse wheel event 2020-07-02 22:36:57 +02:00
glubsy
ea6197626b drag mouse with ImageViewer class 2020-07-02 22:36:57 +02:00
glubsy
468a736bfb add normal size button 2020-07-02 22:36:57 +02:00
glubsy
f42df12a29 attempt at double click on Qlabel 2020-07-02 22:36:57 +02:00
glubsy
9b48e1851d add zoom and swap buttons to details dialog 2020-07-02 22:36:57 +02:00
31 changed files with 1838 additions and 164 deletions

4
.gitignore vendored
View File

@ -22,4 +22,6 @@ cocoa/autogen
*.pyd *.pyd
*.exe *.exe
*.spec *.spec
.vscode

View File

@ -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

Binary file not shown.

BIN
images/exchange.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
images/exchange.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 797 B

BIN
images/exchange_purple.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 685 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
images/old_zoom_in.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
images/old_zoom_out.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -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:
self.details_dialog.show() if not self.details_dialog.isVisible():
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()

View File

@ -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):

View File

@ -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()

View File

@ -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>

View File

@ -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)

View File

@ -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)

View File

@ -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

File diff suppressed because it is too large Load Diff

View 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)

View File

@ -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,27 @@ 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_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( self.resultWindowIsMaximized = get(
"ResultWindowIsMaximized", self.resultWindowIsMaximized "ResultWindowIsMaximized", self.resultWindowIsMaximized
) )
@ -72,6 +95,15 @@ 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_ref_background_color = QColor(Qt.darkGray)
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 +139,15 @@ 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_("ResultTableRefBackgroundColor", self.result_table_ref_background_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)

View File

@ -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,77 @@ 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_ref_background_color = ColorPickerButton(self)
gridlayout.addRow(tr("Reference background color:"),
self.result_table_ref_background_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 +231,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 +264,42 @@ 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
self.filterHardnessSlider.setValue(prefs.filter_hardness)
self.filterHardnessLabel.setNum(prefs.filter_hardness)
setchecked = lambda cb, b: cb.setCheckState(Qt.Checked if b else Qt.Unchecked) setchecked = lambda cb, b: cb.setCheckState(Qt.Checked if b else Qt.Unchecked)
setchecked(self.mixFileKindBox, prefs.mix_file_kind) if section & Sections.GENERAL:
setchecked(self.useRegexpBox, prefs.use_regexp) self.filterHardnessSlider.setValue(prefs.filter_hardness)
setchecked(self.removeEmptyFoldersBox, prefs.remove_empty_folders) self.filterHardnessLabel.setNum(prefs.filter_hardness)
setchecked(self.ignoreHardlinkMatches, prefs.ignore_hardlink_matches) setchecked(self.mixFileKindBox, prefs.mix_file_kind)
setchecked(self.debugModeBox, prefs.debug_mode) setchecked(self.useRegexpBox, prefs.use_regexp)
setchecked(self.reference_bold_font, prefs.reference_bold_font) setchecked(self.removeEmptyFoldersBox, prefs.remove_empty_folders)
setchecked(self.tabs_default_pos, prefs.tabs_default_pos) setchecked(self.ignoreHardlinkMatches, prefs.ignore_hardlink_matches)
self.copyMoveDestinationComboBox.setCurrentIndex(prefs.destination_type) setchecked(self.debugModeBox, prefs.debug_mode)
self.customCommandEdit.setText(prefs.custom_command) self.copyMoveDestinationComboBox.setCurrentIndex(prefs.destination_type)
self.fontSizeSpinBox.setValue(prefs.tableFontSize) self.customCommandEdit.setText(prefs.custom_command)
try: if section & Sections.DISPLAY:
langindex = self.supportedLanguages.index(self.app.prefs.language) setchecked(self.reference_bold_font, prefs.reference_bold_font)
except ValueError: setchecked(self.tabs_default_pos, prefs.tabs_default_pos)
langindex = 0 setchecked(self.details_dialog_titlebar_enabled,
self.languageComboBox.setCurrentIndex(langindex) prefs.details_dialog_titlebar_enabled)
self._load(prefs, setchecked) setchecked(self.details_dialog_vertical_titlebar,
prefs.details_dialog_vertical_titlebar)
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_ref_background_color.setColor(
prefs.result_table_ref_background_color)
self.result_table_delta_foreground_color.setColor(
prefs.result_table_delta_foreground_color)
try:
langindex = self.supportedLanguages.index(self.app.prefs.language)
except ValueError:
langindex = 0
self.languageComboBox.setCurrentIndex(langindex)
self._load(prefs, setchecked, section)
def save(self): def save(self):
prefs = self.app.prefs prefs = self.app.prefs
@ -215,6 +311,12 @@ 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_ref_background_color = self.result_table_ref_background_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 +334,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))

View File

@ -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)
@ -29,6 +29,8 @@ class ResultsModel(Table):
def _getData(self, row, column, role): def _getData(self, row, column, role):
if column.name == "marked": if column.name == "marked":
if role == Qt.BackgroundRole and row.isref:
return QBrush(self.prefs.result_table_ref_background_color)
if role == Qt.CheckStateRole and row.markable: if role == Qt.CheckStateRole and row.markable:
return Qt.Checked if row.marked else Qt.Unchecked return Qt.Checked if row.marked else Qt.Unchecked
return None return None
@ -37,9 +39,12 @@ 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.BackgroundRole:
if row.isref:
return QBrush(self.prefs.result_table_ref_background_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:

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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