Remove old open project dialog

This commit is contained in:
Veronica Berglyd Olsen
2023-12-29 12:16:52 +01:00
parent 18b3edbf87
commit bbabc41ac2
5 changed files with 3 additions and 436 deletions
-12
View File
@@ -112,7 +112,6 @@ class Config:
self._mainWinSize = [1200, 650] # Last size of the main GUI window
self._welcomeSize = [800, 500] # Last size of the welcome window
self._prefsWinSize = [700, 615] # Last size of the Preferences dialog
self._projLoadCols = [280, 60, 160] # Last columns widths of the Project Load dialog
self._mainPanePos = [300, 800] # Last position of the main window splitter
self._viewPanePos = [500, 150] # Last position of the document viewer splitter
self._outlnPanePos = [500, 150] # Last position of the outline panel splitter
@@ -258,10 +257,6 @@ class Config:
def preferencesWinSize(self) -> list[int]:
return [int(x*self.guiScale) for x in self._prefsWinSize]
@property
def projLoadColWidths(self) -> list[int]:
return [int(x*self.guiScale) for x in self._projLoadCols]
@property
def mainPanePos(self) -> list[int]:
return [int(x*self.guiScale) for x in self._mainPanePos]
@@ -323,11 +318,6 @@ class Config:
self._prefsWinSize[1] = int(height/self.guiScale)
return
def setProjLoadColWidths(self, widths: list[int]) -> None:
"""Set the column widths of the Load Project dialog."""
self._projLoadCols = [int(x/self.guiScale) for x in widths]
return
def setMainPanePos(self, pos: list[int]) -> None:
"""Set the position of the main GUI splitter."""
self._mainPanePos = [int(x/self.guiScale) for x in pos]
@@ -558,7 +548,6 @@ class Config:
self._mainWinSize = conf.rdIntList(sec, "mainwindow", self._mainWinSize)
self._welcomeSize = conf.rdIntList(sec, "welcome", self._welcomeSize)
self._prefsWinSize = conf.rdIntList(sec, "preferences", self._prefsWinSize)
self._projLoadCols = conf.rdIntList(sec, "projloadcols", self._projLoadCols)
self._mainPanePos = conf.rdIntList(sec, "mainpane", self._mainPanePos)
self._viewPanePos = conf.rdIntList(sec, "viewpane", self._viewPanePos)
self._outlnPanePos = conf.rdIntList(sec, "outlinepane", self._outlnPanePos)
@@ -666,7 +655,6 @@ class Config:
"mainwindow": self._packList(self._mainWinSize),
"welcome": self._packList(self._welcomeSize),
"preferences": self._packList(self._prefsWinSize),
"projloadcols": self._packList(self._projLoadCols),
"mainpane": self._packList(self._mainPanePos),
"viewpane": self._packList(self._viewPanePos),
"outlinepane": self._packList(self._outlnPanePos),
-294
View File
@@ -1,294 +0,0 @@
"""
novelWriter GUI Open Project
==============================
File History:
Created: 2020-02-26 [0.4.5] GuiProjectLoad
This file is a part of novelWriter
Copyright 20182023, Veronica Berglyd Olsen
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import logging
from typing import TYPE_CHECKING
from pathlib import Path
from datetime import datetime
from PyQt5.QtGui import QCloseEvent, QKeySequence
from PyQt5.QtCore import Qt, QSize, pyqtSlot
from PyQt5.QtWidgets import (
QDialog, QHBoxLayout, QVBoxLayout, QGridLayout, QPushButton, QTreeWidget,
QAbstractItemView, QTreeWidgetItem, QDialogButtonBox, QLabel, QShortcut,
QFileDialog, QLineEdit
)
from novelwriter import CONFIG, SHARED
from novelwriter.common import formatInt
from novelwriter.constants import nwFiles
if TYPE_CHECKING: # pragma: no cover
from novelwriter.guimain import GuiMain
logger = logging.getLogger(__name__)
class GuiProjectLoad(QDialog):
NONE_STATE = 0
NEW_STATE = 1
OPEN_STATE = 2
C_NAME = 0
C_COUNT = 1
C_TIME = 2
D_PATH = Qt.ItemDataRole.UserRole
def __init__(self, mainGui: GuiMain) -> None:
super().__init__(parent=mainGui)
logger.debug("Create: GuiProjectLoad")
self.setObjectName("GuiProjectLoad")
self.openState = self.NONE_STATE
self.openPath = None
sPx = CONFIG.pxInt(16)
nPx = CONFIG.pxInt(96)
iPx = SHARED.theme.baseIconSize
self.outerBox = QVBoxLayout()
self.innerBox = QHBoxLayout()
self.outerBox.setSpacing(sPx)
self.innerBox.setSpacing(sPx)
self.setWindowTitle(self.tr("Open Project"))
self.setMinimumWidth(CONFIG.pxInt(650))
self.setMinimumHeight(CONFIG.pxInt(400))
self.nwIcon = QLabel()
self.nwIcon.setPixmap(SHARED.theme.getPixmap("novelwriter", (nPx, nPx)))
self.innerBox.addWidget(self.nwIcon, 0, Qt.AlignTop)
self.projectForm = QGridLayout()
self.projectForm.setContentsMargins(0, 0, 0, 0)
self.listBox = QTreeWidget()
self.listBox.setSelectionMode(QAbstractItemView.SingleSelection)
self.listBox.setDragDropMode(QAbstractItemView.NoDragDrop)
self.listBox.setColumnCount(3)
self.listBox.setHeaderLabels([
self.tr("Working Title"),
self.tr("Words"),
self.tr("Last Opened"),
])
self.listBox.setRootIsDecorated(False)
self.listBox.itemSelectionChanged.connect(self._doSelectRecent)
self.listBox.itemDoubleClicked.connect(self._doOpenRecent)
self.listBox.setIconSize(QSize(iPx, iPx))
treeHead = self.listBox.headerItem()
if treeHead:
treeHead.setTextAlignment(self.C_COUNT, Qt.AlignRight)
treeHead.setTextAlignment(self.C_TIME, Qt.AlignRight)
self.lblRecent = QLabel("<b>%s</b>" % self.tr("Recently Opened Projects"))
self.lblPath = QLabel("<b>%s</b>" % self.tr("Path"))
self.selPath = QLineEdit("")
self.selPath.setReadOnly(True)
self.browseButton = QPushButton(SHARED.theme.getIcon("browse"), "", self)
self.browseButton.clicked.connect(self._doBrowse)
self.projectForm.addWidget(self.lblRecent, 0, 0, 1, 3)
self.projectForm.addWidget(self.listBox, 1, 0, 1, 3)
self.projectForm.addWidget(self.lblPath, 2, 0, 1, 1)
self.projectForm.addWidget(self.selPath, 2, 1, 1, 1)
self.projectForm.addWidget(self.browseButton, 2, 2, 1, 1)
self.projectForm.setColumnStretch(0, 0)
self.projectForm.setColumnStretch(1, 1)
self.projectForm.setColumnStretch(2, 0)
self.projectForm.setVerticalSpacing(CONFIG.pxInt(4))
self.projectForm.setHorizontalSpacing(CONFIG.pxInt(8))
self.innerBox.addLayout(self.projectForm)
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Open | QDialogButtonBox.Cancel)
self.buttonBox.accepted.connect(self._doOpenRecent)
self.buttonBox.rejected.connect(self._doCancel)
self.newButton = self.buttonBox.addButton(self.tr("New"), QDialogButtonBox.ActionRole)
self.newButton.clicked.connect(self._doNewProject)
self.delButton = self.buttonBox.addButton(self.tr("Remove"), QDialogButtonBox.ActionRole)
self.delButton.clicked.connect(self._doDeleteRecent)
self.outerBox.addLayout(self.innerBox)
self.outerBox.addWidget(self.buttonBox)
self.setLayout(self.outerBox)
self._populateList()
self._doSelectRecent()
keyDelete = QShortcut(self.listBox)
keyDelete.setKey(QKeySequence(Qt.Key_Delete))
keyDelete.activated.connect(self._doDeleteRecent)
logger.debug("Ready: GuiProjectLoad")
return
def __del__(self) -> None: # pragma: no cover
logger.debug("Delete: GuiProjectLoad")
return
##
# Private Slots
##
@pyqtSlot()
def _doOpenRecent(self) -> None:
"""Close the dialog window with a recent project selected."""
self._saveSettings()
self.openPath = None
self.openState = self.NONE_STATE
selItems = self.listBox.selectedItems()
if selItems:
self.openPath = selItems[0].data(self.C_NAME, self.D_PATH)
self.openState = self.OPEN_STATE
self.accept()
return
@pyqtSlot()
def _doSelectRecent(self) -> None:
"""Update path when a recent item has been selected."""
selList = self.listBox.selectedItems()
if selList:
self.selPath.setText(selList[0].data(self.C_NAME, self.D_PATH))
return
@pyqtSlot()
def _doBrowse(self) -> None:
"""Browse for a folder path."""
extFilter = [
self.tr("novelWriter Project File ({0})").format(nwFiles.PROJ_FILE),
self.tr("All files ({0})").format("*"),
]
projFile, _ = QFileDialog.getOpenFileName(
self, self.tr("Open Project"), "", filter=";;".join(extFilter)
)
if projFile:
thePath = Path(projFile).absolute()
self.selPath.setText(str(thePath))
self.openPath = thePath
self.openState = self.OPEN_STATE
self.accept()
return
@pyqtSlot()
def _doCancel(self) -> None:
"""Close the dialog window without doing anything."""
self.openPath = None
self.openState = self.NONE_STATE
self.close()
return
@pyqtSlot()
def _doNewProject(self) -> None:
"""Create a new project."""
self._saveSettings()
self.openPath = None
self.openState = self.NEW_STATE
self.accept()
return
@pyqtSlot()
def _doDeleteRecent(self) -> None:
"""Remove an entry from the recent projects list."""
selList = self.listBox.selectedItems()
if selList:
projName = selList[0].text(self.C_NAME)
msgYes = SHARED.question(self.tr(
"Remove '{0}' from the recent projects list? "
"The project files will not be deleted."
).format(projName))
if msgYes:
CONFIG.recentProjects.remove(
selList[0].data(self.C_NAME, self.D_PATH)
)
self._populateList()
return
##
# Events
##
def closeEvent(self, event: QCloseEvent) -> None:
"""Capture the user closing the dialog and save settings."""
self._saveSettings()
event.accept()
return
##
# Internal Functions
##
def _saveSettings(self) -> None:
"""Save the changes made to the dialog."""
colWidths = [0, 0, 0]
colWidths[self.C_NAME] = self.listBox.columnWidth(self.C_NAME)
colWidths[self.C_COUNT] = self.listBox.columnWidth(self.C_COUNT)
colWidths[self.C_TIME] = self.listBox.columnWidth(self.C_TIME)
CONFIG.setProjLoadColWidths(colWidths)
return
def _populateList(self) -> None:
"""Populate the list box with recent project data."""
self.listBox.clear()
dataList = CONFIG.recentProjects.listEntries()
sortList = sorted(dataList, key=lambda x: x[3], reverse=True)
nwxIcon = SHARED.theme.getIcon("proj_nwx")
for path, title, words, time in sortList:
newItem = QTreeWidgetItem([""]*4)
newItem.setIcon(self.C_NAME, nwxIcon)
newItem.setText(self.C_NAME, title)
newItem.setData(self.C_NAME, self.D_PATH, path)
newItem.setText(self.C_COUNT, formatInt(words))
newItem.setText(self.C_TIME, datetime.fromtimestamp(time).strftime("%x %X"))
newItem.setTextAlignment(self.C_NAME, Qt.AlignLeft | Qt.AlignVCenter)
newItem.setTextAlignment(self.C_COUNT, Qt.AlignRight | Qt.AlignVCenter)
newItem.setTextAlignment(self.C_TIME, Qt.AlignRight | Qt.AlignVCenter)
newItem.setFont(self.C_TIME, SHARED.theme.guiFontFixed)
self.listBox.addTopLevelItem(newItem)
self.listBox.setCurrentItem(self.listBox.topLevelItem(0))
projColWidth = CONFIG.projLoadColWidths
if len(projColWidth) == 3:
self.listBox.setColumnWidth(self.C_NAME, projColWidth[self.C_NAME])
self.listBox.setColumnWidth(self.C_COUNT, projColWidth[self.C_COUNT])
self.listBox.setColumnWidth(self.C_TIME, projColWidth[self.C_TIME])
return
# END Class GuiProjectLoad
+3 -18
View File
@@ -30,11 +30,11 @@ from time import time
from pathlib import Path
from datetime import datetime
from PyQt5.QtCore import Qt, QTimer, pyqtSlot
from PyQt5.QtGui import QCloseEvent, QCursor, QIcon
from PyQt5.QtCore import Qt, QTimer, pyqtSlot
from PyQt5.QtWidgets import (
QDialog, QFileDialog, QHBoxLayout, QMainWindow, QMessageBox, QShortcut,
QSplitter, QStackedWidget, QVBoxLayout, QWidget, qApp
QFileDialog, QHBoxLayout, QMainWindow, QMessageBox, QShortcut, QSplitter,
QStackedWidget, QVBoxLayout, QWidget, qApp
)
from novelwriter import CONFIG, SHARED, __hexversion__
@@ -51,7 +51,6 @@ from novelwriter.gui.itemdetails import GuiItemDetails
from novelwriter.gui.docviewerpanel import GuiDocViewerPanel
from novelwriter.dialogs.about import GuiAbout
from novelwriter.dialogs.updates import GuiUpdates
from novelwriter.dialogs.projload import GuiProjectLoad
from novelwriter.dialogs.wordlist import GuiWordList
from novelwriter.dialogs.preferences import GuiPreferences
from novelwriter.dialogs.projdetails import GuiProjectDetails
@@ -799,20 +798,6 @@ class GuiMain(QMainWindow):
# Main Dialogs
##
def showProjectLoadDialog(self) -> None:
"""Open the projects dialog for selecting either existing
projects from a cache of recently opened projects, or provide a
browse button for projects not yet cached. Selecting to create a
new project is forwarded to the new project wizard.
"""
dlgProj = GuiProjectLoad(self)
dlgProj.exec_()
if dlgProj.result() == QDialog.Accepted:
if dlgProj.openState == GuiProjectLoad.OPEN_STATE:
self.openProject(dlgProj.openPath)
return
@pyqtSlot()
def showWelcomeDialog(self) -> None:
"""Open the welcome dialog."""
-13
View File
@@ -291,19 +291,6 @@ def testBaseConfig_SettersGetters(fncPath):
tstConf.setPreferencesWinSize(700, 615)
# Project Settings Tree Columns
tstConf.guiScale = 2.0
tstConf.setProjLoadColWidths([10, 20, 30])
assert tstConf.projLoadColWidths == [10, 20, 30]
assert tstConf._projLoadCols == [5, 10, 15]
tstConf.guiScale = 1.0
tstConf.setProjLoadColWidths([10, 20, 30])
assert tstConf.projLoadColWidths == [10, 20, 30]
assert tstConf._projLoadCols == [10, 20, 30]
tstConf.setProjLoadColWidths([200, 60, 140])
# Main Pane Splitter
tstConf.guiScale = 2.0
tstConf.setMainPanePos([200, 700])
-99
View File
@@ -1,99 +0,0 @@
"""
novelWriter Project Load Dialog Class Tester
==============================================
This file is a part of novelWriter
Copyright 20182023, Veronica Berglyd Olsen
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import pytest
from tools import buildTestProject, getGuiItem
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
QDialogButtonBox, QTreeWidgetItem, QDialog, QAction, QFileDialog
)
from novelwriter.dialogs.projload import GuiProjectLoad
@pytest.mark.gui
def testDlgLoadProject_Main(qtbot, monkeypatch, nwGUI, projPath):
"""Test the load project wizard.
"""
buildTestProject(nwGUI, projPath)
assert nwGUI.closeProject()
monkeypatch.setattr(GuiProjectLoad, "exec_", lambda *a: None)
monkeypatch.setattr(GuiProjectLoad, "result", lambda *a: QDialog.Accepted)
nwGUI.mainMenu.aOpenProject.activate(QAction.Trigger)
qtbot.waitUntil(lambda: getGuiItem("GuiProjectLoad") is not None, timeout=1000)
nwLoad = getGuiItem("GuiProjectLoad")
assert isinstance(nwLoad, GuiProjectLoad)
nwLoad.show()
recentCount = nwLoad.listBox.topLevelItemCount()
assert recentCount > 0
selItem = nwLoad.listBox.topLevelItem(0)
selPath = selItem.data(nwLoad.C_NAME, Qt.ItemDataRole.UserRole)
assert isinstance(selItem, QTreeWidgetItem)
nwLoad.selPath.setText("")
nwLoad.listBox.setCurrentItem(selItem)
nwLoad._doSelectRecent()
assert nwLoad.selPath.text() == selPath
qtbot.mouseClick(nwLoad.buttonBox.button(QDialogButtonBox.Open), Qt.LeftButton)
assert nwLoad.openPath == selPath
assert nwLoad.openState == nwLoad.OPEN_STATE
# Just create a new project load from scratch for the rest of the test
del nwLoad
nwGUI.mainMenu.aOpenProject.activate(QAction.Trigger)
qtbot.waitUntil(lambda: getGuiItem("GuiProjectLoad") is not None, timeout=1000)
nwLoad = getGuiItem("GuiProjectLoad")
assert isinstance(nwLoad, GuiProjectLoad)
nwLoad.show()
qtbot.mouseClick(nwLoad.buttonBox.button(QDialogButtonBox.Cancel), Qt.LeftButton)
assert nwLoad.openPath is None
assert nwLoad.openState == nwLoad.NONE_STATE
nwLoad.show()
qtbot.mouseClick(nwLoad.newButton, Qt.LeftButton)
assert nwLoad.openPath is None
assert nwLoad.openState == nwLoad.NEW_STATE
nwLoad.show()
nwLoad._doDeleteRecent()
assert nwLoad.listBox.topLevelItemCount() == recentCount - 1
getFile = str(projPath / "nwProject.nwx")
monkeypatch.setattr(QFileDialog, "getOpenFileName", lambda *a, **k: (getFile, None))
qtbot.mouseClick(nwLoad.browseButton, Qt.LeftButton)
assert nwLoad.openPath == projPath / "nwProject.nwx"
assert nwLoad.openState == nwLoad.OPEN_STATE
nwLoad.close()
# qtbot.stop()
# END Test testDlgLoadProject_Main