manuskript/manuskript/mainWindow.py

1623 lines
66 KiB
Python
Raw Normal View History

2015-05-28 13:32:09 +12:00
#!/usr/bin/env python
2016-02-06 00:25:25 +13:00
# --!-- coding: utf8 --!--
import imp
2016-02-07 00:34:22 +13:00
import os
import re
2016-02-07 00:34:22 +13:00
from PyQt5.Qt import qVersion, PYQT_VERSION_STR
from PyQt5.QtCore import (pyqtSignal, QSignalMapper, QTimer, QSettings, Qt, QPoint,
QRegExp, QUrl, QSize, QModelIndex)
2016-04-09 06:09:41 +12:00
from PyQt5.QtGui import QStandardItemModel, QIcon, QColor
2016-02-07 06:36:02 +13:00
from PyQt5.QtWidgets import QMainWindow, QHeaderView, qApp, QMenu, QActionGroup, QAction, QStyle, QListWidgetItem, \
Track dirty state and have the UI respect it Intending to learn more about the way Manuskript goes about saving the project in order to figure out how to tackle some recent saving-related issues, I stumbled into learning that Manuskript likes to save data a whole lot. Too much, in fact. When I close the project with unsaved changes, I expected those changes to not be saved... but they were. This completely subverts my expectations of a program using typical file-based operations involving opening, saving and closing files. There are three more settings that influence when the program saves, and I personally consider them a bit overkill or even detrimental to the stated purpose. What if Manuskript forces a save when nothing was changed and something goes wrong? Saving too much can in fact be dangerous! For now, I have left existing functionality as-is, but I would prefer to respect the dirty flag I have introduced in this commit for at least the 'save-on-quit' and 'save every X minutes' features. (The third is smarter and only triggers after noticing changes, so it is less important.) Making sure the dirty flag works as expected is the first step in making such changes in the future. UI-wise, this commit now offers the user the opportunity to save their changes, discard them, or outright cancel their action entirely when performing a destructive action on a dirty project. As of this commit, I have identified two of such scenarios: 1) closing the project, 2) closing the window with save-on-quit turned off. If I missed any, do let me know. But for now, maybe now I can finally start digging into those issues that sent me down this rabbit hole...
2019-04-25 09:02:37 +12:00
QLabel, QDockWidget, QWidget, QMessageBox
2016-02-07 00:34:22 +13:00
from manuskript import settings
2016-03-25 01:42:47 +13:00
from manuskript.enums import Character, PlotStep, Plot, World, Outline
2017-11-21 03:42:30 +13:00
from manuskript.functions import wordCount, appPath, findWidgetsOfClass
2017-10-16 21:48:04 +13:00
import manuskript.functions as F
from manuskript import loadSave
from manuskript.models.characterModel import characterModel
2017-11-16 08:33:27 +13:00
from manuskript.models import outlineModel
2016-02-07 00:34:22 +13:00
from manuskript.models.plotModel import plotModel
from manuskript.models.worldModel import worldModel
from manuskript.settingsWindow import settingsWindow
from manuskript.ui import style
from manuskript.ui.about import aboutDialog
2016-02-07 00:34:22 +13:00
from manuskript.ui.collapsibleDockWidgets import collapsibleDockWidgets
2017-11-06 21:16:44 +13:00
from manuskript.ui.importers.importer import importerDialog
2016-04-02 06:01:27 +13:00
from manuskript.ui.exporters.exporter import exporterDialog
2016-02-07 00:34:22 +13:00
from manuskript.ui.helpLabel import helpLabel
from manuskript.ui.mainWindow import Ui_MainWindow
2016-02-09 01:50:35 +13:00
from manuskript.ui.tools.frequencyAnalyzer import frequencyAnalyzer
from manuskript.ui.views.outlineDelegates import outlineCharacterDelegate
2016-02-07 00:34:22 +13:00
from manuskript.ui.views.plotDelegate import plotDelegate
2017-12-01 01:12:55 +13:00
from manuskript.ui.views.MDEditView import MDEditView
from manuskript.ui.statusLabel import statusLabel
2015-05-28 13:32:09 +12:00
# Spellcheck support
2016-02-07 00:34:22 +13:00
from manuskript.ui.views.textEditView import textEditView
from manuskript.functions import Spellchecker
2015-06-23 07:34:11 +12:00
2015-05-28 13:32:09 +12:00
class MainWindow(QMainWindow, Ui_MainWindow):
# dictChanged = pyqtSignal(str)
2016-02-06 00:25:25 +13:00
2015-07-10 01:30:59 +12:00
# Tab indexes
2015-07-10 01:01:07 +12:00
TabInfos = 0
TabSummary = 1
TabPersos = 2
TabPlots = 3
TabWorld = 4
TabOutline = 5
TabRedac = 6
TabDebug = 7
SHOW_DEBUG_TAB = False
2015-07-10 01:01:07 +12:00
2015-05-28 13:32:09 +12:00
def __init__(self):
QMainWindow.__init__(self)
self.setupUi(self)
# Var
2015-06-23 10:19:40 +12:00
self.currentProject = None
Track dirty state and have the UI respect it Intending to learn more about the way Manuskript goes about saving the project in order to figure out how to tackle some recent saving-related issues, I stumbled into learning that Manuskript likes to save data a whole lot. Too much, in fact. When I close the project with unsaved changes, I expected those changes to not be saved... but they were. This completely subverts my expectations of a program using typical file-based operations involving opening, saving and closing files. There are three more settings that influence when the program saves, and I personally consider them a bit overkill or even detrimental to the stated purpose. What if Manuskript forces a save when nothing was changed and something goes wrong? Saving too much can in fact be dangerous! For now, I have left existing functionality as-is, but I would prefer to respect the dirty flag I have introduced in this commit for at least the 'save-on-quit' and 'save every X minutes' features. (The third is smarter and only triggers after noticing changes, so it is less important.) Making sure the dirty flag works as expected is the first step in making such changes in the future. UI-wise, this commit now offers the user the opportunity to save their changes, discard them, or outright cancel their action entirely when performing a destructive action on a dirty project. As of this commit, I have identified two of such scenarios: 1) closing the project, 2) closing the window with save-on-quit turned off. If I missed any, do let me know. But for now, maybe now I can finally start digging into those issues that sent me down this rabbit hole...
2019-04-25 09:02:37 +12:00
self.projectDirty = None # has the user made any unsaved changes ?
self._lastFocus = None
2017-12-01 01:12:55 +13:00
self._lastMDEditView = None
self._defaultCursorFlashTime = 1000 # Overridden at startup with system
2017-11-21 03:42:30 +13:00
# value. In manuskript.main.
self._autoLoadProject = None # Used to load a command line project
2015-07-02 20:08:20 +12:00
2015-05-31 16:03:07 +12:00
self.readSettings()
2015-06-23 07:34:11 +12:00
2015-05-29 05:15:57 +12:00
# UI
2015-06-04 05:25:03 +12:00
self.setupMoreUi()
self.statusLabel = statusLabel(parent=self)
2017-12-08 22:01:58 +13:00
self.statusLabel.setAutoFillBackground(True)
self.statusLabel.hide()
2015-07-02 20:08:20 +12:00
2015-06-23 10:19:40 +12:00
# Welcome
2015-06-24 04:22:39 +12:00
self.welcome.updateValues()
self.switchToWelcome()
2015-07-02 20:08:20 +12:00
2015-05-31 16:03:07 +12:00
# Word count
self.mprWordCount = QSignalMapper(self)
2015-05-28 13:32:09 +12:00
for t, i in [
(self.txtSummarySentence, 0),
2015-05-28 13:32:09 +12:00
(self.txtSummaryPara, 1),
(self.txtSummaryPage, 2),
(self.txtSummaryFull, 3)
2016-02-06 00:25:25 +13:00
]:
2015-05-31 16:03:07 +12:00
t.textChanged.connect(self.mprWordCount.map)
self.mprWordCount.setMapping(t, i)
self.mprWordCount.mapped.connect(self.wordCount)
2015-06-23 07:34:11 +12:00
2015-05-28 13:32:09 +12:00
# Snowflake Method Cycle
self.mapperCycle = QSignalMapper(self)
for t, i in [
2016-02-06 00:25:25 +13:00
(self.btnStepTwo, 0),
2015-05-28 13:32:09 +12:00
(self.btnStepThree, 1),
2016-02-06 00:25:25 +13:00
(self.btnStepFour, 2),
(self.btnStepFive, 3),
(self.btnStepSix, 4),
2015-05-28 13:32:09 +12:00
(self.btnStepSeven, 5),
(self.btnStepEight, 6)
2016-02-06 00:25:25 +13:00
]:
2015-05-28 13:32:09 +12:00
t.clicked.connect(self.mapperCycle.map)
self.mapperCycle.setMapping(t, i)
2015-06-23 07:34:11 +12:00
2015-05-28 13:32:09 +12:00
self.mapperCycle.mapped.connect(self.clickCycle)
2015-06-21 21:29:35 +12:00
self.cmbSummary.currentIndexChanged.connect(self.summaryPageChanged)
self.cmbSummary.setCurrentIndex(0)
self.cmbSummary.currentIndexChanged.emit(0)
2015-06-23 07:34:11 +12:00
# Main Menu
2015-06-23 10:19:40 +12:00
for i in [self.actSave, self.actSaveAs, self.actCloseProject,
2017-11-27 20:05:53 +13:00
self.menuEdit, self.menuView, self.menuOrganize,
self.menuTools, self.menuHelp, self.actImport,
self.actCompile, self.actSettings]:
2015-06-23 10:19:40 +12:00
i.setEnabled(False)
2015-07-02 20:08:20 +12:00
# Main Menu:: File
2015-06-24 04:22:39 +12:00
self.actOpen.triggered.connect(self.welcome.openFile)
2015-06-17 22:00:03 +12:00
self.actSave.triggered.connect(self.saveDatas)
2015-06-24 04:22:39 +12:00
self.actSaveAs.triggered.connect(self.welcome.saveAsFile)
2017-11-06 21:16:44 +13:00
self.actImport.triggered.connect(self.doImport)
2015-07-01 23:14:03 +12:00
self.actCompile.triggered.connect(self.doCompile)
2015-06-24 04:22:39 +12:00
self.actCloseProject.triggered.connect(self.closeProject)
self.actQuit.triggered.connect(self.close)
2015-07-02 20:08:20 +12:00
2017-12-01 01:12:55 +13:00
# Main menu:: Edit
self.actCopy.triggered.connect(self.documentsCopy)
self.actCut.triggered.connect(self.documentsCut)
self.actPaste.triggered.connect(self.documentsPaste)
self.actRename.triggered.connect(self.documentsRename)
self.actDuplicate.triggered.connect(self.documentsDuplicate)
self.actDelete.triggered.connect(self.documentsDelete)
2017-12-01 01:12:55 +13:00
self.actLabels.triggered.connect(self.settingsLabel)
self.actStatus.triggered.connect(self.settingsStatus)
self.actSettings.triggered.connect(self.settingsWindow)
# Main menu:: Edit:: Format
self.actHeaderSetextL1.triggered.connect(self.formatSetext1)
self.actHeaderSetextL2.triggered.connect(self.formatSetext2)
self.actHeaderAtxL1.triggered.connect(self.formatAtx1)
self.actHeaderAtxL2.triggered.connect(self.formatAtx2)
self.actHeaderAtxL3.triggered.connect(self.formatAtx3)
self.actHeaderAtxL4.triggered.connect(self.formatAtx4)
self.actHeaderAtxL5.triggered.connect(self.formatAtx5)
self.actHeaderAtxL6.triggered.connect(self.formatAtx6)
self.actFormatBold.triggered.connect(self.formatBold)
self.actFormatItalic.triggered.connect(self.formatItalic)
self.actFormatStrike.triggered.connect(self.formatStrike)
self.actFormatVerbatim.triggered.connect(self.formatVerbatim)
self.actFormatSuperscript.triggered.connect(self.formatSuperscript)
self.actFormatSubscript.triggered.connect(self.formatSubscript)
self.actFormatCommentLines.triggered.connect(self.formatCommentLines)
self.actFormatList.triggered.connect(self.formatList)
self.actFormatOrderedList.triggered.connect(self.formatOrderedList)
self.actFormatBlockquote.triggered.connect(self.formatBlockquote)
self.actFormatCommentBlock.triggered.connect(self.formatCommentBlock)
self.actFormatClear.triggered.connect(self.formatClear)
# Main menu:: Organize
self.actMoveUp.triggered.connect(self.documentsMoveUp)
self.actMoveDown.triggered.connect(self.documentsMoveDown)
self.actSplitDialog.triggered.connect(self.documentsSplitDialog)
self.actSplitCursor.triggered.connect(self.documentsSplitCursor)
self.actMerge.triggered.connect(self.documentsMerge)
# Main Menu:: view
self.generateViewMenu()
2016-03-25 01:42:47 +13:00
self.actModeGroup = QActionGroup(self)
self.actModeSimple.setActionGroup(self.actModeGroup)
self.actModeFiction.setActionGroup(self.actModeGroup)
self.actModeSimple.triggered.connect(self.setViewModeSimple)
self.actModeFiction.triggered.connect(self.setViewModeFiction)
# Main Menu:: Tool
self.actToolFrequency.triggered.connect(self.frequencyAnalyzer)
self.actAbout.triggered.connect(self.about)
self.makeUIConnections()
2015-07-02 20:08:20 +12:00
2016-02-06 00:25:25 +13:00
# self.loadProject(os.path.join(appPath(), "test_project.zip"))
2017-10-24 01:40:55 +13:00
def updateDockVisibility(self, restore=False):
"""
2017-10-24 01:40:55 +13:00
Saves the state of the docks visibility. Or if `restore` is True,
restores from `self._dckVisibility`. This allows to hide the docks
while showing the welcome screen, and then restore them as they
were.
2017-10-24 01:40:55 +13:00
If `self._dckVisibility` contains "LOCK", then we don't override values
with current visibility state. This is used the first time we load.
"LOCK" is then removed.
"""
docks = [
self.dckCheatSheet,
self.dckNavigation,
self.dckSearch,
]
2017-10-24 01:40:55 +13:00
for d in docks:
if not restore:
# We store the values, but only if "LOCK" is not present
if not "LOCK" in self._dckVisibility:
self._dckVisibility[d.objectName()] = d.isVisible()
# Hide the dock
d.setVisible(False)
else:
# Restore the dock's visibility based on stored value
d.setVisible(self._dckVisibility[d.objectName()])
2017-10-24 01:40:55 +13:00
# Lock is used only once, at start up. We can remove it
if "LOCK" in self._dckVisibility:
self._dckVisibility.pop("LOCK")
2017-10-24 01:40:55 +13:00
def switchToWelcome(self):
"""
While switching to welcome screen, we have to hide all the docks.
Otherwise one could use the search dock, and manuskript would crash.
Plus it's unnecessary distraction.
But we also want to restore them to their visibility prior to switching,
so we store states.
"""
# Stores the state of docks
self.updateDockVisibility()
# Hides the toolbar
self.toolbar.setVisible(False)
# Switch to welcome screen
self.stack.setCurrentIndex(0)
2017-10-24 01:40:55 +13:00
def switchToProject(self):
"""Restores docks and toolbar visibility, and switch to project."""
# Restores the docks visibility
self.updateDockVisibility(restore=True)
# Show the toolbar
self.toolbar.setVisible(True)
self.stack.setCurrentIndex(1)
2015-06-23 07:34:11 +12:00
###############################################################################
# GENERAL / UI STUFF
###############################################################################
def tabMainChanged(self):
"Called when main tab changes."
2017-11-27 20:05:53 +13:00
tabIsEditor = self.tabMain.currentIndex() == self.TabRedac
self.menuOrganize.menuAction().setEnabled(tabIsEditor)
for i in [self.actCut,
self.actCopy,
self.actPaste,
self.actDelete,
self.actRename]:
i.setEnabled(tabIsEditor)
def focusChanged(self, old, new):
"""
We get notified by qApp when focus changes, from old to new widget.
"""
2017-12-01 01:12:55 +13:00
# If new is a MDEditView, we keep it in memory
if issubclass(type(new), MDEditView):
self._lastMDEditView = new
else:
self._lastMDEditView = None
2017-11-11 05:21:02 +13:00
# Determine which view had focus last, to send the keyboard shortcuts
# to the right place
targets = [
self.treeRedacOutline,
self.mainEditor
]
while new is not None:
if new in targets:
self._lastFocus = new
break
new = new.parent()
Track dirty state and have the UI respect it Intending to learn more about the way Manuskript goes about saving the project in order to figure out how to tackle some recent saving-related issues, I stumbled into learning that Manuskript likes to save data a whole lot. Too much, in fact. When I close the project with unsaved changes, I expected those changes to not be saved... but they were. This completely subverts my expectations of a program using typical file-based operations involving opening, saving and closing files. There are three more settings that influence when the program saves, and I personally consider them a bit overkill or even detrimental to the stated purpose. What if Manuskript forces a save when nothing was changed and something goes wrong? Saving too much can in fact be dangerous! For now, I have left existing functionality as-is, but I would prefer to respect the dirty flag I have introduced in this commit for at least the 'save-on-quit' and 'save every X minutes' features. (The third is smarter and only triggers after noticing changes, so it is less important.) Making sure the dirty flag works as expected is the first step in making such changes in the future. UI-wise, this commit now offers the user the opportunity to save their changes, discard them, or outright cancel their action entirely when performing a destructive action on a dirty project. As of this commit, I have identified two of such scenarios: 1) closing the project, 2) closing the window with save-on-quit turned off. If I missed any, do let me know. But for now, maybe now I can finally start digging into those issues that sent me down this rabbit hole...
2019-04-25 09:02:37 +12:00
def projectName(self):
"""
Returns a user-friendly name for the loaded project.
"""
pName = os.path.split(self.currentProject)[1]
if pName.endswith('.msk'):
pName=pName[:-4]
return pName
2016-02-06 00:25:25 +13:00
###############################################################################
# SUMMARY
###############################################################################
2015-06-23 07:34:11 +12:00
2015-06-21 21:29:35 +12:00
def summaryPageChanged(self, index):
fractalButtons = [
self.btnStepTwo,
self.btnStepThree,
self.btnStepFive,
self.btnStepSeven,
2016-02-06 00:25:25 +13:00
]
2015-06-21 21:29:35 +12:00
for b in fractalButtons:
b.setVisible(fractalButtons.index(b) == index)
2015-06-23 07:34:11 +12:00
2016-02-06 00:25:25 +13:00
###############################################################################
# OUTLINE
###############################################################################
2015-06-23 07:34:11 +12:00
def outlineRemoveItemsRedac(self):
self.treeRedacOutline.delete()
2016-02-06 00:25:25 +13:00
def outlineRemoveItemsOutline(self):
self.treeOutlineOutline.delete()
2016-02-06 00:25:25 +13:00
###############################################################################
2016-03-24 23:17:48 +13:00
# CHARACTERS
2016-02-06 00:25:25 +13:00
###############################################################################
2015-06-01 08:41:32 +12:00
def changeCurrentCharacter(self, trash=None):
"""
2015-06-23 07:34:11 +12:00
@return:
"""
c = self.lstCharacters.currentCharacter()
if not c:
self.tabPersos.setEnabled(False)
2015-06-29 09:46:51 +12:00
return
2015-06-23 07:34:11 +12:00
2015-06-29 09:46:51 +12:00
self.tabPersos.setEnabled(True)
index = c.index()
2015-07-02 20:08:20 +12:00
2015-06-29 20:22:18 +12:00
for w in [
self.txtPersoName,
self.sldPersoImportance,
self.txtPersoMotivation,
self.txtPersoGoal,
self.txtPersoConflict,
self.txtPersoEpiphany,
self.txtPersoSummarySentence,
2015-06-29 20:22:18 +12:00
self.txtPersoSummaryPara,
self.txtPersoSummaryFull,
self.txtPersoNotes,
2016-02-06 00:25:25 +13:00
]:
2015-06-29 20:22:18 +12:00
w.setCurrentModelIndex(index)
2015-07-02 20:08:20 +12:00
2015-06-29 21:28:34 +12:00
# Button color
self.updateCharacterColor(c.ID())
2015-07-02 20:08:20 +12:00
# Slider importance
self.updateCharacterImportance(c.ID())
# Character Infos
2015-06-29 21:28:34 +12:00
self.tblPersoInfos.setRootIndex(index)
2015-07-02 20:08:20 +12:00
if self.mdlCharacter.rowCount(index):
2015-06-29 21:28:34 +12:00
self.updatePersoInfoView()
2015-07-02 20:08:20 +12:00
2015-06-29 21:28:34 +12:00
def updatePersoInfoView(self):
self.tblPersoInfos.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeToContents)
self.tblPersoInfos.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
2015-06-29 21:28:34 +12:00
self.tblPersoInfos.verticalHeader().hide()
def updateCharacterColor(self, ID):
c = self.mdlCharacter.getCharacterByID(ID)
color = c.color().name()
self.btnPersoColor.setStyleSheet("background:{};".format(color))
def updateCharacterImportance(self, ID):
c = self.mdlCharacter.getCharacterByID(ID)
self.sldPersoImportance.setValue(int(c.importance()))
2016-02-06 00:25:25 +13:00
###############################################################################
# PLOTS
###############################################################################
2015-06-22 23:11:45 +12:00
def changeCurrentPlot(self):
2015-06-23 06:30:43 +12:00
index = self.lstPlots.currentPlotIndex()
2015-06-23 07:34:11 +12:00
2015-06-22 23:11:45 +12:00
if not index.isValid():
self.tabPlot.setEnabled(False)
return
2015-06-23 07:34:11 +12:00
2015-06-22 23:11:45 +12:00
self.tabPlot.setEnabled(True)
self.txtPlotName.setCurrentModelIndex(index)
self.txtPlotDescription.setCurrentModelIndex(index)
self.txtPlotResult.setCurrentModelIndex(index)
self.sldPlotImportance.setCurrentModelIndex(index)
2015-06-23 07:34:11 +12:00
self.lstPlotPerso.setRootIndex(index.sibling(index.row(),
2017-11-16 09:05:48 +13:00
Plot.characters))
# Slider importance
self.updatePlotImportance(index.row())
2017-11-16 09:05:48 +13:00
subplotindex = index.sibling(index.row(), Plot.steps)
2015-07-07 01:00:22 +12:00
self.lstSubPlots.setRootIndex(subplotindex)
if self.mdlPlots.rowCount(subplotindex):
self.updateSubPlotView()
2015-06-23 07:34:11 +12:00
self.txtSubPlotSummary.setCurrentModelIndex(QModelIndex())
2015-07-06 20:07:05 +12:00
self.lstPlotPerso.selectionModel().clear()
2015-06-23 07:34:11 +12:00
2015-07-07 01:00:22 +12:00
def updateSubPlotView(self):
# Hide columns
# FIXME: when columns are hidden, and drag and drop InternalMove is enabled
# as well as selectionBehavior=SelectRows, then when moving a row
# hidden cells (here: summary and ID) are deleted...
# So instead we set their width to 0.
#for i in range(self.mdlPlots.columnCount()):
#self.lstSubPlots.hideColumn(i)
2017-11-16 09:05:48 +13:00
#self.lstSubPlots.showColumn(PlotStep.name)
#self.lstSubPlots.showColumn(PlotStep.meta)
self.lstSubPlots.horizontalHeader().setSectionResizeMode(
2017-11-16 09:05:48 +13:00
PlotStep.ID, QHeaderView.Fixed)
self.lstSubPlots.horizontalHeader().setSectionResizeMode(
2017-11-16 09:05:48 +13:00
PlotStep.summary, QHeaderView.Fixed)
self.lstSubPlots.horizontalHeader().resizeSection(
2017-11-16 09:05:48 +13:00
PlotStep.ID, 0)
self.lstSubPlots.horizontalHeader().resizeSection(
2017-11-16 09:05:48 +13:00
PlotStep.summary, 0)
2015-07-07 01:00:22 +12:00
self.lstSubPlots.horizontalHeader().setSectionResizeMode(
2017-11-16 09:05:48 +13:00
PlotStep.name, QHeaderView.Stretch)
2015-07-07 01:00:22 +12:00
self.lstSubPlots.horizontalHeader().setSectionResizeMode(
2017-11-16 09:05:48 +13:00
PlotStep.meta, QHeaderView.ResizeToContents)
2015-07-07 01:00:22 +12:00
self.lstSubPlots.verticalHeader().hide()
2016-02-06 00:25:25 +13:00
def updatePlotImportance(self, row):
imp = self.mdlPlots.getPlotImportanceByRow(row)
self.sldPlotImportance.setValue(int(imp))
2015-06-22 23:11:45 +12:00
def changeCurrentSubPlot(self, index):
2017-11-16 09:05:48 +13:00
index = index.sibling(index.row(), PlotStep.summary)
self.txtSubPlotSummary.setColumn(PlotStep.summary)
self.txtSubPlotSummary.setCurrentModelIndex(index)
2016-02-06 00:25:25 +13:00
2015-07-06 20:07:05 +12:00
def plotPersoSelectionChanged(self):
"Enables or disables remove plot perso button."
self.btnRmPlotPerso.setEnabled(
2016-02-06 00:25:25 +13:00
len(self.lstPlotPerso.selectedIndexes()) != 0)
2015-06-23 07:34:11 +12:00
2016-02-06 00:25:25 +13:00
###############################################################################
# WORLD
###############################################################################
def changeCurrentWorld(self):
index = self.mdlWorld.selectedIndex()
if not index.isValid():
self.tabWorld.setEnabled(False)
return
self.tabWorld.setEnabled(True)
self.txtWorldName.setCurrentModelIndex(index)
self.txtWorldDescription.setCurrentModelIndex(index)
self.txtWorldPassion.setCurrentModelIndex(index)
self.txtWorldConflict.setCurrentModelIndex(index)
2017-10-15 08:39:16 +13:00
###############################################################################
# EDITOR
###############################################################################
def openIndex(self, index):
self.treeRedacOutline.setCurrentIndex(index)
def openIndexes(self, indexes, newTab=True):
self.mainEditor.openIndexes(indexes, newTab=True)
2017-12-01 01:12:55 +13:00
# Menu #############################################################
2017-12-01 01:12:55 +13:00
# Functions called by the menus
# self._lastFocus is the last editor that had focus (either treeView or
# mainEditor). So we just pass along the signal.
2017-12-01 01:12:55 +13:00
# Edit
def documentsCopy(self):
2017-11-11 04:26:23 +13:00
"Copy selected item(s)."
if self._lastFocus: self._lastFocus.copy()
def documentsCut(self):
2017-11-11 04:26:23 +13:00
"Cut selected item(s)."
if self._lastFocus: self._lastFocus.cut()
def documentsPaste(self):
2017-11-11 04:26:23 +13:00
"Paste clipboard item(s) into selected item."
if self._lastFocus: self._lastFocus.paste()
def documentsRename(self):
"Rename selected item."
if self._lastFocus: self._lastFocus.rename()
def documentsDuplicate(self):
2017-11-11 04:26:23 +13:00
"Duplicate selected item(s)."
if self._lastFocus: self._lastFocus.duplicate()
def documentsDelete(self):
2017-11-11 04:26:23 +13:00
"Delete selected item(s)."
if self._lastFocus: self._lastFocus.delete()
2017-12-01 01:12:55 +13:00
# Formats
def callLastMDEditView(self, functionName, param=[]):
"""
If last focused widget was MDEditView, call the given function.
"""
if self._lastMDEditView:
function = getattr(self._lastMDEditView, functionName)
function(*param)
def formatSetext1(self): self.callLastMDEditView("titleSetext", [1])
def formatSetext2(self): self.callLastMDEditView("titleSetext", [2])
def formatAtx1(self): self.callLastMDEditView("titleATX", [1])
def formatAtx2(self): self.callLastMDEditView("titleATX", [2])
def formatAtx3(self): self.callLastMDEditView("titleATX", [3])
def formatAtx4(self): self.callLastMDEditView("titleATX", [4])
def formatAtx5(self): self.callLastMDEditView("titleATX", [5])
def formatAtx6(self): self.callLastMDEditView("titleATX", [6])
def formatBold(self): self.callLastMDEditView("bold")
def formatItalic(self): self.callLastMDEditView("italic")
def formatStrike(self): self.callLastMDEditView("strike")
def formatVerbatim(self): self.callLastMDEditView("verbatim")
def formatSuperscript(self): self.callLastMDEditView("superscript")
def formatSubscript(self): self.callLastMDEditView("subscript")
def formatCommentLines(self): self.callLastMDEditView("commentLine")
def formatList(self): self.callLastMDEditView("unorderedList")
def formatOrderedList(self): self.callLastMDEditView("orderedList")
def formatBlockquote(self): self.callLastMDEditView("blockquote")
def formatCommentBlock(self): self.callLastMDEditView("comment")
def formatClear(self): self.callLastMDEditView("clearFormat")
# Organize
def documentsMoveUp(self):
2017-11-11 04:26:23 +13:00
"Move up selected item(s)."
if self._lastFocus: self._lastFocus.moveUp()
def documentsMoveDown(self):
2017-11-11 04:26:23 +13:00
"Move Down selected item(s)."
if self._lastFocus: self._lastFocus.moveDown()
2017-11-11 04:26:23 +13:00
def documentsSplitDialog(self):
2017-11-11 04:26:23 +13:00
"Opens a dialog to split selected items."
if self._lastFocus: self._lastFocus.splitDialog()
# current items or selected items?
pass
# use outlineBasics, to do that on all selected items.
# use editorWidget to do that on selected text.
def documentsSplitCursor(self):
2017-11-11 04:26:23 +13:00
"""
Split current item (open in text editor) at cursor position. If there is
a text selection, that selection becomes the title of the new scene.
"""
if self._lastFocus and self._lastFocus == self.mainEditor:
self.mainEditor.splitCursor()
def documentsMerge(self):
2017-11-11 05:21:02 +13:00
"Merges selected item(s)."
if self._lastFocus: self._lastFocus.merge()
2017-10-15 08:39:16 +13:00
###############################################################################
# LOAD AND SAVE
###############################################################################
2015-06-23 07:34:11 +12:00
2015-06-24 04:22:39 +12:00
def loadProject(self, project, loadFromFile=True):
"""Loads the project ``project``.
2015-07-02 20:08:20 +12:00
2015-06-24 04:22:39 +12:00
If ``loadFromFile`` is False, then it does not load datas from file.
It assumes that the datas have been populated in a different way."""
if loadFromFile and not os.path.exists(project):
print(self.tr("The file {} does not exist. Has it been moved or deleted?").format(project))
2017-11-28 03:09:07 +13:00
F.statusMessage(
self.tr("The file {} does not exist. Has it been moved or deleted?").format(project), importance=3)
2015-06-24 04:22:39 +12:00
return
2015-07-02 20:08:20 +12:00
2015-06-24 04:22:39 +12:00
if loadFromFile:
2015-06-25 20:01:28 +12:00
# Load empty settings
2015-07-02 20:08:20 +12:00
imp.reload(settings)
settings.initDefaultValues()
2015-07-02 20:08:20 +12:00
# Load data
2015-06-24 04:22:39 +12:00
self.loadEmptyDatas()
2015-07-07 01:00:22 +12:00
self.loadDatas(project)
2015-07-02 20:08:20 +12:00
self.makeConnections()
2015-06-23 07:34:11 +12:00
2015-06-17 22:00:03 +12:00
# Load settings
if settings.openIndexes and settings.openIndexes != [""]:
2016-04-11 03:29:27 +12:00
self.mainEditor.tabSplitter.restoreOpenIndexes(settings.openIndexes)
self.generateViewMenu()
2015-06-28 00:06:35 +12:00
self.mainEditor.sldCorkSizeFactor.setValue(settings.corkSizeFactor)
self.actSpellcheck.setChecked(settings.spellcheck)
2015-06-30 00:21:57 +12:00
self.toggleSpellcheck(settings.spellcheck)
self.updateMenuDict()
self.setDictionary()
2015-07-02 20:08:20 +12:00
2017-10-24 01:40:55 +13:00
iconSize = settings.viewSettings["Tree"]["iconSize"]
self.treeRedacOutline.setIconSize(QSize(iconSize, iconSize))
2015-06-28 00:06:35 +12:00
self.mainEditor.setFolderView(settings.folderView)
2015-06-30 00:21:57 +12:00
self.mainEditor.updateFolderViewButtons(settings.folderView)
2016-04-12 01:14:24 +12:00
self.mainEditor.tabSplitter.updateStyleSheet()
2015-06-16 06:30:18 +12:00
self.tabMain.setCurrentIndex(settings.lastTab)
2015-06-28 00:06:35 +12:00
self.mainEditor.updateCorkBackground()
2016-03-25 01:42:47 +13:00
if settings.viewMode == "simple":
self.setViewModeSimple()
else:
self.setViewModeFiction()
2015-06-23 07:34:11 +12:00
2015-06-17 22:00:03 +12:00
# Set autosave
2015-06-18 04:40:55 +12:00
self.saveTimer = QTimer()
self.saveTimer.setInterval(settings.autoSaveDelay * 60 * 1000)
self.saveTimer.setSingleShot(False)
self.saveTimer.timeout.connect(self.saveDatas)
2015-06-17 22:00:03 +12:00
if settings.autoSave:
self.saveTimer.start()
2015-06-23 07:34:11 +12:00
2015-06-18 04:40:55 +12:00
# Set autosave if no changes
self.saveTimerNoChanges = QTimer()
2016-02-06 00:25:25 +13:00
self.saveTimerNoChanges.setInterval(settings.autoSaveNoChangesDelay * 1000)
2015-06-18 04:40:55 +12:00
self.saveTimerNoChanges.setSingleShot(True)
2015-06-24 04:22:39 +12:00
self.mdlFlatData.dataChanged.connect(self.startTimerNoChanges)
2015-06-18 04:40:55 +12:00
self.mdlOutline.dataChanged.connect(self.startTimerNoChanges)
self.mdlCharacter.dataChanged.connect(self.startTimerNoChanges)
2015-07-03 03:45:27 +12:00
self.mdlPlots.dataChanged.connect(self.startTimerNoChanges)
self.mdlWorld.dataChanged.connect(self.startTimerNoChanges)
2016-02-06 00:25:25 +13:00
# self.mdlPersosInfos.dataChanged.connect(self.startTimerNoChanges)
2015-06-18 06:48:20 +12:00
self.mdlStatus.dataChanged.connect(self.startTimerNoChanges)
self.mdlLabels.dataChanged.connect(self.startTimerNoChanges)
2015-06-23 07:34:11 +12:00
2015-06-18 04:40:55 +12:00
self.saveTimerNoChanges.timeout.connect(self.saveDatas)
self.saveTimerNoChanges.stop()
2015-06-23 07:34:11 +12:00
2015-06-17 22:00:03 +12:00
# UI
for i in [self.actOpen, self.menuRecents]:
i.setEnabled(False)
2015-06-23 10:19:40 +12:00
for i in [self.actSave, self.actSaveAs, self.actCloseProject,
2017-11-27 20:05:53 +13:00
self.menuEdit, self.menuView, self.menuOrganize,
self.menuTools, self.menuHelp, self.actImport,
self.actCompile, self.actSettings]:
2015-06-23 10:19:40 +12:00
i.setEnabled(True)
# We force to emit even if it opens on the current tab
self.tabMain.currentChanged.emit(settings.lastTab)
2017-05-23 04:48:20 +12:00
Track dirty state and have the UI respect it Intending to learn more about the way Manuskript goes about saving the project in order to figure out how to tackle some recent saving-related issues, I stumbled into learning that Manuskript likes to save data a whole lot. Too much, in fact. When I close the project with unsaved changes, I expected those changes to not be saved... but they were. This completely subverts my expectations of a program using typical file-based operations involving opening, saving and closing files. There are three more settings that influence when the program saves, and I personally consider them a bit overkill or even detrimental to the stated purpose. What if Manuskript forces a save when nothing was changed and something goes wrong? Saving too much can in fact be dangerous! For now, I have left existing functionality as-is, but I would prefer to respect the dirty flag I have introduced in this commit for at least the 'save-on-quit' and 'save every X minutes' features. (The third is smarter and only triggers after noticing changes, so it is less important.) Making sure the dirty flag works as expected is the first step in making such changes in the future. UI-wise, this commit now offers the user the opportunity to save their changes, discard them, or outright cancel their action entirely when performing a destructive action on a dirty project. As of this commit, I have identified two of such scenarios: 1) closing the project, 2) closing the window with save-on-quit turned off. If I missed any, do let me know. But for now, maybe now I can finally start digging into those issues that sent me down this rabbit hole...
2019-04-25 09:02:37 +12:00
# Make sure we can update the window title later.
self.currentProject = project
self.projectDirty = False
QSettings().setValue("lastProject", project)
2017-05-23 04:48:20 +12:00
# Add project name to Window's name
Track dirty state and have the UI respect it Intending to learn more about the way Manuskript goes about saving the project in order to figure out how to tackle some recent saving-related issues, I stumbled into learning that Manuskript likes to save data a whole lot. Too much, in fact. When I close the project with unsaved changes, I expected those changes to not be saved... but they were. This completely subverts my expectations of a program using typical file-based operations involving opening, saving and closing files. There are three more settings that influence when the program saves, and I personally consider them a bit overkill or even detrimental to the stated purpose. What if Manuskript forces a save when nothing was changed and something goes wrong? Saving too much can in fact be dangerous! For now, I have left existing functionality as-is, but I would prefer to respect the dirty flag I have introduced in this commit for at least the 'save-on-quit' and 'save every X minutes' features. (The third is smarter and only triggers after noticing changes, so it is less important.) Making sure the dirty flag works as expected is the first step in making such changes in the future. UI-wise, this commit now offers the user the opportunity to save their changes, discard them, or outright cancel their action entirely when performing a destructive action on a dirty project. As of this commit, I have identified two of such scenarios: 1) closing the project, 2) closing the window with save-on-quit turned off. If I missed any, do let me know. But for now, maybe now I can finally start digging into those issues that sent me down this rabbit hole...
2019-04-25 09:02:37 +12:00
self.setWindowTitle(self.projectName() + " - " + self.tr("Manuskript"))
2015-06-23 07:34:11 +12:00
2015-06-01 08:41:32 +12:00
# Stuff
# self.checkPersosID() # Shouldn't be necessary any longer
2015-07-02 20:08:20 +12:00
2015-06-24 04:22:39 +12:00
# Show main Window
self.switchToProject()
2015-07-02 20:08:20 +12:00
Track dirty state and have the UI respect it Intending to learn more about the way Manuskript goes about saving the project in order to figure out how to tackle some recent saving-related issues, I stumbled into learning that Manuskript likes to save data a whole lot. Too much, in fact. When I close the project with unsaved changes, I expected those changes to not be saved... but they were. This completely subverts my expectations of a program using typical file-based operations involving opening, saving and closing files. There are three more settings that influence when the program saves, and I personally consider them a bit overkill or even detrimental to the stated purpose. What if Manuskript forces a save when nothing was changed and something goes wrong? Saving too much can in fact be dangerous! For now, I have left existing functionality as-is, but I would prefer to respect the dirty flag I have introduced in this commit for at least the 'save-on-quit' and 'save every X minutes' features. (The third is smarter and only triggers after noticing changes, so it is less important.) Making sure the dirty flag works as expected is the first step in making such changes in the future. UI-wise, this commit now offers the user the opportunity to save their changes, discard them, or outright cancel their action entirely when performing a destructive action on a dirty project. As of this commit, I have identified two of such scenarios: 1) closing the project, 2) closing the window with save-on-quit turned off. If I missed any, do let me know. But for now, maybe now I can finally start digging into those issues that sent me down this rabbit hole...
2019-04-25 09:02:37 +12:00
def handleUnsavedChanges(self):
"""
There may be some currently unsaved changes, but the action the user triggered
will result in the project or application being closed. To save, or not to save?
Or just bail out entirely?
Sometimes it is best to just ask.
"""
if not self.projectDirty:
return True # no unsaved changes, all is good
msg = QMessageBox(QMessageBox.Question,
self.tr("Save project?"),
"<p><b>" +
self.tr("Save changes to project \"{}\" before closing?").format(self.projectName()) +
"</b></p>" +
"<p>" +
self.tr("Your changes will be lost if you don't save them.") +
"</p>",
QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
ret = msg.exec()
if ret == QMessageBox.Cancel:
return False # the situation has not been handled, cancel action
if ret == QMessageBox.Save:
self.saveDatas()
return True # the situation has been handled
2015-06-24 04:22:39 +12:00
def closeProject(self):
2016-03-31 21:50:20 +13:00
if not self.currentProject:
return
Track dirty state and have the UI respect it Intending to learn more about the way Manuskript goes about saving the project in order to figure out how to tackle some recent saving-related issues, I stumbled into learning that Manuskript likes to save data a whole lot. Too much, in fact. When I close the project with unsaved changes, I expected those changes to not be saved... but they were. This completely subverts my expectations of a program using typical file-based operations involving opening, saving and closing files. There are three more settings that influence when the program saves, and I personally consider them a bit overkill or even detrimental to the stated purpose. What if Manuskript forces a save when nothing was changed and something goes wrong? Saving too much can in fact be dangerous! For now, I have left existing functionality as-is, but I would prefer to respect the dirty flag I have introduced in this commit for at least the 'save-on-quit' and 'save every X minutes' features. (The third is smarter and only triggers after noticing changes, so it is less important.) Making sure the dirty flag works as expected is the first step in making such changes in the future. UI-wise, this commit now offers the user the opportunity to save their changes, discard them, or outright cancel their action entirely when performing a destructive action on a dirty project. As of this commit, I have identified two of such scenarios: 1) closing the project, 2) closing the window with save-on-quit turned off. If I missed any, do let me know. But for now, maybe now I can finally start digging into those issues that sent me down this rabbit hole...
2019-04-25 09:02:37 +12:00
# Make sure data is saved.
if (self.projectDirty and settings.saveOnQuit == True):
self.saveDatas()
elif not self.handleUnsavedChanges():
return # user cancelled action
Track dirty state and have the UI respect it Intending to learn more about the way Manuskript goes about saving the project in order to figure out how to tackle some recent saving-related issues, I stumbled into learning that Manuskript likes to save data a whole lot. Too much, in fact. When I close the project with unsaved changes, I expected those changes to not be saved... but they were. This completely subverts my expectations of a program using typical file-based operations involving opening, saving and closing files. There are three more settings that influence when the program saves, and I personally consider them a bit overkill or even detrimental to the stated purpose. What if Manuskript forces a save when nothing was changed and something goes wrong? Saving too much can in fact be dangerous! For now, I have left existing functionality as-is, but I would prefer to respect the dirty flag I have introduced in this commit for at least the 'save-on-quit' and 'save every X minutes' features. (The third is smarter and only triggers after noticing changes, so it is less important.) Making sure the dirty flag works as expected is the first step in making such changes in the future. UI-wise, this commit now offers the user the opportunity to save their changes, discard them, or outright cancel their action entirely when performing a destructive action on a dirty project. As of this commit, I have identified two of such scenarios: 1) closing the project, 2) closing the window with save-on-quit turned off. If I missed any, do let me know. But for now, maybe now I can finally start digging into those issues that sent me down this rabbit hole...
2019-04-25 09:02:37 +12:00
# Close open tabs in editor
self.mainEditor.closeAllTabs()
2015-06-24 04:22:39 +12:00
self.currentProject = None
Track dirty state and have the UI respect it Intending to learn more about the way Manuskript goes about saving the project in order to figure out how to tackle some recent saving-related issues, I stumbled into learning that Manuskript likes to save data a whole lot. Too much, in fact. When I close the project with unsaved changes, I expected those changes to not be saved... but they were. This completely subverts my expectations of a program using typical file-based operations involving opening, saving and closing files. There are three more settings that influence when the program saves, and I personally consider them a bit overkill or even detrimental to the stated purpose. What if Manuskript forces a save when nothing was changed and something goes wrong? Saving too much can in fact be dangerous! For now, I have left existing functionality as-is, but I would prefer to respect the dirty flag I have introduced in this commit for at least the 'save-on-quit' and 'save every X minutes' features. (The third is smarter and only triggers after noticing changes, so it is less important.) Making sure the dirty flag works as expected is the first step in making such changes in the future. UI-wise, this commit now offers the user the opportunity to save their changes, discard them, or outright cancel their action entirely when performing a destructive action on a dirty project. As of this commit, I have identified two of such scenarios: 1) closing the project, 2) closing the window with save-on-quit turned off. If I missed any, do let me know. But for now, maybe now I can finally start digging into those issues that sent me down this rabbit hole...
2019-04-25 09:02:37 +12:00
self.projectDirty = None
2015-06-24 04:22:39 +12:00
QSettings().setValue("lastProject", "")
2015-07-02 20:08:20 +12:00
2015-06-24 04:22:39 +12:00
# Clear datas
self.loadEmptyDatas()
self.saveTimer.stop()
self.saveTimerNoChanges.stop()
loadSave.clearSaveCache()
2015-07-02 20:08:20 +12:00
self.breakConnections()
2015-06-24 04:22:39 +12:00
# UI
for i in [self.actOpen, self.menuRecents]:
i.setEnabled(True)
2015-06-24 04:22:39 +12:00
for i in [self.actSave, self.actSaveAs, self.actCloseProject,
2017-11-27 20:05:53 +13:00
self.menuEdit, self.menuView, self.menuOrganize,
self.menuTools, self.menuHelp, self.actImport,
self.actCompile, self.actSettings]:
2015-06-24 04:22:39 +12:00
i.setEnabled(False)
2015-07-02 20:08:20 +12:00
2017-05-23 04:48:20 +12:00
# Set Window's name - no project loaded
self.setWindowTitle(self.tr("Manuskript"))
2015-06-24 04:22:39 +12:00
# Reload recent files
self.welcome.updateValues()
2015-07-02 20:08:20 +12:00
2015-06-24 04:22:39 +12:00
# Show welcome dialog
self.switchToWelcome()
2015-06-23 07:34:11 +12:00
2015-06-24 04:22:39 +12:00
def readSettings(self):
# Load State and geometry
sttgns = QSettings(qApp.organizationName(), qApp.applicationName())
if sttgns.contains("geometry"):
self.restoreGeometry(sttgns.value("geometry"))
if sttgns.contains("windowState"):
self.restoreState(sttgns.value("windowState"))
if sttgns.contains("docks"):
self._dckVisibility = {}
vals = sttgns.value("docks")
for name in vals:
self._dckVisibility[name] = vals[name]
else:
# Create default settings
self._dckVisibility = {
self.dckNavigation.objectName() : True,
self.dckCheatSheet.objectName() : False,
self.dckSearch.objectName() : False,
}
self._dckVisibility["LOCK"] = True # prevent overriding loaded values
2015-07-04 19:59:35 +12:00
if sttgns.contains("metadataState"):
state = [False if v == "false" else True for v in sttgns.value("metadataState")]
self.redacMetadata.restoreState(state)
if sttgns.contains("revisionsState"):
state = [False if v == "false" else True for v in sttgns.value("revisionsState")]
self.redacMetadata.revisions.restoreState(state)
if sttgns.contains("splitterRedacH"):
self.splitterRedacH.restoreState(sttgns.value("splitterRedacH"))
if sttgns.contains("splitterRedacV"):
self.splitterRedacV.restoreState(sttgns.value("splitterRedacV"))
if sttgns.contains("toolbar"):
# self.toolbar is not initialized yet, so we just store value
self._toolbarState = sttgns.value("toolbar")
else:
self._toolbarState = ""
2015-06-24 04:22:39 +12:00
def closeEvent(self, event):
# Specific settings to save before quitting
settings.lastTab = self.tabMain.currentIndex()
2015-07-02 20:08:20 +12:00
2015-06-24 04:22:39 +12:00
if self.currentProject:
# Remembering the current items (stores outlineItem's ID)
2016-04-11 03:29:27 +12:00
settings.openIndexes = self.mainEditor.tabSplitter.openIndexes()
2015-06-24 04:22:39 +12:00
Track dirty state and have the UI respect it Intending to learn more about the way Manuskript goes about saving the project in order to figure out how to tackle some recent saving-related issues, I stumbled into learning that Manuskript likes to save data a whole lot. Too much, in fact. When I close the project with unsaved changes, I expected those changes to not be saved... but they were. This completely subverts my expectations of a program using typical file-based operations involving opening, saving and closing files. There are three more settings that influence when the program saves, and I personally consider them a bit overkill or even detrimental to the stated purpose. What if Manuskript forces a save when nothing was changed and something goes wrong? Saving too much can in fact be dangerous! For now, I have left existing functionality as-is, but I would prefer to respect the dirty flag I have introduced in this commit for at least the 'save-on-quit' and 'save every X minutes' features. (The third is smarter and only triggers after noticing changes, so it is less important.) Making sure the dirty flag works as expected is the first step in making such changes in the future. UI-wise, this commit now offers the user the opportunity to save their changes, discard them, or outright cancel their action entirely when performing a destructive action on a dirty project. As of this commit, I have identified two of such scenarios: 1) closing the project, 2) closing the window with save-on-quit turned off. If I missed any, do let me know. But for now, maybe now I can finally start digging into those issues that sent me down this rabbit hole...
2019-04-25 09:02:37 +12:00
# Save data from models
if settings.saveOnQuit:
self.saveDatas()
elif not self.handleUnsavedChanges():
event.ignore() # user opted to cancel the close action
2015-06-24 04:22:39 +12:00
2016-02-06 00:25:25 +13:00
# closeEvent
# QMainWindow.closeEvent(self, event) # Causing segfaults?
2015-06-24 04:22:39 +12:00
Track dirty state and have the UI respect it Intending to learn more about the way Manuskript goes about saving the project in order to figure out how to tackle some recent saving-related issues, I stumbled into learning that Manuskript likes to save data a whole lot. Too much, in fact. When I close the project with unsaved changes, I expected those changes to not be saved... but they were. This completely subverts my expectations of a program using typical file-based operations involving opening, saving and closing files. There are three more settings that influence when the program saves, and I personally consider them a bit overkill or even detrimental to the stated purpose. What if Manuskript forces a save when nothing was changed and something goes wrong? Saving too much can in fact be dangerous! For now, I have left existing functionality as-is, but I would prefer to respect the dirty flag I have introduced in this commit for at least the 'save-on-quit' and 'save every X minutes' features. (The third is smarter and only triggers after noticing changes, so it is less important.) Making sure the dirty flag works as expected is the first step in making such changes in the future. UI-wise, this commit now offers the user the opportunity to save their changes, discard them, or outright cancel their action entirely when performing a destructive action on a dirty project. As of this commit, I have identified two of such scenarios: 1) closing the project, 2) closing the window with save-on-quit turned off. If I missed any, do let me know. But for now, maybe now I can finally start digging into those issues that sent me down this rabbit hole...
2019-04-25 09:02:37 +12:00
# User may have canceled close event, so make sure we indeed want to close.
# This is necessary because self.updateDockVisibility() hides UI elements.
if event.isAccepted():
# Save State and geometry and other things
appSettings = QSettings(qApp.organizationName(), qApp.applicationName())
appSettings.setValue("geometry", self.saveGeometry())
appSettings.setValue("windowState", self.saveState())
appSettings.setValue("metadataState", self.redacMetadata.saveState())
appSettings.setValue("revisionsState", self.redacMetadata.revisions.saveState())
appSettings.setValue("splitterRedacH", self.splitterRedacH.saveState())
appSettings.setValue("splitterRedacV", self.splitterRedacV.saveState())
appSettings.setValue("toolbar", self.toolbar.saveState())
# If we are not in the welcome window, we update the visibility
# of the docks widgets
if self.stack.currentIndex() == 1:
self.updateDockVisibility()
# Storing the visibility of docks to restore it on restart
appSettings.setValue("docks", self._dckVisibility)
2015-06-24 04:22:39 +12:00
def startTimerNoChanges(self):
Track dirty state and have the UI respect it Intending to learn more about the way Manuskript goes about saving the project in order to figure out how to tackle some recent saving-related issues, I stumbled into learning that Manuskript likes to save data a whole lot. Too much, in fact. When I close the project with unsaved changes, I expected those changes to not be saved... but they were. This completely subverts my expectations of a program using typical file-based operations involving opening, saving and closing files. There are three more settings that influence when the program saves, and I personally consider them a bit overkill or even detrimental to the stated purpose. What if Manuskript forces a save when nothing was changed and something goes wrong? Saving too much can in fact be dangerous! For now, I have left existing functionality as-is, but I would prefer to respect the dirty flag I have introduced in this commit for at least the 'save-on-quit' and 'save every X minutes' features. (The third is smarter and only triggers after noticing changes, so it is less important.) Making sure the dirty flag works as expected is the first step in making such changes in the future. UI-wise, this commit now offers the user the opportunity to save their changes, discard them, or outright cancel their action entirely when performing a destructive action on a dirty project. As of this commit, I have identified two of such scenarios: 1) closing the project, 2) closing the window with save-on-quit turned off. If I missed any, do let me know. But for now, maybe now I can finally start digging into those issues that sent me down this rabbit hole...
2019-04-25 09:02:37 +12:00
"""
Something changed in the project that requires auto-saving.
"""
self.projectDirty = True
2015-06-24 04:22:39 +12:00
if settings.autoSaveNoChanges:
self.saveTimerNoChanges.start()
def saveDatas(self, projectName=None):
"""Saves the current project (in self.currentProject).
2015-07-02 20:08:20 +12:00
2015-06-24 04:22:39 +12:00
If ``projectName`` is given, currentProject becomes projectName.
In other words, it "saves as...".
"""
2015-07-02 20:08:20 +12:00
2015-06-24 04:22:39 +12:00
if projectName:
self.currentProject = projectName
QSettings().setValue("lastProject", projectName)
2015-07-02 20:08:20 +12:00
Track dirty state and have the UI respect it Intending to learn more about the way Manuskript goes about saving the project in order to figure out how to tackle some recent saving-related issues, I stumbled into learning that Manuskript likes to save data a whole lot. Too much, in fact. When I close the project with unsaved changes, I expected those changes to not be saved... but they were. This completely subverts my expectations of a program using typical file-based operations involving opening, saving and closing files. There are three more settings that influence when the program saves, and I personally consider them a bit overkill or even detrimental to the stated purpose. What if Manuskript forces a save when nothing was changed and something goes wrong? Saving too much can in fact be dangerous! For now, I have left existing functionality as-is, but I would prefer to respect the dirty flag I have introduced in this commit for at least the 'save-on-quit' and 'save every X minutes' features. (The third is smarter and only triggers after noticing changes, so it is less important.) Making sure the dirty flag works as expected is the first step in making such changes in the future. UI-wise, this commit now offers the user the opportunity to save their changes, discard them, or outright cancel their action entirely when performing a destructive action on a dirty project. As of this commit, I have identified two of such scenarios: 1) closing the project, 2) closing the window with save-on-quit turned off. If I missed any, do let me know. But for now, maybe now I can finally start digging into those issues that sent me down this rabbit hole...
2019-04-25 09:02:37 +12:00
# Stop the timer before saving: if auto-saving fails (bugs out?) we don't want it
# to keep trying and continuously hitting the failure condition. Nor do we want to
# risk a scenario where the timer somehow triggers a new save while saving.
self.saveTimerNoChanges.stop()
2017-10-24 01:40:55 +13:00
if self.currentProject is None:
# No UI feedback here as this code path indicates a race condition that happens
# after the user has already closed the project through some way. But in that
# scenario, this code should not be reachable to begin with.
print("Bug: there is no current project to save.")
return
Track dirty state and have the UI respect it Intending to learn more about the way Manuskript goes about saving the project in order to figure out how to tackle some recent saving-related issues, I stumbled into learning that Manuskript likes to save data a whole lot. Too much, in fact. When I close the project with unsaved changes, I expected those changes to not be saved... but they were. This completely subverts my expectations of a program using typical file-based operations involving opening, saving and closing files. There are three more settings that influence when the program saves, and I personally consider them a bit overkill or even detrimental to the stated purpose. What if Manuskript forces a save when nothing was changed and something goes wrong? Saving too much can in fact be dangerous! For now, I have left existing functionality as-is, but I would prefer to respect the dirty flag I have introduced in this commit for at least the 'save-on-quit' and 'save every X minutes' features. (The third is smarter and only triggers after noticing changes, so it is less important.) Making sure the dirty flag works as expected is the first step in making such changes in the future. UI-wise, this commit now offers the user the opportunity to save their changes, discard them, or outright cancel their action entirely when performing a destructive action on a dirty project. As of this commit, I have identified two of such scenarios: 1) closing the project, 2) closing the window with save-on-quit turned off. If I missed any, do let me know. But for now, maybe now I can finally start digging into those issues that sent me down this rabbit hole...
2019-04-25 09:02:37 +12:00
r = loadSave.saveProject() # version=0
2017-12-08 22:01:58 +13:00
projectName = os.path.basename(self.currentProject)
if r:
Track dirty state and have the UI respect it Intending to learn more about the way Manuskript goes about saving the project in order to figure out how to tackle some recent saving-related issues, I stumbled into learning that Manuskript likes to save data a whole lot. Too much, in fact. When I close the project with unsaved changes, I expected those changes to not be saved... but they were. This completely subverts my expectations of a program using typical file-based operations involving opening, saving and closing files. There are three more settings that influence when the program saves, and I personally consider them a bit overkill or even detrimental to the stated purpose. What if Manuskript forces a save when nothing was changed and something goes wrong? Saving too much can in fact be dangerous! For now, I have left existing functionality as-is, but I would prefer to respect the dirty flag I have introduced in this commit for at least the 'save-on-quit' and 'save every X minutes' features. (The third is smarter and only triggers after noticing changes, so it is less important.) Making sure the dirty flag works as expected is the first step in making such changes in the future. UI-wise, this commit now offers the user the opportunity to save their changes, discard them, or outright cancel their action entirely when performing a destructive action on a dirty project. As of this commit, I have identified two of such scenarios: 1) closing the project, 2) closing the window with save-on-quit turned off. If I missed any, do let me know. But for now, maybe now I can finally start digging into those issues that sent me down this rabbit hole...
2019-04-25 09:02:37 +12:00
self.projectDirty = False # successful save, clear dirty flag
2017-12-08 22:01:58 +13:00
feedback = self.tr("Project {} saved.").format(projectName)
F.statusMessage(feedback, importance=0)
else:
2017-12-08 22:01:58 +13:00
feedback = self.tr("WARNING: Project {} not saved.").format(projectName)
F.statusMessage(feedback, importance=3)
2015-06-24 04:22:39 +12:00
2017-12-08 22:01:58 +13:00
# Giving some feedback in console
print(feedback)
2015-06-24 04:22:39 +12:00
def loadEmptyDatas(self):
self.mdlFlatData = QStandardItemModel(self)
self.mdlCharacter = characterModel(self)
2016-02-06 00:25:25 +13:00
# self.mdlPersosProxy = persosProxyModel(self)
# self.mdlPersosInfos = QStandardItemModel(self)
self.mdlLabels = QStandardItemModel(self)
self.mdlStatus = QStandardItemModel(self)
self.mdlPlots = plotModel(self)
self.mdlOutline = outlineModel(self)
self.mdlWorld = worldModel(self)
2015-06-24 04:22:39 +12:00
2015-07-07 01:00:22 +12:00
def loadDatas(self, project):
2015-06-24 04:22:39 +12:00
errors = loadSave.loadProject(project)
2015-07-02 20:08:20 +12:00
2015-06-24 04:22:39 +12:00
# Giving some feedback
if not errors:
2015-07-07 01:00:22 +12:00
print(self.tr("Project {} loaded.").format(project))
2017-11-28 03:09:07 +13:00
F.statusMessage(
2017-12-08 22:01:58 +13:00
self.tr("Project {} loaded.").format(project), 2000)
2015-06-24 04:22:39 +12:00
else:
2015-07-07 01:00:22 +12:00
print(self.tr("Project {} loaded with some errors:").format(project))
2015-06-24 04:22:39 +12:00
for e in errors:
print(self.tr(" * {} wasn't found in project file.").format(e))
2017-11-28 03:09:07 +13:00
F.statusMessage(
2017-12-08 22:01:58 +13:00
self.tr("Project {} loaded with some errors.").format(project), 5000, importance = 3)
2015-06-24 04:22:39 +12:00
2016-02-06 00:25:25 +13:00
###############################################################################
# MAIN CONNECTIONS
###############################################################################
def makeUIConnections(self):
2016-03-24 23:17:48 +13:00
"Connections that have to be made once only, even when a new project is loaded."
2017-11-21 03:42:30 +13:00
self.lstCharacters.currentItemChanged.connect(self.changeCurrentCharacter, F.AUC)
2015-07-02 20:08:20 +12:00
2017-11-21 03:42:30 +13:00
self.txtPlotFilter.textChanged.connect(self.lstPlots.setFilter, F.AUC)
self.lstPlots.currentItemChanged.connect(self.changeCurrentPlot, F.AUC)
self.lstSubPlots.clicked.connect(self.changeCurrentSubPlot, F.AUC)
2015-07-02 20:08:20 +12:00
2017-11-21 03:42:30 +13:00
self.btnRedacAddFolder.clicked.connect(self.treeRedacOutline.addFolder, F.AUC)
self.btnOutlineAddFolder.clicked.connect(self.treeOutlineOutline.addFolder, F.AUC)
self.btnRedacAddText.clicked.connect(self.treeRedacOutline.addText, F.AUC)
self.btnOutlineAddText.clicked.connect(self.treeOutlineOutline.addText, F.AUC)
self.btnRedacRemoveItem.clicked.connect(self.outlineRemoveItemsRedac, F.AUC)
self.btnOutlineRemoveItem.clicked.connect(self.outlineRemoveItemsOutline, F.AUC)
2015-07-02 20:08:20 +12:00
2015-07-10 01:01:07 +12:00
self.tabMain.currentChanged.connect(self.toolbar.setCurrentGroup)
self.tabMain.currentChanged.connect(self.tabMainChanged)
qApp.focusChanged.connect(self.focusChanged)
def makeConnections(self):
2015-06-23 07:34:11 +12:00
# Flat datas (Summary and general infos)
for widget, col in [
(self.txtSummarySituation, 0),
(self.txtSummarySentence, 1),
(self.txtSummarySentence_2, 1),
(self.txtSummaryPara, 2),
(self.txtSummaryPara_2, 2),
(self.txtPlotSummaryPara, 2),
(self.txtSummaryPage, 3),
(self.txtSummaryPage_2, 3),
(self.txtPlotSummaryPage, 3),
(self.txtSummaryFull, 4),
(self.txtPlotSummaryFull, 4),
2016-02-06 00:25:25 +13:00
]:
widget.setModel(self.mdlFlatData)
widget.setColumn(col)
widget.setCurrentModelIndex(self.mdlFlatData.index(1, col))
2015-06-23 07:34:11 +12:00
for widget, col in [
(self.txtGeneralTitle, 0),
(self.txtGeneralSubtitle, 1),
(self.txtGeneralSerie, 2),
(self.txtGeneralVolume, 3),
(self.txtGeneralGenre, 4),
(self.txtGeneralLicense, 5),
(self.txtGeneralAuthor, 6),
(self.txtGeneralEmail, 7),
2016-02-06 00:25:25 +13:00
]:
widget.setModel(self.mdlFlatData)
widget.setColumn(col)
widget.setCurrentModelIndex(self.mdlFlatData.index(0, col))
2015-06-23 07:34:11 +12:00
2016-03-24 23:17:48 +13:00
# Characters
self.lstCharacters.setCharactersModel(self.mdlCharacter)
self.tblPersoInfos.setModel(self.mdlCharacter)
2015-07-02 20:08:20 +12:00
2017-11-21 03:42:30 +13:00
self.btnAddPerso.clicked.connect(self.mdlCharacter.addCharacter, F.AUC)
2016-03-10 05:38:12 +13:00
try:
2017-11-21 03:42:30 +13:00
self.btnRmPerso.clicked.connect(self.lstCharacters.removeCharacter, F.AUC)
self.btnPersoColor.clicked.connect(self.lstCharacters.choseCharacterColor, F.AUC)
self.btnPersoAddInfo.clicked.connect(self.lstCharacters.addCharacterInfo, F.AUC)
self.btnPersoRmInfo.clicked.connect(self.lstCharacters.removeCharacterInfo, F.AUC)
2016-03-10 05:38:12 +13:00
except TypeError:
# Connection has already been made
pass
2015-07-02 20:08:20 +12:00
2015-06-29 20:22:18 +12:00
for w, c in [
2017-11-16 09:05:48 +13:00
(self.txtPersoName, Character.name),
(self.sldPersoImportance, Character.importance),
(self.txtPersoMotivation, Character.motivation),
(self.txtPersoGoal, Character.goal),
(self.txtPersoConflict, Character.conflict),
(self.txtPersoEpiphany, Character.epiphany),
(self.txtPersoSummarySentence, Character.summarySentence),
(self.txtPersoSummaryPara, Character.summaryPara),
(self.txtPersoSummaryFull, Character.summaryFull),
(self.txtPersoNotes, Character.notes)
2016-02-06 00:25:25 +13:00
]:
w.setModel(self.mdlCharacter)
2015-06-29 20:22:18 +12:00
w.setColumn(c)
self.tabPersos.setEnabled(False)
2015-07-02 20:08:20 +12:00
# Plots
2015-06-22 23:11:45 +12:00
self.lstSubPlots.setModel(self.mdlPlots)
self.lstPlotPerso.setModel(self.mdlPlots)
self.lstPlots.setPlotModel(self.mdlPlots)
2015-06-23 07:07:38 +12:00
self._updatingSubPlot = False
2017-11-21 03:42:30 +13:00
self.btnAddPlot.clicked.connect(self.mdlPlots.addPlot, F.AUC)
2015-06-23 07:34:11 +12:00
self.btnRmPlot.clicked.connect(lambda:
2017-11-21 03:42:30 +13:00
self.mdlPlots.removePlot(self.lstPlots.currentPlotIndex()), F.AUC)
self.btnAddSubPlot.clicked.connect(self.mdlPlots.addSubPlot, F.AUC)
self.btnAddSubPlot.clicked.connect(self.updateSubPlotView, F.AUC)
self.btnRmSubPlot.clicked.connect(self.mdlPlots.removeSubPlot, F.AUC)
2015-07-06 20:07:05 +12:00
self.lstPlotPerso.selectionModel().selectionChanged.connect(self.plotPersoSelectionChanged)
2017-11-21 03:42:30 +13:00
self.btnRmPlotPerso.clicked.connect(self.mdlPlots.removePlotPerso, F.AUC)
self.lstSubPlots.selectionModel().currentRowChanged.connect(self.changeCurrentSubPlot, F.AUC)
2015-06-23 07:34:11 +12:00
for w, c in [
2017-11-16 09:05:48 +13:00
(self.txtPlotName, Plot.name),
(self.txtPlotDescription, Plot.description),
(self.txtPlotResult, Plot.result),
(self.sldPlotImportance, Plot.importance),
2016-02-06 00:25:25 +13:00
]:
w.setModel(self.mdlPlots)
w.setColumn(c)
2015-06-23 07:34:11 +12:00
self.tabPlot.setEnabled(False)
2015-06-29 20:22:18 +12:00
self.mdlPlots.updatePlotPersoButton()
self.mdlCharacter.dataChanged.connect(self.mdlPlots.updatePlotPersoButton)
2015-06-23 06:30:43 +12:00
self.lstOutlinePlots.setPlotModel(self.mdlPlots)
self.lstOutlinePlots.setShowSubPlot(True)
2016-03-06 21:21:10 +13:00
self.plotCharacterDelegate = outlineCharacterDelegate(self.mdlCharacter, self)
self.lstPlotPerso.setItemDelegate(self.plotCharacterDelegate)
2015-07-07 01:00:22 +12:00
self.plotDelegate = plotDelegate(self)
2017-11-16 09:05:48 +13:00
self.lstSubPlots.setItemDelegateForColumn(PlotStep.meta, self.plotDelegate)
2015-06-23 07:34:11 +12:00
# World
self.treeWorld.setModel(self.mdlWorld)
for i in range(self.mdlWorld.columnCount()):
self.treeWorld.hideColumn(i)
self.treeWorld.showColumn(0)
self.btnWorldEmptyData.setMenu(self.mdlWorld.emptyDataMenu())
2017-11-21 03:42:30 +13:00
self.treeWorld.selectionModel().selectionChanged.connect(self.changeCurrentWorld, F.AUC)
self.btnAddWorld.clicked.connect(self.mdlWorld.addItem, F.AUC)
self.btnRmWorld.clicked.connect(self.mdlWorld.removeItem, F.AUC)
for w, c in [
2017-11-16 09:05:48 +13:00
(self.txtWorldName, World.name),
(self.txtWorldDescription, World.description),
(self.txtWorldPassion, World.passion),
(self.txtWorldConflict, World.conflict),
2016-02-06 00:25:25 +13:00
]:
w.setModel(self.mdlWorld)
w.setColumn(c)
self.tabWorld.setEnabled(False)
self.treeWorld.expandAll()
2016-02-06 00:25:25 +13:00
# Outline
self.treeRedacOutline.setModel(self.mdlOutline)
self.treeOutlineOutline.setModelCharacters(self.mdlCharacter)
self.treeOutlineOutline.setModelLabels(self.mdlLabels)
self.treeOutlineOutline.setModelStatus(self.mdlStatus)
2015-06-23 07:34:11 +12:00
self.redacMetadata.setModels(self.mdlOutline, self.mdlCharacter,
2015-06-23 07:34:11 +12:00
self.mdlLabels, self.mdlStatus)
self.outlineItemEditor.setModels(self.mdlOutline, self.mdlCharacter,
2015-06-23 07:34:11 +12:00
self.mdlLabels, self.mdlStatus)
self.treeOutlineOutline.setModel(self.mdlOutline)
2016-02-06 00:25:25 +13:00
# self.redacEditor.setModel(self.mdlOutline)
self.storylineView.setModels(self.mdlOutline, self.mdlCharacter, self.mdlPlots)
2015-06-23 07:34:11 +12:00
2017-11-21 03:42:30 +13:00
self.treeOutlineOutline.selectionModel().selectionChanged.connect(self.outlineItemEditor.selectionChanged, F.AUC)
self.treeOutlineOutline.clicked.connect(self.outlineItemEditor.selectionChanged, F.AUC)
2015-07-02 20:08:20 +12:00
2015-06-30 00:21:57 +12:00
# Sync selection
2017-11-21 03:42:30 +13:00
self.treeRedacOutline.selectionModel().selectionChanged.connect(self.redacMetadata.selectionChanged, F.AUC)
self.treeRedacOutline.clicked.connect(self.redacMetadata.selectionChanged, F.AUC)
2015-06-23 07:34:11 +12:00
2017-11-21 03:42:30 +13:00
self.treeRedacOutline.selectionModel().selectionChanged.connect(self.mainEditor.selectionChanged, F.AUC)
2015-07-02 20:08:20 +12:00
2015-06-30 22:27:43 +12:00
# Cheat Sheet
self.cheatSheet.setModels()
2015-07-02 20:08:20 +12:00
2016-02-06 00:25:25 +13:00
# Debug
self.mdlFlatData.setVerticalHeaderLabels(["General info", "Summary"])
2015-06-24 04:22:39 +12:00
self.tblDebugFlatData.setModel(self.mdlFlatData)
self.tblDebugPersos.setModel(self.mdlCharacter)
self.tblDebugPersosInfos.setModel(self.mdlCharacter)
2015-06-29 21:28:34 +12:00
self.tblDebugPersos.selectionModel().currentChanged.connect(
lambda: self.tblDebugPersosInfos.setRootIndex(self.mdlCharacter.index(
2016-02-06 00:25:25 +13:00
self.tblDebugPersos.selectionModel().currentIndex().row(),
2017-11-21 03:42:30 +13:00
Character.name)), F.AUC)
2015-07-02 20:08:20 +12:00
2015-06-24 04:22:39 +12:00
self.tblDebugPlots.setModel(self.mdlPlots)
self.tblDebugPlotsPersos.setModel(self.mdlPlots)
self.tblDebugSubPlots.setModel(self.mdlPlots)
self.tblDebugPlots.selectionModel().currentChanged.connect(
2016-02-06 00:25:25 +13:00
lambda: self.tblDebugPlotsPersos.setRootIndex(self.mdlPlots.index(
self.tblDebugPlots.selectionModel().currentIndex().row(),
2017-11-21 03:42:30 +13:00
Plot.characters)), F.AUC)
2015-06-24 04:22:39 +12:00
self.tblDebugPlots.selectionModel().currentChanged.connect(
2016-02-06 00:25:25 +13:00
lambda: self.tblDebugSubPlots.setRootIndex(self.mdlPlots.index(
self.tblDebugPlots.selectionModel().currentIndex().row(),
2017-11-21 03:42:30 +13:00
Plot.steps)), F.AUC)
self.treeDebugWorld.setModel(self.mdlWorld)
2015-06-24 04:22:39 +12:00
self.treeDebugOutline.setModel(self.mdlOutline)
self.lstDebugLabels.setModel(self.mdlLabels)
self.lstDebugStatus.setModel(self.mdlStatus)
2015-07-02 20:08:20 +12:00
def disconnectAll(self, signal, oldHandler=None):
# Disconnect all "oldHandler" slot connections for a signal
#
# Ref: PyQt Widget connect() and disconnect()
# https://stackoverflow.com/questions/21586643/pyqt-widget-connect-and-disconnect
#
# The loop is needed for safely disconnecting a specific handler,
# because it may have been connected multiple times, and
# disconnect only removes one connection at a time.
while True:
try:
if oldHandler is not None:
signal.disconnect(oldHandler)
else:
signal.disconnect()
except TypeError:
break
def breakConnections(self):
# Break connections for UI elements that were connected in makeConnections()
# Characters
self.disconnectAll(self.btnAddPerso.clicked, self.mdlCharacter.addCharacter)
self.disconnectAll(self.btnRmPerso.clicked, self.lstCharacters.removeCharacter)
self.disconnectAll(self.btnPersoColor.clicked, self.lstCharacters.choseCharacterColor)
self.disconnectAll(self.btnPersoAddInfo.clicked, self.lstCharacters.addCharacterInfo)
self.disconnectAll(self.btnPersoRmInfo.clicked, self.lstCharacters.removeCharacterInfo)
# Plots
self._updatingSubPlot = False
self.disconnectAll(self.btnAddPlot.clicked, self.mdlPlots.addPlot)
self.disconnectAll(self.btnRmPlot.clicked, lambda:
self.mdlPlots.removePlot(self.lstPlots.currentPlotIndex()))
self.disconnectAll(self.btnAddSubPlot.clicked, self.mdlPlots.addSubPlot)
self.disconnectAll(self.btnAddSubPlot.clicked, self.updateSubPlotView)
self.disconnectAll(self.btnRmSubPlot.clicked, self.mdlPlots.removeSubPlot)
self.disconnectAll(self.lstPlotPerso.selectionModel().selectionChanged, self.plotPersoSelectionChanged)
self.disconnectAll(self.lstSubPlots.selectionModel().currentRowChanged, self.changeCurrentSubPlot)
self.disconnectAll(self.btnRmPlotPerso.clicked, self.mdlPlots.removePlotPerso)
self.disconnectAll(self.mdlCharacter.dataChanged, self.mdlPlots.updatePlotPersoButton)
# World
self.disconnectAll(self.treeWorld.selectionModel().selectionChanged, self.changeCurrentWorld)
self.disconnectAll(self.btnAddWorld.clicked, self.mdlWorld.addItem)
self.disconnectAll(self.btnRmWorld.clicked, self.mdlWorld.removeItem)
# Outline
self.disconnectAll(self.treeOutlineOutline.selectionModel().selectionChanged, self.outlineItemEditor.selectionChanged)
self.disconnectAll(self.treeOutlineOutline.clicked, self.outlineItemEditor.selectionChanged)
# Sync selection
self.disconnectAll(self.treeRedacOutline.selectionModel().selectionChanged, self.redacMetadata.selectionChanged)
self.disconnectAll(self.treeRedacOutline.clicked, self.redacMetadata.selectionChanged)
self.disconnectAll(self.treeRedacOutline.selectionModel().selectionChanged, self.mainEditor.selectionChanged)
# Debug
self.disconnectAll(self.tblDebugPersos.selectionModel().currentChanged,
lambda: self.tblDebugPersosInfos.setRootIndex(self.mdlCharacter.index(
self.tblDebugPersos.selectionModel().currentIndex().row(),
2017-11-16 09:05:48 +13:00
Character.name)))
self.disconnectAll(self.tblDebugPlots.selectionModel().currentChanged,
lambda: self.tblDebugPlotsPersos.setRootIndex(self.mdlPlots.index(
self.tblDebugPlots.selectionModel().currentIndex().row(),
2017-11-16 09:05:48 +13:00
Plot.characters)))
self.disconnectAll(self.tblDebugPlots.selectionModel().currentChanged,
lambda: self.tblDebugSubPlots.setRootIndex(self.mdlPlots.index(
self.tblDebugPlots.selectionModel().currentIndex().row(),
2017-11-16 09:05:48 +13:00
Plot.steps)))
2016-02-06 00:25:25 +13:00
###############################################################################
# HELP
###############################################################################
def centerChildWindow(self, win):
r = win.geometry()
r2 = self.geometry()
win.move(r2.center() - QPoint(r.width()/2, r.height()/2))
def about(self):
self.dialog = aboutDialog(mw=self)
self.dialog.setFixedSize(self.dialog.size())
self.dialog.show()
# Center about dialog
self.centerChildWindow(self.dialog)
###############################################################################
2016-02-06 00:25:25 +13:00
# GENERAL AKA UNSORTED
###############################################################################
2015-06-23 07:34:11 +12:00
2015-05-28 13:32:09 +12:00
def clickCycle(self, i):
2015-06-23 07:34:11 +12:00
if i == 0: # step 2 - paragraph summary
2015-07-10 01:01:07 +12:00
self.tabMain.setCurrentIndex(self.TabSummary)
2015-05-28 13:32:09 +12:00
self.tabSummary.setCurrentIndex(1)
2015-06-23 07:34:11 +12:00
if i == 1: # step 3 - characters summary
2015-07-10 01:01:07 +12:00
self.tabMain.setCurrentIndex(self.TabPersos)
2015-05-28 13:32:09 +12:00
self.tabPersos.setCurrentIndex(0)
2015-06-23 07:34:11 +12:00
if i == 2: # step 4 - page summary
2015-07-10 01:01:07 +12:00
self.tabMain.setCurrentIndex(self.TabSummary)
2015-05-28 13:32:09 +12:00
self.tabSummary.setCurrentIndex(2)
2015-06-23 07:34:11 +12:00
if i == 3: # step 5 - characters description
2015-07-10 01:01:07 +12:00
self.tabMain.setCurrentIndex(self.TabPersos)
2015-05-28 13:32:09 +12:00
self.tabPersos.setCurrentIndex(1)
2015-06-23 07:34:11 +12:00
if i == 4: # step 6 - four page synopsis
2015-07-10 01:01:07 +12:00
self.tabMain.setCurrentIndex(self.TabSummary)
2015-05-28 13:32:09 +12:00
self.tabSummary.setCurrentIndex(3)
2015-06-23 07:34:11 +12:00
if i == 5: # step 7 - full character charts
2015-07-10 01:01:07 +12:00
self.tabMain.setCurrentIndex(self.TabPersos)
2015-05-28 13:32:09 +12:00
self.tabPersos.setCurrentIndex(2)
2015-06-23 07:34:11 +12:00
if i == 6: # step 8 - scene list
2015-07-10 01:01:07 +12:00
self.tabMain.setCurrentIndex(self.TabPlots)
2015-06-23 07:34:11 +12:00
2015-05-31 16:03:07 +12:00
def wordCount(self, i):
2015-06-23 07:34:11 +12:00
src = {
0: self.txtSummarySentence,
2015-06-23 07:34:11 +12:00
1: self.txtSummaryPara,
2: self.txtSummaryPage,
3: self.txtSummaryFull
2016-02-06 00:25:25 +13:00
}[i]
2015-06-23 07:34:11 +12:00
2015-05-28 13:32:09 +12:00
lbl = {
0: self.lblSummaryWCSentence,
2015-06-23 07:34:11 +12:00
1: self.lblSummaryWCPara,
2: self.lblSummaryWCPage,
3: self.lblSummaryWCFull
2016-02-06 00:25:25 +13:00
}[i]
2015-06-23 07:34:11 +12:00
2015-06-05 06:22:37 +12:00
wc = wordCount(src.toPlainText())
2015-06-23 07:34:11 +12:00
if i in [2, 3]:
pages = self.tr(" (~{} pages)").format(int(wc / 25) / 10.)
else:
pages = ""
2015-06-08 22:01:45 +12:00
lbl.setText(self.tr("Words: {}{}").format(wc, pages))
2015-06-23 07:34:11 +12:00
2015-06-04 05:25:03 +12:00
def setupMoreUi(self):
2016-02-06 00:25:25 +13:00
style.styleMainWindow(self)
2015-07-10 01:01:07 +12:00
# Tool bar on the right
self.toolbar = collapsibleDockWidgets(Qt.RightDockWidgetArea, self)
self.toolbar.addCustomWidget(self.tr("Book summary"), self.grpPlotSummary, self.TabPlots, False)
self.toolbar.addCustomWidget(self.tr("Project tree"), self.treeRedacWidget, self.TabRedac, True)
self.toolbar.addCustomWidget(self.tr("Metadata"), self.redacMetadata, self.TabRedac, False)
self.toolbar.addCustomWidget(self.tr("Story line"), self.storylineView, self.TabRedac, False)
if self._toolbarState:
self.toolbar.restoreState(self._toolbarState)
2016-02-06 00:25:25 +13:00
2017-12-05 02:18:58 +13:00
# Hides navigation dock title bar
self.dckNavigation.setTitleBarWidget(QWidget(None))
2016-02-07 06:36:02 +13:00
# Custom "tab" bar on the left
self.lstTabs.setIconSize(QSize(48, 48))
for i in range(self.tabMain.count()):
2017-10-24 01:40:55 +13:00
2017-10-16 21:48:04 +13:00
icons = [QIcon.fromTheme("stock_view-details"), #info
QIcon.fromTheme("application-text-template"), #applications-publishing
F.themeIcon("characters"),
F.themeIcon("plots"),
F.themeIcon("world"),
F.themeIcon("outline"),
QIcon.fromTheme("gtk-edit"),
QIcon.fromTheme("applications-debugging")
]
2017-10-16 21:48:04 +13:00
self.tabMain.setTabIcon(i, icons[i])
2017-10-24 01:40:55 +13:00
2016-02-07 06:36:02 +13:00
item = QListWidgetItem(self.tabMain.tabIcon(i),
self.tabMain.tabText(i))
item.setSizeHint(QSize(item.sizeHint().width(), 64))
2017-10-15 01:14:17 +13:00
item.setToolTip(self.tabMain.tabText(i))
2016-02-07 06:36:02 +13:00
item.setTextAlignment(Qt.AlignCenter)
self.lstTabs.addItem(item)
self.tabMain.tabBar().hide()
self.lstTabs.currentRowChanged.connect(self.tabMain.setCurrentIndex)
self.lstTabs.item(self.TabDebug).setHidden(not self.SHOW_DEBUG_TAB)
self.tabMain.setTabEnabled(self.TabDebug, self.SHOW_DEBUG_TAB)
2016-02-07 06:36:02 +13:00
self.tabMain.currentChanged.connect(self.lstTabs.setCurrentRow)
2015-06-04 05:25:03 +12:00
# Splitters
self.splitterPersos.setStretchFactor(0, 25)
self.splitterPersos.setStretchFactor(1, 75)
2015-06-23 07:34:11 +12:00
2015-06-04 05:25:03 +12:00
self.splitterPlot.setStretchFactor(0, 20)
2015-06-05 06:22:37 +12:00
self.splitterPlot.setStretchFactor(1, 60)
self.splitterPlot.setStretchFactor(2, 30)
2016-02-06 00:25:25 +13:00
self.splitterWorld.setStretchFactor(0, 25)
self.splitterWorld.setStretchFactor(1, 75)
2015-06-23 07:34:11 +12:00
2015-06-05 06:22:37 +12:00
self.splitterOutlineH.setStretchFactor(0, 25)
self.splitterOutlineH.setStretchFactor(1, 75)
self.splitterOutlineV.setStretchFactor(0, 75)
self.splitterOutlineV.setStretchFactor(1, 25)
2015-06-23 07:34:11 +12:00
self.splitterRedacV.setStretchFactor(0, 75)
self.splitterRedacV.setStretchFactor(1, 25)
self.splitterRedacH.setStretchFactor(0, 30)
self.splitterRedacH.setStretchFactor(1, 40)
self.splitterRedacH.setStretchFactor(2, 30)
2016-02-06 00:25:25 +13:00
# QFormLayout stretch
for w in [self.txtWorldDescription, self.txtWorldPassion, self.txtWorldConflict]:
s = w.sizePolicy()
s.setVerticalStretch(1)
w.setSizePolicy(s)
2016-02-06 00:25:25 +13:00
2015-06-04 05:25:03 +12:00
# Help box
references = [
(self.lytTabOverview,
self.tr("Enter information about your book, and yourself."),
2015-06-21 21:29:35 +12:00
0),
(self.lytSituation,
2016-02-06 00:25:25 +13:00
self.tr(
"""The basic situation, in the form of a 'What if...?' question. Ex: 'What if the most dangerous
evil wizard wasn't able to kill a baby?' (Harry Potter)"""),
2015-06-21 21:29:35 +12:00
1),
(self.lytSummary,
2016-02-06 00:25:25 +13:00
self.tr(
"""Take time to think about a one sentence (~50 words) summary of your book. Then expand it to
2016-02-06 00:25:25 +13:00
a paragraph, then to a page, then to a full summary."""),
2015-06-21 21:29:35 +12:00
1),
2015-06-04 05:25:03 +12:00
(self.lytTabPersos,
2015-06-21 21:29:35 +12:00
self.tr("Create your characters."),
0),
2015-06-04 05:25:03 +12:00
(self.lytTabPlot,
2015-06-21 21:29:35 +12:00
self.tr("Develop plots."),
0),
2017-09-24 07:53:18 +13:00
(self.lytTabContext,
self.tr("Build worlds. Create hierarchy of broad categories down to specific details."),
0),
2015-06-04 05:25:03 +12:00
(self.lytTabOutline,
2015-06-21 21:29:35 +12:00
self.tr("Create the outline of your masterpiece."),
0),
2015-06-04 05:25:03 +12:00
(self.lytTabRedac,
2015-06-21 21:29:35 +12:00
self.tr("Write."),
0),
2015-06-04 05:25:03 +12:00
(self.lytTabDebug,
self.tr("Debug info. Sometimes useful."),
2015-06-21 21:29:35 +12:00
0)
2016-02-06 00:25:25 +13:00
]
2015-06-04 05:25:03 +12:00
2015-06-21 21:29:35 +12:00
for widget, text, pos in references:
label = helpLabel(text, self)
2017-11-21 03:42:30 +13:00
self.actShowHelp.toggled.connect(label.setVisible, F.AUC)
2015-06-21 21:29:35 +12:00
widget.layout().insertWidget(pos, label)
2015-06-23 07:34:11 +12:00
2015-06-07 05:10:44 +12:00
self.actShowHelp.setChecked(False)
2015-06-23 07:34:11 +12:00
2015-06-07 05:10:44 +12:00
# Spellcheck
if Spellchecker.isInstalled():
2015-06-08 22:01:45 +12:00
self.menuDict = QMenu(self.tr("Dictionary"))
2015-06-07 05:10:44 +12:00
self.menuDictGroup = QActionGroup(self)
self.updateMenuDict()
2015-06-07 05:10:44 +12:00
self.menuTools.addMenu(self.menuDict)
2015-06-23 07:34:11 +12:00
2017-11-21 03:42:30 +13:00
self.actSpellcheck.toggled.connect(self.toggleSpellcheck, F.AUC)
# self.dictChanged.connect(self.mainEditor.setDict, F.AUC)
# self.dictChanged.connect(self.redacMetadata.setDict, F.AUC)
# self.dictChanged.connect(self.outlineItemEditor.setDict, F.AUC)
2015-06-23 07:34:11 +12:00
2015-06-07 05:10:44 +12:00
else:
# No Spell check support
self.actSpellcheck.setVisible(False)
for lib, requirement in Spellchecker.supportedLibraries().items():
a = QAction(self.tr("Install {}{} to use spellcheck").format(lib, requirement or ""), self)
a.setIcon(self.style().standardIcon(QStyle.SP_MessageBoxWarning))
# Need to bound the lib argument otherwise the lambda uses the same lib value across all calls
def gen_slot_cb(l):
return lambda: self.openSpellcheckWebPage(l)
a.triggered.connect(gen_slot_cb(lib), F.AUC)
self.menuTools.addAction(a)
2015-06-23 07:34:11 +12:00
2016-02-06 00:25:25 +13:00
###############################################################################
# SPELLCHECK
###############################################################################
2015-06-24 04:22:39 +12:00
def updateMenuDict(self):
2015-06-23 07:34:11 +12:00
if not Spellchecker.isInstalled():
2015-06-16 18:15:24 +12:00
return
2015-06-23 07:34:11 +12:00
self.menuDict.clear()
dictionaries = Spellchecker.availableDictionaries()
# Set first run dictionary
if settings.dict is None:
settings.dict = Spellchecker.getDefaultDictionary()
# Check if project dict is unavailable on this machine
dict_available = False
for lib, dicts in dictionaries.items():
if dict_available:
break
for i in dicts:
if Spellchecker.normalizeDictName(lib, i) == settings.dict:
dict_available = True
break
# Reset dict to default one if it's unavailable
if not dict_available:
settings.dict = Spellchecker.getDefaultDictionary()
for lib, dicts in dictionaries.items():
if len(dicts) > 0:
a = QAction(lib, self)
else:
a = QAction(self.tr("{} has no installed dictionaries").format(lib), self)
a.setEnabled(False)
self.menuDict.addAction(a)
for i in dicts:
a = QAction(i, self)
a.data = lib
a.setCheckable(True)
if Spellchecker.normalizeDictName(lib, i) == settings.dict:
a.setChecked(True)
a.triggered.connect(self.setDictionary, F.AUC)
self.menuDictGroup.addAction(a)
self.menuDict.addAction(a)
self.menuDict.addSeparator()
2015-06-23 07:34:11 +12:00
# If a new dictionary was chosen, apply the change and re-enable spellcheck if it was enabled.
if not dict_available:
self.setDictionary()
self.toggleSpellcheck(settings.spellcheck)
for lib, requirement in Spellchecker.supportedLibraries().items():
if lib not in dictionaries:
a = QAction(self.tr("{}{} is not installed").format(lib, requirement or ""), self)
a.setEnabled(False)
self.menuDict.addAction(a)
self.menuDict.addSeparator()
2015-06-07 05:10:44 +12:00
def setDictionary(self):
if not Spellchecker.isInstalled():
2015-06-16 18:15:24 +12:00
return
2015-06-23 07:34:11 +12:00
2015-06-07 05:10:44 +12:00
for i in self.menuDictGroup.actions():
if i.isChecked():
2016-02-06 00:25:25 +13:00
# self.dictChanged.emit(i.text().replace("&", ""))
settings.dict = Spellchecker.normalizeDictName(i.data, i.text().replace("&", ""))
2015-06-23 07:34:11 +12:00
# Find all textEditView from self, and toggle spellcheck
2015-06-23 07:34:11 +12:00
for w in self.findChildren(textEditView, QRegExp(".*"),
Qt.FindChildrenRecursively):
w.setDict(settings.dict)
2015-06-07 05:10:44 +12:00
def openSpellcheckWebPage(self, lib):
F.openURL(Spellchecker.getLibraryURL(lib))
2015-06-23 07:34:11 +12:00
def toggleSpellcheck(self, val):
settings.spellcheck = val
2015-06-23 07:34:11 +12:00
# Find all textEditView from self, and toggle spellcheck
2015-06-23 07:34:11 +12:00
for w in self.findChildren(textEditView, QRegExp(".*"),
Qt.FindChildrenRecursively):
w.toggleSpellcheck(val)
2015-06-23 07:34:11 +12:00
2016-02-06 00:25:25 +13:00
###############################################################################
# SETTINGS
###############################################################################
def settingsLabel(self):
2015-07-04 09:00:54 +12:00
self.settingsWindow(3)
2015-06-23 07:34:11 +12:00
def settingsStatus(self):
2015-07-04 09:00:54 +12:00
self.settingsWindow(4)
2015-06-23 07:34:11 +12:00
def settingsWindow(self, tab=None):
self.sw = settingsWindow(self)
self.sw.hide()
self.sw.setWindowModality(Qt.ApplicationModal)
self.sw.setWindowFlags(Qt.Dialog)
self.centerChildWindow(self.sw)
if tab:
2015-06-18 06:45:24 +12:00
self.sw.setTab(tab)
self.sw.show()
2015-06-23 07:34:11 +12:00
2016-02-09 01:50:35 +13:00
###############################################################################
# TOOLS
###############################################################################
def frequencyAnalyzer(self):
self.fw = frequencyAnalyzer(self)
self.fw.show()
self.centerChildWindow(self.fw)
2016-02-09 01:50:35 +13:00
2016-02-06 00:25:25 +13:00
###############################################################################
# VIEW MENU
###############################################################################
2015-06-23 07:34:11 +12:00
def generateViewMenu(self):
values = [
2016-02-06 00:25:25 +13:00
(self.tr("Nothing"), "Nothing"),
(self.tr("POV"), "POV"),
(self.tr("Label"), "Label"),
(self.tr("Progress"), "Progress"),
(self.tr("Compile"), "Compile"),
]
2015-06-23 07:34:11 +12:00
menus = [
(self.tr("Tree"), "Tree", "view-list-tree"),
(self.tr("Index cards"), "Cork", "view-cards"),
(self.tr("Outline"), "Outline", "view-outline")
2016-02-06 00:25:25 +13:00
]
2015-06-23 07:34:11 +12:00
submenus = {
"Tree": [
(self.tr("Icon color"), "Icon"),
(self.tr("Text color"), "Text"),
(self.tr("Background color"), "Background"),
2016-02-06 00:25:25 +13:00
],
"Cork": [
(self.tr("Icon"), "Icon"),
(self.tr("Text"), "Text"),
(self.tr("Background"), "Background"),
(self.tr("Border"), "Border"),
(self.tr("Corner"), "Corner"),
2016-02-06 00:25:25 +13:00
],
"Outline": [
(self.tr("Icon color"), "Icon"),
(self.tr("Text color"), "Text"),
(self.tr("Background color"), "Background"),
2016-02-06 00:25:25 +13:00
],
}
2015-06-23 07:34:11 +12:00
self.menuView.clear()
2016-03-25 01:42:47 +13:00
self.menuView.addMenu(self.menuMode)
self.menuView.addSeparator()
2015-06-23 07:34:11 +12:00
2016-02-06 00:25:25 +13:00
# print("Generating menus with", settings.viewSettings)
2015-06-23 07:34:11 +12:00
for mnu, mnud, icon in menus:
m = QMenu(mnu, self.menuView)
if icon:
m.setIcon(QIcon.fromTheme(icon))
for s, sd in submenus[mnud]:
m2 = QMenu(s, m)
agp = QActionGroup(m2)
for v, vd in values:
a = QAction(v, m)
a.setCheckable(True)
a.setData("{},{},{}".format(mnud, sd, vd))
if settings.viewSettings[mnud][sd] == vd:
a.setChecked(True)
2017-11-21 03:42:30 +13:00
a.triggered.connect(self.setViewSettingsAction, F.AUC)
agp.addAction(a)
m2.addAction(a)
m.addMenu(m2)
self.menuView.addMenu(m)
2015-06-23 07:34:11 +12:00
2015-06-18 04:40:55 +12:00
def setViewSettingsAction(self):
action = self.sender()
item, part, element = action.data().split(",")
2015-06-18 04:40:55 +12:00
self.setViewSettings(item, part, element)
2015-06-23 07:34:11 +12:00
2015-06-18 04:40:55 +12:00
def setViewSettings(self, item, part, element):
2015-06-16 09:15:10 +12:00
settings.viewSettings[item][part] = element
if item == "Cork":
2015-06-28 00:06:35 +12:00
self.mainEditor.updateCorkView()
2015-06-18 03:15:13 +12:00
if item == "Outline":
2015-06-28 00:06:35 +12:00
self.mainEditor.updateTreeView()
self.treeOutlineOutline.viewport().update()
2015-06-18 03:15:13 +12:00
if item == "Tree":
2015-07-01 23:14:03 +12:00
self.treeRedacOutline.viewport().update()
2015-07-02 20:08:20 +12:00
2016-03-25 01:42:47 +13:00
###############################################################################
# VIEW MODES
###############################################################################
def setViewModeSimple(self):
settings.viewMode = "simple"
self.tabMain.setCurrentIndex(self.TabRedac)
self.viewModeFictionVisibilitySwitch(False)
self.actModeSimple.setChecked(True)
def setViewModeFiction(self):
settings.viewMode = "fiction"
self.viewModeFictionVisibilitySwitch(True)
self.actModeFiction.setChecked(True)
def viewModeFictionVisibilitySwitch(self, val):
"""
Switches the visibility of some UI components useful for fiction only
2016-03-25 01:42:47 +13:00
@param val: sets visibility to val
"""
# Menu navigation & button in toolbar
2016-03-25 01:42:47 +13:00
self.toolbar.setDockVisibility(self.dckNavigation, val)
# POV in metadata
2016-03-25 01:42:47 +13:00
from manuskript.ui.views.propertiesView import propertiesView
for w in findWidgetsOfClass(propertiesView):
w.lblPOV.setVisible(val)
w.cmbPOV.setVisible(val)
# POV in outline view
if val is None and Outline.POV in settings.outlineViewColumns:
2017-11-16 08:58:12 +13:00
settings.outlineViewColumns.remove(Outline.POV)
2016-03-25 01:42:47 +13:00
from manuskript.ui.views.outlineView import outlineView
for w in findWidgetsOfClass(outlineView):
w.hideColumns()
# TODO: clean up all other fiction things in non-fiction view mode
# Character in search widget
# POV in settings / views
2016-02-06 00:25:25 +13:00
###############################################################################
2017-11-06 21:16:44 +13:00
# IMPORT / EXPORT
2016-02-06 00:25:25 +13:00
###############################################################################
2015-07-01 23:14:03 +12:00
2017-11-06 21:16:44 +13:00
def doImport(self):
# Warn about buggy Qt versions and import crash
#
# (Py)Qt 5.11 and 5.12 have a bug that can cause crashes when simply
# setting up various UI elements.
# This has been reported and verified to happen with File -> Import.
# See PR #611.
if re.match("^5\\.1[12](\\.?|$)", qVersion()):
warning1 = self.tr("PyQt / Qt versions 5.11 and 5.12 are known to cause a crash which might result in a loss of data.")
warning2 = self.tr("PyQt {} and Qt {} are in use.").format(qVersion(), PYQT_VERSION_STR)
# Don't translate for debug log.
print("WARNING:", warning1, warning2)
msg = QMessageBox(QMessageBox.Warning,
self.tr("Proceeding might crash and lose data"),
"<p><b>" +
warning1 +
"</b></p>" +
"<p>" +
warning2 +
"</p>",
QMessageBox.Abort | QMessageBox.Ignore)
msg.setDefaultButton(QMessageBox.Abort)
# Return because user heeds warning
if msg.exec() == QMessageBox.Abort:
return
# Proceed with Import
2017-11-06 21:16:44 +13:00
self.dialog = importerDialog(mw=self)
self.dialog.show()
self.centerChildWindow(self.dialog)
2017-11-06 21:16:44 +13:00
2015-07-01 23:14:03 +12:00
def doCompile(self):
2016-04-05 06:00:19 +12:00
self.dialog = exporterDialog(mw=self)
2016-04-02 06:01:27 +13:00
self.dialog.show()
self.centerChildWindow(self.dialog)