manuskript/manuskript/ui/welcome.py

464 lines
16 KiB
Python
Raw Normal View History

2015-06-24 04:22:39 +12:00
#!/usr/bin/env python
2016-02-07 00:34:22 +13:00
# --!-- coding: utf8 --!--
2015-06-24 04:22:39 +12:00
import locale
2015-06-25 20:01:28 +12:00
import imp
2016-02-07 00:34:22 +13:00
import os
2016-02-10 14:04:26 +13:00
from PyQt5.QtCore import QSettings, QRegExp, Qt, QDir
2016-02-07 00:34:22 +13:00
from PyQt5.QtGui import QIcon, QBrush, QColor, QStandardItemModel, QStandardItem
from PyQt5.QtWidgets import QWidget, QAction, QFileDialog, QSpinBox, QLineEdit, QLabel, QPushButton, QTreeWidgetItem, \
qApp, QMessageBox
2016-02-07 00:34:22 +13:00
from manuskript import loadSave
2016-02-07 00:34:22 +13:00
from manuskript import settings
from manuskript.enums import Outline
2016-02-10 14:04:26 +13:00
from manuskript.functions import mainWindow, iconFromColor, appPath
from manuskript.models.characterModel import characterModel
2017-11-16 08:33:27 +13:00
from manuskript.models import outlineItem, outlineModel
2016-02-07 00:34:22 +13:00
from manuskript.models.plotModel import plotModel
2016-02-09 02:44:39 +13:00
from manuskript.models.worldModel import worldModel
2016-02-07 00:34:22 +13:00
from manuskript.ui.welcome_ui import Ui_welcome
2017-11-15 02:48:28 +13:00
from manuskript.ui import style as S
2015-06-25 20:01:28 +12:00
try:
locale.setlocale(locale.LC_ALL, '')
except:
pass
2015-06-24 04:22:39 +12:00
class welcome(QWidget, Ui_welcome):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.setupUi(self)
2016-02-07 00:34:22 +13:00
self.template = []
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
self.mw = mainWindow()
self.btnOpen.clicked.connect(self.openFile)
self.btnCreate.clicked.connect(self.createFile)
self.chkLoadLastProject.toggled.connect(self.setAutoLoad)
self.tree.itemClicked.connect(self.changeTemplate)
2015-06-24 04:22:39 +12:00
self.btnAddLevel.clicked.connect(self.templateAddLevel)
self.btnAddWC.clicked.connect(self.templateAddWordCount)
2016-02-10 14:04:26 +13:00
self.btnCreateText = self.btnCreate.text()
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
self.populateTemplates()
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
def updateValues(self):
# Auto load
autoLoad, last = self.getAutoLoadValues()
self.chkLoadLastProject.setChecked(autoLoad)
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
# Recent Files
self.loadRecents()
2016-02-07 00:34:22 +13:00
###############################################################################
# AUTOLOAD
###############################################################################
2015-06-24 04:22:39 +12:00
def showEvent(self, event):
"""Waiting for things to be fully loaded to start opening projects."""
QWidget.showEvent(self, event)
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
# Auto load last project
autoLoad, last = self.getAutoLoadValues()
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
if autoLoad and last:
self.mw.loadProject(last)
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
def getAutoLoadValues(self):
sttgns = QSettings()
autoLoad = sttgns.value("autoLoad", type=bool)
2015-06-24 04:22:39 +12:00
if autoLoad and sttgns.contains("lastProject"):
last = sttgns.value("lastProject")
else:
last = ""
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
return autoLoad, last
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
def setAutoLoad(self, v):
QSettings().setValue("autoLoad", v)
2016-02-07 00:34:22 +13:00
###############################################################################
# RECENTS
###############################################################################
2015-06-24 04:22:39 +12:00
def loadRecents(self):
sttgns = QSettings()
2015-09-29 22:17:03 +13:00
self.mw.menuRecents.setIcon(QIcon.fromTheme("folder-recent"))
2015-06-24 04:22:39 +12:00
if sttgns.contains("recentFiles"):
lst = sttgns.value("recentFiles")
self.mw.menuRecents.clear()
for f in [f for f in lst if os.path.exists(f)]:
2015-06-24 04:22:39 +12:00
name = os.path.split(f)[1]
a = QAction(name, self)
a.setData(f)
a.setStatusTip(f)
a.triggered.connect(self.loadRecentFile)
self.mw.menuRecents.addAction(a)
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
self.btnRecent.setMenu(self.mw.menuRecents)
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
def appendToRecentFiles(self, project):
sttgns = QSettings()
if sttgns.contains("recentFiles"):
recentFiles = sttgns.value("recentFiles")
else:
recentFiles = []
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
while project in recentFiles:
recentFiles.remove(project)
recentFiles.insert(0, project)
recentFiles = recentFiles[:10]
sttgns.setValue("recentFiles", recentFiles)
def loadRecentFile(self):
act = self.sender()
self.appendToRecentFiles(act.data())
2016-03-31 21:50:20 +13:00
self.mw.closeProject()
2015-06-24 04:22:39 +12:00
self.mw.loadProject(act.data())
2016-02-07 00:34:22 +13:00
###############################################################################
# DIALOGS
###############################################################################
2015-06-24 04:22:39 +12:00
def openFile(self):
"""File dialog that request an existing file. For opening project."""
2016-02-07 00:34:22 +13:00
filename = QFileDialog.getOpenFileName(self,
self.tr("Open project"),
".",
self.tr("Manuskript project (*.msk);;All files (*)"))[0]
2015-06-24 04:22:39 +12:00
if filename:
self.appendToRecentFiles(filename)
self.mw.loadProject(filename)
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
def saveAsFile(self):
"""File dialog that request a file, existing or not.
2015-06-24 04:22:39 +12:00
Save datas to that file, which then becomes the current project."""
2016-02-07 00:34:22 +13:00
filename = QFileDialog.getSaveFileName(self,
self.tr("Save project as..."),
".",
self.tr("Manuskript project (*.msk)"))[0]
2015-06-24 04:22:39 +12:00
if filename:
if filename[-4:] != ".msk":
filename += ".msk"
2015-06-24 04:22:39 +12:00
self.appendToRecentFiles(filename)
loadSave.clearSaveCache() # Ensure all file(s) are saved under new filename
2015-06-24 04:22:39 +12:00
self.mw.saveDatas(filename)
# Update Window's project name with new filename
pName = os.path.split(filename)[1]
if pName.endswith('.msk'):
pName=pName[:-4]
self.mw.setWindowTitle(pName + " - " + self.tr("Manuskript"))
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
def createFile(self):
"""When starting a new project, ask for a place to save it.
Datas are not loaded from file, so they must be populated another way."""
2016-02-07 00:34:22 +13:00
filename = QFileDialog.getSaveFileName(self,
self.tr("Create New Project"),
".",
self.tr("Manuskript project (*.msk)"))[0]
2015-06-24 04:22:39 +12:00
if filename:
2016-03-12 03:05:40 +13:00
if filename[-4:] != ".msk":
filename += ".msk"
if os.path.exists(filename):
# Check if okay to overwrite existing project
result = QMessageBox.warning(self, self.tr("Warning"),
self.tr("Overwrite existing project {} ?").format(filename),
QMessageBox.Ok|QMessageBox.Cancel, QMessageBox.Cancel)
if result == QMessageBox.Cancel:
return
# Create new project
2015-06-24 04:22:39 +12:00
self.appendToRecentFiles(filename)
self.loadDefaultDatas()
self.mw.loadProject(filename, loadFromFile=False)
2016-02-07 00:34:22 +13:00
###############################################################################
# TEMPLATES
###############################################################################
2015-06-24 04:22:39 +12:00
def templates(self):
return [
(self.tr("Empty fiction"), [], "Fiction"),
2015-06-24 04:22:39 +12:00
(self.tr("Novel"), [
2016-02-07 00:34:22 +13:00
(20, self.tr("Chapter")),
(5, self.tr("Scene")),
(500, None) # A line with None is word count
], "Fiction"),
2015-06-24 04:22:39 +12:00
(self.tr("Novella"), [
2016-02-07 00:34:22 +13:00
(10, self.tr("Chapter")),
(5, self.tr("Scene")),
(500, None)
], "Fiction"),
2015-06-24 04:22:39 +12:00
(self.tr("Short Story"), [
2016-02-07 00:34:22 +13:00
(10, self.tr("Scene")),
2015-06-24 04:22:39 +12:00
(1000, None)
], "Fiction"),
2015-06-24 04:22:39 +12:00
(self.tr("Trilogy"), [
2016-02-07 00:34:22 +13:00
(3, self.tr("Book")),
(3, self.tr("Section")),
(10, self.tr("Chapter")),
(5, self.tr("Scene")),
(500, None)
], "Fiction"),
(self.tr("Empty non-fiction"), [], "Non-fiction"),
2015-06-24 04:22:39 +12:00
(self.tr("Research paper"), [
2016-02-07 00:34:22 +13:00
(3, self.tr("Section")),
2015-06-24 04:22:39 +12:00
(1000, None)
], "Non-fiction")
2016-02-07 00:34:22 +13:00
]
2015-06-24 04:22:39 +12:00
def changeTemplate(self, item, column):
2015-06-24 09:59:56 +12:00
template = [i for i in self.templates() if i[0] == item.text(0)]
2016-02-10 14:04:26 +13:00
self.btnCreate.setText(self.btnCreateText)
# Selected item is a template
2015-06-24 09:59:56 +12:00
if len(template):
self.template = template[0]
2015-06-24 09:59:56 +12:00
self.updateTemplate()
# Selected item is a sample project
2016-02-10 14:04:26 +13:00
elif item.data(0, Qt.UserRole):
name = item.data(0, Qt.UserRole)
# Clear templates
self.template = self.templates()[0]
2016-02-10 14:04:26 +13:00
self.updateTemplate()
# Change button text
self.btnCreate.setText("Open {}".format(name))
# Load project
self.mw.loadProject(appPath("sample-projects/{}".format(name)))
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
def updateTemplate(self):
# Clear layout
def clearLayout(l):
while l.count() != 0:
i = l.takeAt(0)
if i.widget():
i.widget().deleteLater()
if i.layout():
clearLayout(i.layout())
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
clearLayout(self.lytTemplate)
2016-02-07 00:34:22 +13:00
# self.templateLayout.addStretch()
# l = QGridLayout()
# self.templateLayout.addLayout(l)
2015-06-24 04:22:39 +12:00
k = 0
hasWC = False
for d in self.template[1]:
2015-06-24 04:22:39 +12:00
spin = QSpinBox(self)
spin.setRange(0, 999999)
spin.setValue(d[0])
# Storing the level of the template in that spinbox, so we can use
# it to update the template when valueChanged on that spinbox
# (we do that in self.updateWordCount for convenience).
spin.setProperty("templateIndex", self.template[1].index(d))
2015-06-24 04:22:39 +12:00
spin.valueChanged.connect(self.updateWordCount)
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
if d[1] != None:
txt = QLineEdit(self)
txt.setText(d[1])
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
else:
hasWC = True
txt = QLabel(self.tr("words each."), self)
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
if k != 0:
of = QLabel(self.tr("of"), self)
self.lytTemplate.addWidget(of, k, 0)
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
btn = QPushButton("", self)
btn.setIcon(QIcon.fromTheme("edit-delete"))
btn.setProperty("deleteRow", k)
2017-11-15 02:48:28 +13:00
btn.setFlat(True)
2015-06-24 04:22:39 +12:00
btn.clicked.connect(self.deleteTemplateRow)
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
self.lytTemplate.addWidget(btn, k, 3)
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
self.lytTemplate.addWidget(spin, k, 1)
self.lytTemplate.addWidget(txt, k, 2)
k += 1
2016-02-07 00:34:22 +13:00
self.btnAddWC.setEnabled(not hasWC and len(self.template[1]) > 0)
2015-06-24 04:22:39 +12:00
self.btnAddLevel.setEnabled(True)
self.lblTotal.setVisible(hasWC)
self.updateWordCount()
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
def templateAddLevel(self):
if len(self.template[1]) > 0 and \
self.template[1][len(self.template[1]) - 1][1] == None:
2015-06-24 04:22:39 +12:00
# has word cound, so insert before
self.template[1].insert(len(self.template[1]) - 1, (10, self.tr("Text")))
2015-06-24 04:22:39 +12:00
else:
# No word count, so insert at end
self.template[1].append((10, self.tr("Something")))
2015-06-24 04:22:39 +12:00
self.updateTemplate()
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
def templateAddWordCount(self):
self.template[1].append((500, None))
2015-06-24 04:22:39 +12:00
self.updateTemplate()
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
def deleteTemplateRow(self):
btn = self.sender()
row = btn.property("deleteRow")
self.template[1].pop(row)
2015-06-24 04:22:39 +12:00
self.updateTemplate()
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
def updateWordCount(self):
"""
Updates the word count of the template, and displays it in a label.
Also, updates self.template, which is used to create the items when
calling self.createFile.
"""
2015-06-24 04:22:39 +12:00
total = 1
# Searching for every spinboxes on the widget, and multiplying
# their values to get the number of words.
2015-06-24 04:22:39 +12:00
for s in self.findChildren(QSpinBox, QRegExp(".*"),
Qt.FindChildrenRecursively):
total = total * s.value()
2016-02-07 00:34:22 +13:00
# Update self.template to reflect the changed values
templateIndex = s.property("templateIndex")
self.template[1][templateIndex] = (
s.value(),
self.template[1][templateIndex][1])
2016-02-07 00:34:22 +13:00
if total == 1:
2015-06-24 04:22:39 +12:00
total = 0
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
self.lblTotal.setText(self.tr("<b>Total:</b> {} words (~ {} pages)").format(
2016-02-07 00:34:22 +13:00
locale.format("%d", total, grouping=True),
locale.format("%d", total / 250, grouping=True)
2015-06-24 04:22:39 +12:00
))
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
def addTopLevelItem(self, name):
item = QTreeWidgetItem(self.tree, [name])
2017-11-15 02:48:28 +13:00
item.setBackground(0, QBrush(QColor(S.highlightLight)))
item.setForeground(0, QBrush(QColor(S.highlightedTextDark)))
2015-06-24 04:22:39 +12:00
item.setTextAlignment(0, Qt.AlignCenter)
item.setFlags(Qt.ItemIsEnabled)
f = item.font(0)
f.setBold(True)
item.setFont(0, f)
return item
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
def populateTemplates(self):
self.tree.clear()
self.tree.setIndentation(0)
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
# Add templates
item = self.addTopLevelItem(self.tr("Fiction"))
templates = [i for i in self.templates() if i[2] == "Fiction"]
for t in templates:
sub = QTreeWidgetItem(item, [t[0]])
# Add templates: non-fiction
item = self.addTopLevelItem(self.tr("Non-fiction"))
templates = [i for i in self.templates() if i[2] == "Non-fiction"]
2015-06-24 04:22:39 +12:00
for t in templates:
sub = QTreeWidgetItem(item, [t[0]])
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
# Add Demo project
item = self.addTopLevelItem(self.tr("Demo projects"))
2016-02-10 14:04:26 +13:00
dir = QDir(appPath("sample-projects"))
for f in dir.entryList(["*.msk"], filters=QDir.Files):
sub = QTreeWidgetItem(item, [f[:-4]])
sub.setData(0, Qt.UserRole, f)
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
self.tree.expandAll()
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
def loadDefaultDatas(self):
2016-02-07 00:34:22 +13:00
2015-06-25 20:01:28 +12:00
# Empty settings
imp.reload(settings)
settings.initDefaultValues()
if self.template:
t = [i for i in self.templates() if i[0] == self.template[0]]
if t and t[0][2] == "Non-fiction":
settings.viewMode = "simple"
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
# Données
self.mw.mdlFlatData = QStandardItemModel(2, 8, self.mw)
2015-06-24 04:22:39 +12:00
# Persos
2016-02-07 00:34:22 +13:00
# self.mw.mdlPersos = QStandardItemModel(0, 0, self.mw)
self.mw.mdlCharacter = characterModel(self.mw)
2016-02-07 00:34:22 +13:00
# self.mdlPersosProxy = None # persosProxyModel() # None
# self.mw.mdlPersosProxy = persosProxyModel(self.mw)
2015-06-24 04:22:39 +12:00
2016-02-07 00:34:22 +13:00
# self.mw.mdlPersosInfos = QStandardItemModel(1, 0, self.mw)
# self.mw.mdlPersosInfos.insertColumn(0, [QStandardItem("ID")])
# self.mw.mdlPersosInfos.setHorizontalHeaderLabels(["Description"])
2015-06-24 04:22:39 +12:00
# Labels
self.mw.mdlLabels = QStandardItemModel(self.mw)
2015-06-24 04:22:39 +12:00
for color, text in [
(Qt.transparent, ""),
(Qt.yellow, self.tr("Idea")),
(Qt.green, self.tr("Note")),
(Qt.blue, self.tr("Chapter")),
(Qt.red, self.tr("Scene")),
(Qt.cyan, self.tr("Research"))
2016-02-07 00:34:22 +13:00
]:
2015-06-24 04:22:39 +12:00
self.mw.mdlLabels.appendRow(QStandardItem(iconFromColor(color), text))
# Status
self.mw.mdlStatus = QStandardItemModel(self.mw)
2015-06-24 04:22:39 +12:00
for text in [
2016-02-07 00:34:22 +13:00
"",
self.tr("TODO"),
self.tr("First draft"),
self.tr("Second draft"),
self.tr("Final")
]:
2015-06-24 04:22:39 +12:00
self.mw.mdlStatus.appendRow(QStandardItem(text))
# Plot
self.mw.mdlPlots = plotModel(self.mw)
2015-06-24 04:22:39 +12:00
# Outline
self.mw.mdlOutline = outlineModel(self.mw)
2016-02-07 00:34:22 +13:00
2016-02-09 02:44:39 +13:00
# World
self.mw.mdlWorld = worldModel(self.mw)
2015-06-24 04:22:39 +12:00
root = self.mw.mdlOutline.rootItem
2016-03-30 21:07:38 +13:00
_type = "md"
2016-02-07 00:34:22 +13:00
2015-06-24 04:22:39 +12:00
def addElement(parent, datas):
if len(datas) == 2 and datas[1][1] == None or \
2016-02-07 00:34:22 +13:00
len(datas) == 1:
2015-06-24 04:22:39 +12:00
# Next item is word count
n = 0
2016-02-07 00:34:22 +13:00
for i in range(datas[0][0]):
2015-06-24 04:22:39 +12:00
n += 1
item = outlineItem(title="{} {}".format(
2016-02-07 00:34:22 +13:00
datas[0][1],
str(n)),
_type=_type,
parent=parent)
2015-06-24 04:22:39 +12:00
if len(datas) == 2:
item.setData(Outline.setGoal.value, datas[1][0])
2016-02-07 00:34:22 +13:00
# parent.appendChild(item)
2015-06-24 04:22:39 +12:00
else:
n = 0
for i in range(datas[0][0]):
n += 1
item = outlineItem(title="{} {}".format(
2016-02-07 00:34:22 +13:00
datas[0][1],
str(n)),
_type="folder",
parent=parent)
# parent.appendChild(item)
2015-06-24 04:22:39 +12:00
addElement(item, datas[1:])
2016-02-07 00:34:22 +13:00
if self.template and self.template[1]:
addElement(root, self.template[1])