Add a "New Project" button.

startup
Ben Niemann 2019-06-07 22:52:44 +02:00
parent b43eb88d73
commit 11f79fec31
4 changed files with 183 additions and 33 deletions

View File

@ -110,9 +110,14 @@ class ProjectTabPage(ui_base.CommonMixin, QtWidgets.QWidget):
self.__page_cleanup_func = cleanup_func
def showOpenDialog(self, project_registry: project_registry_lib.ProjectRegistry) -> None:
dialog = open_project_dialog.OpenProjectDialog(self, project_registry=project_registry)
dialog = open_project_dialog.OpenProjectDialog(
parent=self,
project_registry=project_registry,
context=self.context)
dialog.projectSelected.connect(
lambda project: self.call_async(self.__projectSelected(project)))
dialog.createProject.connect(
lambda path: self.call_async(self.__createProject(path)))
l1 = QtWidgets.QVBoxLayout()
l1.addSpacing(32)
@ -138,6 +143,17 @@ class ProjectTabPage(ui_base.CommonMixin, QtWidgets.QWidget):
self.__project_view = view
self.__setPage(view)
async def __createProject(self, path: str) -> None:
project = project_registry_lib.Project(
path=path, context=self.context)
self.showLoadSpinner("Creating project \"%s\"..." % project.name)
await self.app.setup_complete.wait()
await project.create()
view = project_view.ProjectView(project_connection=project, context=self.context)
await view.setup()
self.__project_view = view
self.__setPage(view)
def showLoadSpinner(self, message: str) -> None:
page = QtWidgets.QWidget(self)

View File

@ -20,8 +20,11 @@
#
# @end:license
import functools
import logging
from typing import Any, Dict
import os.path
import random
from typing import Any, Dict, List
from PyQt5.QtCore import Qt
from PyQt5 import QtCore
@ -30,6 +33,7 @@ from PyQt5 import QtWidgets
from . import project_registry as project_registry_lib
from . import slots
from . import ui_base
logger = logging.getLogger(__name__)
@ -124,6 +128,15 @@ class ProjectListView(QtWidgets.QListView):
self, selected: QtCore.QItemSelection, deselected: QtCore.QItemSelection) -> None:
self.numProjectsSelected.emit(len(self.selectedIndexes()))
def selectedProjects(self) -> List[project_registry_lib.Project]:
projects = []
for index in self.selectedIndexes():
item = index.model().item(index)
if isinstance(item, project_registry_lib.Project):
projects.append(item)
return projects
class FlatProjectListModel(QtCore.QAbstractListModel):
def __init__(self, project_registry: project_registry_lib.ProjectRegistry) -> None:
@ -150,27 +163,139 @@ class FlatProjectListModel(QtCore.QAbstractListModel):
return self.__registry.headerData(section, orientation, role)
class OpenProjectDialog(QtWidgets.QWidget):
class NewProjectDialog(ui_base.CommonMixin, QtWidgets.QDialog):
def __init__(self, **kwargs: Any) -> None:
super().__init__(**kwargs)
self.setWindowTitle("noisicaä - New Project")
self.setMinimumWidth(500)
self.__create_button = QtWidgets.QPushButton(self)
self.__create_button.setIcon(QtGui.QIcon.fromTheme('document-new'))
self.__create_button.setText("Create")
self.__create_button.clicked.connect(self.accept)
self.__close_button = QtWidgets.QPushButton(self)
self.__close_button.setIcon(QtGui.QIcon.fromTheme('window-close'))
self.__close_button.setText("Cancel")
self.__close_button.clicked.connect(self.reject)
self.__name = QtWidgets.QLineEdit(self)
self.__name.textChanged.connect(self.__nameChanged)
self.__error = QtWidgets.QLabel(self)
palette = QtGui.QPalette(self.__error.palette())
palette.setColor(QtGui.QPalette.WindowText, Qt.red)
self.__error.setPalette(palette)
self.__prev_name = QtWidgets.QToolButton(self)
self.__prev_name.setIcon(QtGui.QIcon.fromTheme('go-previous'))
self.__prev_name.clicked.connect(self.__prevNameClicked)
self.__next_name = QtWidgets.QToolButton(self)
self.__next_name.setIcon(QtGui.QIcon.fromTheme('go-next'))
self.__next_name.clicked.connect(self.__nextNameClicked)
self.__name_seed = random.randint(0, 1000000)
self.__generateName()
l4 = QtWidgets.QHBoxLayout()
l4.addWidget(self.__name, 1)
l4.addWidget(self.__prev_name)
l4.addWidget(self.__next_name)
l3 = QtWidgets.QFormLayout()
l3.addRow("Name:", l4)
l2 = QtWidgets.QHBoxLayout()
l2.addStretch(1)
l2.addWidget(self.__create_button)
l2.addWidget(self.__close_button)
l1 = QtWidgets.QVBoxLayout()
l1.addLayout(l3)
l1.addWidget(self.__error)
l1.addLayout(l2)
self.setLayout(l1)
def projectDir(self) -> str:
directory = '~/Music/Noisicaä'
directory = os.path.expanduser(directory)
directory = os.path.abspath(directory)
return directory
def projectName(self) -> str:
return self.__name.text()
def projectPath(self) -> str:
filename = self.projectName() + '.noise'
filename = filename.replace('%', '%25')
filename = filename.replace('/', '%2F')
return os.path.join(self.projectDir(), filename)
def __generateName(self) -> None:
rnd = random.Random(self.__name_seed)
name = "Song #%d" % rnd.randint(0, 1000)
self.__name.setText(name)
def __nextNameClicked(self) -> None:
self.__name_seed = (self.__name_seed + 1) % 1000000
self.__generateName()
def __prevNameClicked(self) -> None:
self.__name_seed = (self.__name_seed - 1) % 1000000
self.__generateName()
def __nameChanged(self, text: str) -> None:
self.__create_button.setEnabled(False)
self.__error.setVisible(True)
if not text:
self.__error.setText("Enter a valid project name.")
else:
self.__create_button.setEnabled(True)
self.__error.setVisible(False)
class OpenProjectDialog(ui_base.CommonMixin, QtWidgets.QWidget):
projectSelected = QtCore.pyqtSignal(project_registry_lib.Project)
createProject = QtCore.pyqtSignal(str)
def __init__(
self,
parent: QtWidgets.QWidget = None,
*,
project_registry: project_registry_lib.ProjectRegistry
self, *,
project_registry: project_registry_lib.ProjectRegistry,
**kwargs: Any,
) -> None:
super().__init__(parent)
super().__init__(**kwargs)
self.__project_registry = project_registry
self.__search = QtWidgets.QLineEdit(self)
self.__open_button = QtWidgets.QPushButton(self)
self.__open_button.setIcon(QtGui.QIcon.fromTheme('document-open'))
self.__open_button.setText("Open")
self.__open_button.setDisabled(True)
self.__open_button.clicked.connect(self.__openClicked)
self.__new_project_button = QtWidgets.QPushButton(self)
self.__new_project_button.setIcon(QtGui.QIcon.fromTheme('document-new'))
self.__new_project_button.setText("New project")
self.__new_project_button.clicked.connect(self.__newProjectClicked)
self.__new_folder_button = QtWidgets.QPushButton(self)
self.__new_folder_button.setIcon(QtGui.QIcon.fromTheme('folder-new'))
self.__new_folder_button.setText("New folder")
self.__new_folder_button.setDisabled(True)
self.__new_folder_button.clicked.connect(self.__newFolderClicked)
self.__delete_action = QtWidgets.QAction("Delete", self)
self.__delete_action.setIcon(QtGui.QIcon.fromTheme('edit-delete'))
self.__delete_action.setEnabled(False)
self.__delete_action.triggered.connect(self.__deleteClicked)
self.__archive_action = QtWidgets.QAction("Archive", self)
self.__archive_action.setEnabled(False)
self.__archive_action.triggered.connect(self.__archiveClicked)
self.__more_menu = QtWidgets.QMenu()
self.__more_menu.addAction(self.__delete_action)
@ -189,6 +314,8 @@ class OpenProjectDialog(QtWidgets.QWidget):
l1 = QtWidgets.QVBoxLayout()
l1.setContentsMargins(0, 0, 0, 0)
l1.addWidget(self.__open_button)
l1.addWidget(self.__new_project_button)
l1.addWidget(self.__new_folder_button)
l1.addWidget(self.__more_button)
l1.addStretch(1)
@ -210,5 +337,31 @@ class OpenProjectDialog(QtWidgets.QWidget):
self.__open_button.setDisabled(not enable)
self.__more_button.setDisabled(not enable)
def __openClicked(self) -> None:
selected_projects = self.__list.selectedProjects()
if len(selected_projects) == 1:
self.openProject(selected_projects[0])
def __newProjectClicked(self) -> None:
dialog = NewProjectDialog(parent=self, context=self.context)
dialog.setModal(True)
dialog.finished.connect(functools.partial(self.__newProjectDialogDone, dialog))
dialog.show()
def __newProjectDialogDone(self, dialog: NewProjectDialog, result: int) -> None:
if result != QtWidgets.QDialog.Accepted:
return
self.createProject.emit(dialog.projectPath())
def __newFolderClicked(self) -> None:
raise NotImplementedError
def __deleteClicked(self) -> None:
raise NotImplementedError
def __archiveClicked(self) -> None:
raise NotImplementedError
def openProject(self, project: project_registry_lib.Project) -> None:
self.projectSelected.emit(project)

View File

@ -31,7 +31,8 @@ class OpenProjectDialogTest(uitest.UITestCase):
await registry.setup()
try:
dialog = open_project_dialog.OpenProjectDialog(
project_registry=registry)
project_registry=registry,
context=self.context)
dialog.show()
finally:

View File

@ -22,6 +22,7 @@
import logging
import os.path
import urllib.parse
from typing import cast, Any, List, Iterable, Iterator
from PyQt5.QtCore import Qt
@ -81,7 +82,7 @@ class Project(ui_base.CommonMixin, Item):
@property
def name(self) -> str:
return os.path.splitext(os.path.basename(self.path))[0]
return urllib.parse.unquote(os.path.splitext(os.path.basename(self.path))[0])
async def __create_process(self) -> None:
self.client = music.ProjectClient(
@ -143,9 +144,8 @@ class ProjectRegistry(ui_base.CommonMixin, QtCore.QAbstractItemModel):
data_dir_name = filename[:-6] + '.data'
if data_dir_name in dirnames:
dirnames.remove(data_dir_name)
filepath = os.path.join(dirpath, filename)
projects.append(Project(path=filepath, context=self.context))
filepath = os.path.join(dirpath, filename)
projects.append(Project(path=filepath, context=self.context))
return projects
@ -196,23 +196,3 @@ class ProjectRegistry(ui_base.CommonMixin, QtCore.QAbstractItemModel):
self, section: int, orientation: Qt.Orientation, role: int = Qt.DisplayRole
) -> Any: # pragma: no coverage
return None
# def add_project(self, path: str) -> Project:
# project = Project(
# path,
# self.event_loop,
# self.server,
# self.process_manager,
# self.node_db,
# self.urid_mapper,
# self.tmp_dir)
# self.projects[path] = project
# return project
# async def close_project(self, project: Project) -> None:
# await project.close()
# del self.projects[project.path]
# async def close_all(self) -> None:
# for project in list(self.projects.values()):
# await self.close_project(project)