From b7d8ce91fdfe393db3dadcf863a7bbbdd844887c Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Sat, 3 Mar 2018 15:50:40 +0100 Subject: [PATCH] Introduce and use PreserveGeometry class and drop a lot useless code It also adds save/restore geometry to: - password dialog - info dialogs (fix PICARD-1093) - tags from filenames dialog - edit tags dialog Each geometry is saved as "geometry_" persist option (ByteArray()) --- picard/ui/__init__.py | 31 ++++++++++++++++++- picard/ui/cdlookup.py | 49 ++++++++++-------------------- picard/ui/edittagdialog.py | 6 +++- picard/ui/infodialog.py | 6 ++-- picard/ui/logview.py | 47 +++------------------------- picard/ui/mainwindow.py | 29 ++++-------------- picard/ui/options/dialog.py | 22 ++++---------- picard/ui/searchdialog/__init__.py | 17 +++-------- picard/ui/searchdialog/album.py | 2 -- picard/ui/searchdialog/artist.py | 2 -- picard/ui/searchdialog/track.py | 2 -- picard/ui/tagsfromfilenames.py | 30 ++---------------- 12 files changed, 79 insertions(+), 164 deletions(-) diff --git a/picard/ui/__init__.py b/picard/ui/__init__.py index c29407126..9f19203ba 100644 --- a/picard/ui/__init__.py +++ b/picard/ui/__init__.py @@ -19,10 +19,39 @@ import uuid +from picard import config from PyQt5 import QtCore, QtWidgets +from picard.util import restore_method -class PicardDialog(QtWidgets.QDialog): +class PreserveGeometry: + + defaultsize = None + autorestore = True + + def __init__(self): + config.Option("persist", self.opt_name(), QtCore.QByteArray()) + if self.autorestore: + self.restore_geometry() + if getattr(self, 'finished', None): + self.finished.connect(self.save_geometry) + + def opt_name(self): + return 'geometry_' + self.__class__.__name__ + + @restore_method + def restore_geometry(self): + geometry = config.persist[self.opt_name()] + if not geometry.isNull(): + self.restoreGeometry(geometry) + elif self.defaultsize: + self.resize(self.defaultsize) + + def save_geometry(self): + config.persist[self.opt_name()] = self.saveGeometry() + + +class PicardDialog(QtWidgets.QDialog, PreserveGeometry): flags = QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint diff --git a/picard/ui/cdlookup.py b/picard/ui/cdlookup.py index 8f25dee89..3be83d693 100644 --- a/picard/ui/cdlookup.py +++ b/picard/ui/cdlookup.py @@ -31,11 +31,12 @@ from picard.util import restore_method class CDLookupDialog(PicardDialog): - dialog_window_size = "cdlookupdialog_window_size" + + defaultsize = QtCore.QSize(720, 360) + autorestore = False dialog_header_state = "cdlookupdialog_header_state" options = [ - config.Option("persist", dialog_window_size, QtCore.QSize(720, 360)), config.Option("persist", dialog_header_state, QtCore.QByteArray()) ] @@ -49,7 +50,6 @@ class CDLookupDialog(PicardDialog): self.ui.release_list.setAlternatingRowColors(True) self.ui.release_list.setHeaderLabels([_("Album"), _("Artist"), _("Date"), _("Country"), _("Labels"), _("Catalog #s"), _("Barcode")]) - self.restore_state() if self.releases: def myjoin(l): return "\n".join(l) @@ -75,48 +75,31 @@ class CDLookupDialog(PicardDialog): self.ui.release_list.sortByColumn(3, QtCore.Qt.AscendingOrder) self.ui.release_list.sortByColumn(2, QtCore.Qt.DescendingOrder) self.ui.lookup_button.clicked.connect(self.lookup) + self.restore_geometry() self.restore_header_state() - - def save_and_accept(self): - self.save_state() - QtWidgets.QDialog.accept(self) + self.finished.connect(self.save_header_state) def accept(self): release_id = self.ui.release_list.currentItem().data(0, QtCore.Qt.UserRole) self.tagger.load_album(release_id, discid=self.disc.id) - self.save_and_accept() + super().accept() def lookup(self): lookup = self.tagger.get_file_lookup() lookup.disc_lookup(self.disc.submission_url) - self.save_and_accept() - - def reject(self): - self.save_state() - QtWidgets.QDialog.reject(self) - - @restore_method - def restore_state(self): - size = config.persist[self.dialog_window_size] - if size: - self.resize(size) - log.debug("restore_state: %s" % self.dialog_window_size) + super().accept() @restore_method def restore_header_state(self): - header = self.ui.release_list.header() - state = config.persist[self.dialog_header_state] - if state: - header.restoreState(state) - log.debug("restore_state: %s" % self.dialog_header_state) - - def save_state(self): if self.ui.release_list: - self.save_header_state() - log.debug("save_state: %s" % self.dialog_window_size) - config.persist[self.dialog_window_size] = self.size() + header = self.ui.release_list.header() + state = config.persist[self.dialog_header_state] + if state: + header.restoreState(state) + log.debug("restore_state: %s" % self.dialog_header_state) def save_header_state(self): - state = self.ui.release_list.header().saveState() - config.persist[self.dialog_header_state] = state - log.debug("save_state: %s" % self.dialog_header_state) + if self.ui.release_list: + state = self.ui.release_list.header().saveState() + config.persist[self.dialog_header_state] = state + log.debug("save_state: %s" % self.dialog_header_state) diff --git a/picard/ui/edittagdialog.py b/picard/ui/edittagdialog.py index 57692d67c..f4ea3d32e 100644 --- a/picard/ui/edittagdialog.py +++ b/picard/ui/edittagdialog.py @@ -25,6 +25,9 @@ from picard.ui.ui_edittagdialog import Ui_EditTagDialog class EditTagDialog(PicardDialog): + defaultsize = None + autorestore = False + def __init__(self, window, tag): super().__init__(window) self.ui = Ui_EditTagDialog() @@ -55,6 +58,7 @@ class EditTagDialog(PicardDialog): self.ui.remove_value.clicked.connect(self.remove_value) self.value_list.itemChanged.connect(self.value_edited) self.value_list.itemSelectionChanged.connect(self.value_selection_changed) + self.restore_geometry() def edit_value(self): item = self.value_list.currentItem() @@ -175,4 +179,4 @@ class EditTagDialog(PicardDialog): obj.update() self.window.ignore_selection_changes = False self.window.update_selection() - QtWidgets.QDialog.accept(self) + super().accept() diff --git a/picard/ui/infodialog.py b/picard/ui/infodialog.py index 12b846417..a6312da68 100644 --- a/picard/ui/infodialog.py +++ b/picard/ui/infodialog.py @@ -97,6 +97,9 @@ class ArtworkTable(QtWidgets.QTableWidget): class InfoDialog(PicardDialog): + defaultsize = QtCore.QSize(665, 436) + autorestore = False + def __init__(self, obj, parent=None): super().__init__(parent) self.obj = obj @@ -131,14 +134,13 @@ class InfoDialog(PicardDialog): self.ui.artwork_table = ArtworkTable(self.display_existing_artwork) self.ui.artwork_table.setObjectName("artwork_table") self.ui.vboxlayout1.addWidget(self.ui.artwork_table) - if self.display_existing_artwork: - self.resize(665, 436) self.setTabOrder(self.ui.tabWidget, self.ui.artwork_table) self.setTabOrder(self.ui.artwork_table, self.ui.buttonBox) self.setWindowTitle(_("Info")) self.artwork_table = self.ui.artwork_table self._display_tabs() + self.restore_geometry() def _display_tabs(self): self._display_info_tab() diff --git a/picard/ui/logview.py b/picard/ui/logview.py index 984a13a32..3a7eb0326 100644 --- a/picard/ui/logview.py +++ b/picard/ui/logview.py @@ -26,15 +26,14 @@ from functools import partial from PyQt5 import QtCore, QtGui, QtWidgets from picard import config, log from picard.ui import PicardDialog -from picard.util import restore_method class LogViewDialog(PicardDialog): + defaultsize = QtCore.QSize(570, 400) - def __init__(self, title, w, h, parent=None): + def __init__(self, title, parent=None): super().__init__(parent) self.setWindowFlags(QtCore.Qt.Window) - self.resize(w, h) self.setWindowTitle(title) self.doc = QtGui.QTextDocument() self.textCursor = QtGui.QTextCursor(self.doc) @@ -44,26 +43,8 @@ class LogViewDialog(PicardDialog): self.setLayout(self.vbox) self.vbox.addWidget(self.browser) - def saveWindowState(self, position, size): - pos = self.pos() - if not pos.isNull(): - config.persist[position] = pos - config.persist[size] = self.size() - - @restore_method - def restoreWindowState(self, position, size): - pos = config.persist[position] - if pos.x() > 0 and pos.y() > 0: - self.move(pos) - self.resize(config.persist[size]) - - def closeEvent(self, event): - return super().closeEvent(event) - class LogViewCommon(LogViewDialog): - WIDTH = 570 - HEIGHT = 400 def __init__(self, log_tail, *args, **kwargs): self.displaying = False @@ -144,17 +125,12 @@ class VerbosityMenu(QtWidgets.QMenu): class LogView(LogViewCommon): options = [ - config.Option("persist", "logview_position", QtCore.QPoint()), - config.Option("persist", "logview_size", QtCore.QSize( - LogViewCommon.WIDTH, LogViewCommon.HEIGHT)), config.IntOption("setting", "log_verbosity", log.VERBOSITY_DEFAULT), ] def __init__(self, parent=None): - super().__init__(log.main_tail, _("Log"), w=self.WIDTH, - h=self.HEIGHT, parent=parent) + super().__init__(log.main_tail, _("Log"), parent=parent) self.verbosity = config.setting['log_verbosity'] - self.restoreWindowState("logview_position", "logview_size") self._setup_formats() self.hl_text = '' @@ -301,10 +277,6 @@ class LogView(LogViewCommon): self.verbosity = level self.verbosity_menu.set_verbosity(self.verbosity) - def closeEvent(self, event): - self.saveWindowState("logview_position", "logview_size") - super().closeEvent(event) - def _verbosity_changed(self, level): if level != self.verbosity: config.setting['log_verbosity'] = level @@ -314,17 +286,6 @@ class LogView(LogViewCommon): class HistoryView(LogViewCommon): - options = [ - config.Option("persist", "historyview_position", QtCore.QPoint()), - config.Option("persist", "historyview_size", QtCore.QSize(LogViewCommon.WIDTH, - LogViewCommon.HEIGHT)), - ] def __init__(self, parent=None): - super().__init__(log.history_tail, _("Activity History"), w=self.WIDTH, - h=self.HEIGHT, parent=parent) - self.restoreWindowState("historyview_position", "historyview_size") - - def closeEvent(self, event): - self.saveWindowState("historyview_position", "historyview_size") - super().closeEvent(event) + super().__init__(log.history_tail, _("Activity History"), parent=parent) diff --git a/picard/ui/mainwindow.py b/picard/ui/mainwindow.py index c2bc4146d..90b8a8937 100644 --- a/picard/ui/mainwindow.py +++ b/picard/ui/mainwindow.py @@ -29,6 +29,7 @@ from picard.cluster import Cluster from picard.file import File from picard.track import Track from picard.formats import supported_formats +from picard.ui import PreserveGeometry from picard.ui.coverartbox import CoverArtBox from picard.ui.itemviews import MainPanel from picard.ui.metadatabox import MetadataBox @@ -58,14 +59,14 @@ def register_ui_init(function): ui_init.register(function.__module__, function) -class MainWindow(QtWidgets.QMainWindow): +class MainWindow(QtWidgets.QMainWindow, PreserveGeometry): + defaultsize = QtCore.QSize(780, 560) + autorestore = False selection_updated = QtCore.pyqtSignal(object) options = [ config.Option("persist", "window_state", QtCore.QByteArray()), - config.Option("persist", "window_position", QtCore.QPoint()), - config.Option("persist", "window_size", QtCore.QSize(780, 560)), config.Option("persist", "bottom_splitter_state", QtCore.QByteArray()), config.BoolOption("persist", "window_maximized", False), config.BoolOption("persist", "view_cover_art", True), @@ -183,16 +184,7 @@ class MainWindow(QtWidgets.QMainWindow): def saveWindowState(self): config.persist["window_state"] = self.saveState() isMaximized = int(self.windowState()) & QtCore.Qt.WindowMaximized != 0 - if isMaximized: - # FIXME: this doesn't include the window frame - geom = self.normalGeometry() - config.persist["window_position"] = geom.topLeft() - config.persist["window_size"] = geom.size() - else: - pos = self.pos() - if not pos.isNull(): - config.persist["window_position"] = pos - config.persist["window_size"] = self.size() + self.save_geometry() config.persist["window_maximized"] = isMaximized config.persist["view_cover_art"] = self.show_cover_art_action.isChecked() config.persist["view_toolbar"] = self.show_toolbar_action.isChecked() @@ -205,16 +197,7 @@ class MainWindow(QtWidgets.QMainWindow): @restore_method def restoreWindowState(self): self.restoreState(config.persist["window_state"]) - pos = config.persist["window_position"] - size = config.persist["window_size"] - self._desktopgeo = self.tagger.desktop().screenGeometry() - if (pos.x() > 0 and pos.y() > 0 - and pos.x() + size.width() < self._desktopgeo.width() - and pos.y() + size.height() < self._desktopgeo.height()): - self.move(pos) - if size.width() <= 0 or size.height() <= 0: - size = QtCore.QSize(780, 560) - self.resize(size) + self.restore_geometry() if config.persist["window_maximized"]: self.setWindowState(QtCore.Qt.WindowMaximized) bottom_splitter_state = config.persist["bottom_splitter_state"] diff --git a/picard/ui/options/dialog.py b/picard/ui/options/dialog.py index 7d1eec053..9ee3d1df6 100644 --- a/picard/ui/options/dialog.py +++ b/picard/ui/options/dialog.py @@ -47,9 +47,10 @@ from picard.ui.options import ( class OptionsDialog(PicardDialog): + defaultsize = QtCore.QSize(560, 400) + autorestore = False + options = [ - config.Option("persist", "options_position", QtCore.QPoint()), - config.Option("persist", "options_size", QtCore.QSize(560, 400)), config.Option("persist", "options_splitter", QtCore.QByteArray()), ] @@ -119,6 +120,7 @@ class OptionsDialog(PicardDialog): self.ui.pages_tree.itemSelectionChanged.connect(self.switch_page) self.restoreWindowState() + self.finished.connect(self.saveWindowState) for page in self.pages: page.load() @@ -143,26 +145,14 @@ class OptionsDialog(PicardDialog): return for page in self.pages: page.save() - self.saveWindowState() - QtWidgets.QDialog.accept(self) - - def closeEvent(self, event): - self.saveWindowState() - event.accept() + super().accept() def saveWindowState(self): - pos = self.pos() - if not pos.isNull(): - config.persist["options_position"] = pos - config.persist["options_size"] = self.size() config.persist["options_splitter"] = self.ui.splitter.saveState() @restore_method def restoreWindowState(self): - pos = config.persist["options_position"] - if pos.x() > 0 and pos.y() > 0: - self.move(pos) - self.resize(config.persist["options_size"]) + self.restore_geometry() self.ui.splitter.restoreState(config.persist["options_splitter"]) def restore_all_defaults(self): diff --git a/picard/ui/searchdialog/__init__.py b/picard/ui/searchdialog/__init__.py index ad064421c..876b3d4af 100644 --- a/picard/ui/searchdialog/__init__.py +++ b/picard/ui/searchdialog/__init__.py @@ -158,6 +158,8 @@ def to_seconds(timestr): class SearchDialog(PicardDialog): + defaultsize = QtCore.QSize(720, 360) + autorestore = False scrolled = pyqtSignal() def __init__(self, parent, accept_button_title, show_search=True): @@ -172,6 +174,7 @@ class SearchDialog(PicardDialog): # matching label as values self.columns = None self.sorting_enabled = True + self.finished.connect(self.save_state) @property def columns(self): @@ -340,21 +343,13 @@ class SearchDialog(PicardDialog): idx = self.table.selectionModel().selectedRows()[0] row = self.table.itemFromIndex(idx).data(QtCore.Qt.UserRole) self.accept_event(row) - self.save_state() - QtWidgets.QDialog.accept(self) - - def reject(self): - self.save_state() - QtWidgets.QDialog.reject(self) + super().accept() @restore_method def restore_state(self): - size = config.persist[self.dialog_window_size] - if size: - self.resize(size) + self.restore_geometry() if self.show_search: self.search_box.restore_checkbox_state() - log.debug("restore_state: %s" % self.dialog_window_size) @restore_method def restore_table_header_state(self): @@ -368,8 +363,6 @@ class SearchDialog(PicardDialog): def save_state(self): if self.table: self.save_table_header_state() - config.persist[self.dialog_window_size] = self.size() - log.debug("save_state: %s" % self.dialog_window_size) def save_table_header_state(self): state = self.table.horizontalHeader().saveState() diff --git a/picard/ui/searchdialog/album.py b/picard/ui/searchdialog/album.py index f7ffa2c2e..a11f9250f 100644 --- a/picard/ui/searchdialog/album.py +++ b/picard/ui/searchdialog/album.py @@ -117,11 +117,9 @@ class CoverCell: class AlbumSearchDialog(SearchDialog): - dialog_window_size = "albumsearchdialog_window_size" dialog_header_state = "albumsearchdialog_header_state" options = [ - config.Option("persist", dialog_window_size, QtCore.QSize(720, 360)), config.Option("persist", dialog_header_state, QtCore.QByteArray()) ] diff --git a/picard/ui/searchdialog/artist.py b/picard/ui/searchdialog/artist.py index 82f727d23..47ebdb842 100644 --- a/picard/ui/searchdialog/artist.py +++ b/picard/ui/searchdialog/artist.py @@ -28,11 +28,9 @@ from picard.ui.searchdialog import SearchDialog, Retry, BY_NUMBER class ArtistSearchDialog(SearchDialog): - dialog_window_size = "artistsearchdialog_window_size" dialog_header_state = "artistsearchdialog_header_state" options = [ - config.Option("persist", dialog_window_size, QtCore.QSize(720, 360)), config.Option("persist", dialog_header_state, QtCore.QByteArray()) ] diff --git a/picard/ui/searchdialog/track.py b/picard/ui/searchdialog/track.py index 9e36a4524..f10018797 100644 --- a/picard/ui/searchdialog/track.py +++ b/picard/ui/searchdialog/track.py @@ -37,11 +37,9 @@ from picard.ui.searchdialog import SearchDialog, Retry, BY_DURATION, BY_NUMBER class TrackSearchDialog(SearchDialog): - dialog_window_size = "tracksearchdialog_window_size" dialog_header_state = "tracksearchdialog_header_state" options = [ - config.Option("persist", dialog_window_size, QtCore.QSize(720, 360)), config.Option("persist", dialog_header_state, QtCore.QByteArray()) ] diff --git a/picard/ui/tagsfromfilenames.py b/picard/ui/tagsfromfilenames.py index 8ce9b6ba8..0b55c8711 100644 --- a/picard/ui/tagsfromfilenames.py +++ b/picard/ui/tagsfromfilenames.py @@ -24,16 +24,15 @@ from picard import config from picard.ui.util import StandardButton from picard.ui import PicardDialog from picard.ui.ui_tagsfromfilenames import Ui_TagsFromFileNamesDialog -from picard.util import restore_method from picard.util.tags import display_tag_name class TagsFromFileNamesDialog(PicardDialog): + defaultsize = QtCore.QSize(560, 400) + options = [ config.TextOption("persist", "tags_from_filenames_format", ""), - config.Option("persist", "tags_from_filenames_position", QtCore.QPoint()), - config.Option("persist", "tags_from_filenames_size", QtCore.QSize(560, 400)), ] def __init__(self, files, parent=None): @@ -64,7 +63,6 @@ class TagsFromFileNamesDialog(PicardDialog): self.ui.buttonbox.rejected.connect(self.reject) self.ui.preview.clicked.connect(self.preview) self.ui.files.setHeaderLabels([_("File Name")]) - self.restoreWindowState() self.files = files self.items = [] for file in files: @@ -127,26 +125,4 @@ class TagsFromFileNamesDialog(PicardDialog): file.metadata[name] = value file.update() config.persist["tags_from_filenames_format"] = self.ui.format.currentText() - self.saveWindowState() - QtWidgets.QDialog.accept(self) - - def reject(self): - self.saveWindowState() - QtWidgets.QDialog.reject(self) - - def closeEvent(self, event): - self.saveWindowState() - event.accept() - - def saveWindowState(self): - pos = self.pos() - if not pos.isNull(): - config.persist["tags_from_filenames_position"] = pos - config.persist["tags_from_filenames_size"] = self.size() - - @restore_method - def restoreWindowState(self): - pos = config.persist["tags_from_filenames_position"] - if pos.x() > 0 and pos.y() > 0: - self.move(pos) - self.resize(config.persist["tags_from_filenames_size"]) + super().accept()