Skip to content

Commit

Permalink
Merge pull request #683 from glubsy/details_dialog_improvements
Browse files Browse the repository at this point in the history
Add image comparison features to details dialog
  • Loading branch information
arsenetar authored Oct 28, 2020
2 parents 8d26c92 + cf5ba03 commit 8d9933d
Show file tree
Hide file tree
Showing 31 changed files with 1,823 additions and 164 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,6 @@ cocoa/autogen

*.pyd
*.exe
*.spec
*.spec

.vscode
2 changes: 2 additions & 0 deletions CREDITS
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
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.

* 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
* Gregor Tätzner, German localization
* Frank Weber, German localization
Expand Down
Binary file added images/exchange.icns
Binary file not shown.
Binary file added images/exchange.ico
Binary file not shown.
Binary file added images/exchange.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/exchange_purple.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/exchange_purple_upscaled.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/exchange_purple_waifu_s4_tta8.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/exchange_purple_waifu_s4_tta8.xcf
Binary file not shown.
Binary file added images/exchange_waifu_s4_tta8.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/old_zoom_best_fit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/old_zoom_in.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/old_zoom_original.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/old_zoom_out.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 21 additions & 9 deletions qt/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import sys
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.QtWidgets import QApplication, QFileDialog, QDialog, QMessageBox

Expand Down Expand Up @@ -58,11 +58,11 @@ def __init__(self, **kwargs):
def _setup(self):
core.pe.photo.PLAT_SPECIFIC_PHOTO_CLASS = PlatSpecificPhoto
self._setupActions()
self.details_dialog = None
self._update_options()
self.recentResults = Recent(self, "recentResults")
self.recentResults.mustOpenItem.connect(self.model.load_from)
self.resultWindow = None
self.details_dialog = None
if self.use_tabs:
self.main_window = TabBarWindow(self) if not self.prefs.tabs_default_pos else TabWindow(self)
parent_window = self.main_window
Expand Down Expand Up @@ -177,6 +177,9 @@ def _update_options(self):
self.model.options["match_scaled"] = self.prefs.match_scaled
self.model.options["picture_cache_type"] = self.prefs.picture_cache_type

if self.details_dialog:
self.details_dialog.update_options()

# --- Private
def _get_details_dialog_class(self):
if self.model.app_mode == AppMode.Picture:
Expand Down Expand Up @@ -212,22 +215,22 @@ def invokeCustomCommand(self):

def show_details(self):
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):
if self.resultWindow is not None:
if self.use_tabs:
self.main_window.addTab(
self.resultWindow, "Results", switch=True)
self.main_window.showTab(self.resultWindow)
else:
self.resultWindow.show()

def showDirectoriesWindow(self):
if self.directories_dialog is not None:
if self.use_tabs:
index = self.main_window.indexOfWidget(self.directories_dialog)
self.main_window.setTabVisible(index, True)
self.main_window.setCurrentIndex(index)
self.main_window.showTab(self.directories_dialog)
else:
self.directories_dialog.show()

Expand Down Expand Up @@ -295,6 +298,9 @@ def preferencesTriggered(self):
preferences_dialog.setParent(None)

def quitTriggered(self):
if self.details_dialog is not None:
self.details_dialog.close()

if self.main_window:
self.main_window.close()
else:
Expand Down Expand Up @@ -333,14 +339,20 @@ def create_results_window(self):
"""Creates resultWindow and details_dialog depending on the selected ``app_mode``.
"""
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.setParent(None)
# self.details_dialog.setParent(None) # seems unnecessary
if self.resultWindow is not None:
self.resultWindow.close()
self.resultWindow.setParent(None)
if self.use_tabs:
self.resultWindow = self.main_window.createPage(
"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
self.resultWindow = ResultWindow(self.directories_dialog, self)
self.directories_dialog._updateActionsState()
Expand Down
39 changes: 34 additions & 5 deletions qt/details_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,63 @@
# http://www.gnu.org/licenses/gpl-3.0.html

from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QDialog
from PyQt5.QtWidgets import QDockWidget, QWidget

from .details_table import DetailsModel
from hscommon.plat import ISLINUX


class DetailsDialog(QDialog):
class DetailsDialog(QDockWidget):
def __init__(self, parent, app, **kwargs):
super().__init__(parent, Qt.Tool, **kwargs)
self.parent = parent
self.app = app
self.model = app.model.details_panel
self.setAllowedAreas(Qt.AllDockWidgetAreas)
self._setupUi()
# 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.
self._shown_once = False
self.app.prefs.restoreGeometry("DetailsWindowRect", self)
self.tableModel = DetailsModel(self.model)
self._wasDocked, area = self.app.prefs.restoreGeometry("DetailsWindowRect", self)
self.tableModel = DetailsModel(self.model, app)
# tableView is defined in subclasses
self.tableView.setModel(self.tableModel)
self.model.view = self

self.app.willSavePrefs.connect(self.appWillSavePrefs)
# self.setAttribute(Qt.WA_DeleteOnClose)
parent.addDockWidget(
area if self._wasDocked else Qt.BottomDockWidgetArea, self)

def _setupUi(self): # Virtual
pass

def show(self):
if not self._shown_once and self._wasDocked:
self.setFloating(False)
self._shown_once = True
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
def appWillSavePrefs(self):
Expand Down
9 changes: 4 additions & 5 deletions qt/details_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from PyQt5.QtCore import Qt, QAbstractTableModel
from PyQt5.QtWidgets import QHeaderView, QTableView
from PyQt5.QtGui import QFont, QBrush, QColor
from PyQt5.QtGui import QFont, QBrush

from hscommon.trans import trget

Expand All @@ -18,9 +18,10 @@


class DetailsModel(QAbstractTableModel):
def __init__(self, model, **kwargs):
def __init__(self, model, app, **kwargs):
super().__init__(**kwargs)
self.model = model
self.prefs = app.prefs

def columnCount(self, parent):
return len(HEADER)
Expand All @@ -43,7 +44,7 @@ def data(self, index, role):
if role == Qt.DisplayRole:
return self.model.row(row)[column]
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]:
font = QFont(self.model.view.font()) # or simply QFont()
font.setBold(True)
Expand Down Expand Up @@ -85,8 +86,6 @@ def setModel(self, model):
# The model needs to be set to set header stuff
hheader = self.horizontalHeader()
hheader.setHighlightSections(False)
hheader.setStretchLastSection(False)
hheader.resizeSection(0, 100)
hheader.setSectionResizeMode(0, QHeaderView.Stretch)
hheader.setSectionResizeMode(1, QHeaderView.Stretch)
vheader = self.verticalHeader()
Expand Down
5 changes: 5 additions & 0 deletions qt/dg.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,10 @@
<file alias="plus">../images/plus_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="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>
</RCC>
7 changes: 2 additions & 5 deletions qt/me/details_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# http://www.gnu.org/licenses/gpl-3.0.html

from PyQt5.QtCore import QSize
from PyQt5.QtWidgets import QVBoxLayout, QAbstractItemView
from PyQt5.QtWidgets import QAbstractItemView

from hscommon.trans import trget
from ..details_dialog import DetailsDialog as DetailsDialogBase
Expand All @@ -19,11 +19,8 @@ def _setupUi(self):
self.setWindowTitle(tr("Details"))
self.resize(502, 295)
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.setAlternatingRowColors(True)
self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows)
self.tableView.setShowGrid(False)
self.verticalLayout.addWidget(self.tableView)
self.setWidget(self.tableView)
2 changes: 1 addition & 1 deletion qt/me/preferences_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def _setupPreferenceWidgets(self):
self.widgetsVLayout.addWidget(self.debugModeBox)
self._setupBottomPart()

def _load(self, prefs, setchecked):
def _load(self, prefs, setchecked, section):
setchecked(self.tagTrackBox, prefs.scan_tag_track)
setchecked(self.tagArtistBox, prefs.scan_tag_artist)
setchecked(self.tagAlbumBox, prefs.scan_tag_album)
Expand Down
Loading

0 comments on commit 8d9933d

Please sign in to comment.