mirror of
https://github.com/olivierkes/manuskript.git
synced 2024-05-10 07:52:35 +12:00
Merge branch 'feature/PlotViewer' into develop
This commit is contained in:
commit
1f5445b20d
|
@ -322,6 +322,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
self.mainEditor.setFolderView(settings.folderView)
|
||||
self.mainEditor.updateFolderViewButtons(settings.folderView)
|
||||
self.tabMain.setCurrentIndex(settings.lastTab)
|
||||
# We force to emit even if it opens on the current tab
|
||||
self.tabMain.currentChanged.emit(settings.lastTab)
|
||||
self.mainEditor.updateCorkBackground()
|
||||
|
||||
# Set autosave
|
||||
|
@ -406,6 +408,16 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
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 balue
|
||||
self._toolbarState = sttgns.value("toolbar")
|
||||
else:
|
||||
self._toolbarState = ""
|
||||
|
||||
|
||||
def closeEvent(self, event):
|
||||
# Save State and geometry and other things
|
||||
|
@ -413,8 +425,10 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
sttgns.setValue("geometry", self.saveGeometry())
|
||||
sttgns.setValue("windowState", self.saveState())
|
||||
sttgns.setValue("metadataState", self.redacMetadata.saveState())
|
||||
sttgns.setValue("metadataState", self.redacMetadata.saveState())
|
||||
sttgns.setValue("revisionsState", self.redacMetadata.revisions.saveState())
|
||||
sttgns.setValue("splitterRedacH", self.splitterRedacH.saveState())
|
||||
sttgns.setValue("splitterRedacV", self.splitterRedacV.saveState())
|
||||
sttgns.setValue("toolbar", self.toolbar.saveState())
|
||||
|
||||
# Specific settings to save before quitting
|
||||
settings.lastTab = self.tabMain.currentIndex()
|
||||
|
@ -699,6 +713,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
|
||||
self.treeOutlineOutline.setModel(self.mdlOutline)
|
||||
# self.redacEditor.setModel(self.mdlOutline)
|
||||
self.storylineView.setModels(self.mdlOutline, self.mdlPersos, self.mdlPlots)
|
||||
|
||||
self.treeOutlineOutline.selectionModel().selectionChanged.connect(lambda:
|
||||
self.outlineItemEditor.selectionChanged(
|
||||
|
@ -799,6 +814,9 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
self.toolbar.addCustomWidget(self.tr("Book summary"), self.grpPlotSummary, self.TabPlots)
|
||||
self.toolbar.addCustomWidget(self.tr("Project tree"), self.treeRedacWidget, self.TabRedac)
|
||||
self.toolbar.addCustomWidget(self.tr("Metadata"), self.redacMetadata, self.TabRedac)
|
||||
self.toolbar.addCustomWidget(self.tr("Story line"), self.storylineView, self.TabRedac)
|
||||
if self._toolbarState:
|
||||
self.toolbar.restoreState(self._toolbarState)
|
||||
|
||||
# Custom "tab" bar on the left
|
||||
self.lstTabs.setIconSize(QSize(48, 48))
|
||||
|
@ -838,9 +856,12 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
self.splitterOutlineV.setStretchFactor(0, 75)
|
||||
self.splitterOutlineV.setStretchFactor(1, 25)
|
||||
|
||||
self.splitterRedac.setStretchFactor(0, 30)
|
||||
self.splitterRedac.setStretchFactor(1, 40)
|
||||
self.splitterRedac.setStretchFactor(2, 30)
|
||||
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)
|
||||
|
||||
# QFormLayout stretch
|
||||
for w in [self.txtWorldDescription, self.txtWorldPassion, self.txtWorldConflict]:
|
||||
|
|
|
@ -836,11 +836,20 @@ class outlineItem():
|
|||
|
||||
return lst
|
||||
|
||||
def findItemsContaining(self, text, columns, mainWindow, caseSensitive=False):
|
||||
def findItemsContaining(self, text, columns, mainWindow=mainWindow(), caseSensitive=False, recursive=True):
|
||||
"""Returns a list if IDs of all subitems
|
||||
containing ``text`` in columns ``columns``
|
||||
(being a list of int).
|
||||
"""
|
||||
lst = self.itemContains(text, columns, mainWindow, caseSensitive)
|
||||
|
||||
if recursive:
|
||||
for c in self.children():
|
||||
lst.extend(c.findItemsContaining(text, columns, mainWindow, caseSensitive))
|
||||
|
||||
return lst
|
||||
|
||||
def itemContains(self, text, columns, mainWindow=mainWindow(), caseSensitive=False):
|
||||
lst = []
|
||||
text = text.lower() if not caseSensitive else text
|
||||
for c in columns:
|
||||
|
@ -863,9 +872,6 @@ class outlineItem():
|
|||
if not self.ID() in lst:
|
||||
lst.append(self.ID())
|
||||
|
||||
for c in self.children():
|
||||
lst.extend(c.findItemsContaining(text, columns, mainWindow, caseSensitive))
|
||||
|
||||
return lst
|
||||
|
||||
###############################################################################
|
||||
|
|
|
@ -21,30 +21,47 @@ RegEx = r"{(\w):(\d+):?.*?}"
|
|||
RegExNonCapturing = r"{\w:\d+:?.*?}"
|
||||
# The basic format of the references
|
||||
EmptyRef = "{{{}:{}:{}}}"
|
||||
EmptyRefSearchable = "{{{}:{}:"
|
||||
PersoLetter = "C"
|
||||
TextLetter = "T"
|
||||
PlotLetter = "P"
|
||||
WorldLetter = "W"
|
||||
|
||||
|
||||
def plotReference(ID):
|
||||
"""Takes the ID of a plot and returns a reference for that plot."""
|
||||
return EmptyRef.format(PlotLetter, ID, "")
|
||||
def plotReference(ID, searchable=False):
|
||||
"""Takes the ID of a plot and returns a reference for that plot.
|
||||
@searchable: returns a stripped version that allows simple text search."""
|
||||
if not searchable:
|
||||
return EmptyRef.format(PlotLetter, ID, "")
|
||||
else:
|
||||
return EmptyRefSearchable.format(PlotLetter, ID, "")
|
||||
|
||||
|
||||
def persoReference(ID):
|
||||
"""Takes the ID of a character and returns a reference for that character."""
|
||||
return EmptyRef.format(PersoLetter, ID, "")
|
||||
def persoReference(ID, searchable=False):
|
||||
"""Takes the ID of a character and returns a reference for that character.
|
||||
@searchable: returns a stripped version that allows simple text search."""
|
||||
if not searchable:
|
||||
return EmptyRef.format(PersoLetter, ID, "")
|
||||
else:
|
||||
return EmptyRefSearchable.format(PersoLetter, ID, "")
|
||||
|
||||
|
||||
def textReference(ID):
|
||||
"""Takes the ID of an outline item and returns a reference for that item."""
|
||||
return EmptyRef.format(TextLetter, ID, "")
|
||||
def textReference(ID, searchable=False):
|
||||
"""Takes the ID of an outline item and returns a reference for that item.
|
||||
@searchable: returns a stripped version that allows simple text search."""
|
||||
if not searchable:
|
||||
return EmptyRef.format(TextLetter, ID, "")
|
||||
else:
|
||||
return EmptyRefSearchable.format(TextLetter, ID, "")
|
||||
|
||||
|
||||
def worldReference(ID):
|
||||
"""Takes the ID of a world item and returns a reference for that item."""
|
||||
return EmptyRef.format(WorldLetter, ID, "")
|
||||
def worldReference(ID, searchable=False):
|
||||
"""Takes the ID of a world item and returns a reference for that item.
|
||||
@searchable: returns a stripped version that allows simple text search."""
|
||||
if not searchable:
|
||||
return EmptyRef.format(WorldLetter, ID, "")
|
||||
else:
|
||||
return EmptyRefSearchable.format(WorldLetter, ID, "")
|
||||
|
||||
|
||||
###############################################################################
|
||||
|
@ -375,7 +392,10 @@ def tooltip(ref):
|
|||
|
||||
item = idx.internalPointer()
|
||||
|
||||
tt = qApp.translate("references", "Text: <b>{}</b>").format(item.title())
|
||||
if item.isFolder():
|
||||
tt = qApp.translate("references", "Folder: <b>{}</b>").format(item.title())
|
||||
else:
|
||||
tt = qApp.translate("references", "Text: <b>{}</b>").format(item.title())
|
||||
tt += "<br><i>{}</i>".format(item.path())
|
||||
|
||||
return tt
|
||||
|
@ -451,11 +471,32 @@ def linkifyAllRefs(text):
|
|||
return re.sub(RegEx, lambda m: refToLink(m.group(0)), text)
|
||||
|
||||
|
||||
def findReferencesTo(ref, parent=None, recursive=True):
|
||||
"""List of text items containing references ref, and returns IDs.
|
||||
Starts from item parent. If None, starts from root."""
|
||||
oM = mainWindow().mdlOutline
|
||||
|
||||
if parent == None:
|
||||
parent = oM.rootItem
|
||||
|
||||
# Removes everything after the second ':': '{L:ID:random text}' → '{L:ID:'
|
||||
ref = ref[:ref.index(":", ref.index(":") + 1)+1]
|
||||
|
||||
# Bare form '{L:ID}'
|
||||
ref2 = ref[:-1] + "}"
|
||||
|
||||
# Since it's a simple search (no regex), we search for both.
|
||||
lst = parent.findItemsContaining(ref, [Outline.notes.value], recursive=recursive)
|
||||
lst += parent.findItemsContaining(ref2, [Outline.notes.value], recursive=recursive)
|
||||
|
||||
return lst
|
||||
|
||||
def listReferences(ref, title=qApp.translate("references", "Referenced in:")):
|
||||
oM = mainWindow().mdlOutline
|
||||
listRefs = ""
|
||||
ref = ref[:ref.index(":", ref.index(":") + 1)]
|
||||
lst = oM.findItemsContaining(ref, [Outline.notes.value])
|
||||
|
||||
lst = findReferencesTo(ref)
|
||||
|
||||
for t in lst:
|
||||
idx = oM.getIndexByID(t)
|
||||
listRefs += "<li><a href='{link}'>{text}</a></li>".format(
|
||||
|
|
|
@ -69,6 +69,7 @@ class collapsibleDockWidgets(QToolBar):
|
|||
# widget.installEventFilter(self)
|
||||
b = verticalButton(self)
|
||||
b.setDefaultAction(a)
|
||||
#b.setChecked(widget.isVisible())
|
||||
a2 = self.addWidget(b)
|
||||
self.otherWidgets.append((b, a2, widget, group))
|
||||
|
||||
|
@ -87,6 +88,22 @@ class collapsibleDockWidgets(QToolBar):
|
|||
else:
|
||||
action.setVisible(True)
|
||||
|
||||
def saveState(self):
|
||||
# We just need to save states of the custom widgets.
|
||||
state = []
|
||||
for btn, act, w, grp in self.otherWidgets:
|
||||
state.append(
|
||||
(grp, btn.text(), btn.isChecked())
|
||||
)
|
||||
return state
|
||||
|
||||
def restoreState(self, state):
|
||||
for group, title, status in state:
|
||||
for btn, act, widget, grp in self.otherWidgets:
|
||||
if group == grp and title == btn.text():
|
||||
btn.setChecked(status)
|
||||
widget.setVisible(status)
|
||||
|
||||
|
||||
class verticalButton(QToolButton):
|
||||
def __init__(self, parent):
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
# Form implementation generated from reading ui file 'manuskript/ui/mainWindow.ui'
|
||||
#
|
||||
# Created: Mon Feb 8 11:20:10 2016
|
||||
# Created: Sun Feb 28 13:22:17 2016
|
||||
# by: PyQt5 UI code generator 5.2.1
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
@ -869,10 +869,15 @@ class Ui_MainWindow(object):
|
|||
self.verticalLayout_15 = QtWidgets.QVBoxLayout(self.lytTabRedac)
|
||||
self.verticalLayout_15.setContentsMargins(0, 0, 0, 0)
|
||||
self.verticalLayout_15.setObjectName("verticalLayout_15")
|
||||
self.splitterRedac = QtWidgets.QSplitter(self.lytTabRedac)
|
||||
self.splitterRedac.setOrientation(QtCore.Qt.Horizontal)
|
||||
self.splitterRedac.setObjectName("splitterRedac")
|
||||
self.treeRedacWidget = QtWidgets.QWidget(self.splitterRedac)
|
||||
self.splitterRedacV = QtWidgets.QSplitter(self.lytTabRedac)
|
||||
self.splitterRedacV.setOrientation(QtCore.Qt.Vertical)
|
||||
self.splitterRedacV.setChildrenCollapsible(False)
|
||||
self.splitterRedacV.setObjectName("splitterRedacV")
|
||||
self.splitterRedacH = QtWidgets.QSplitter(self.splitterRedacV)
|
||||
self.splitterRedacH.setOrientation(QtCore.Qt.Horizontal)
|
||||
self.splitterRedacH.setChildrenCollapsible(False)
|
||||
self.splitterRedacH.setObjectName("splitterRedacH")
|
||||
self.treeRedacWidget = QtWidgets.QWidget(self.splitterRedacH)
|
||||
self.treeRedacWidget.setObjectName("treeRedacWidget")
|
||||
self.verticalLayout_30 = QtWidgets.QVBoxLayout(self.treeRedacWidget)
|
||||
self.verticalLayout_30.setContentsMargins(0, 0, 0, 0)
|
||||
|
@ -905,11 +910,13 @@ class Ui_MainWindow(object):
|
|||
spacerItem16 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||
self.horizontalLayout_31.addItem(spacerItem16)
|
||||
self.verticalLayout_30.addLayout(self.horizontalLayout_31)
|
||||
self.mainEditor = mainEditor(self.splitterRedac)
|
||||
self.mainEditor = mainEditor(self.splitterRedacH)
|
||||
self.mainEditor.setObjectName("mainEditor")
|
||||
self.redacMetadata = metadataView(self.splitterRedac)
|
||||
self.redacMetadata = metadataView(self.splitterRedacH)
|
||||
self.redacMetadata.setObjectName("redacMetadata")
|
||||
self.verticalLayout_15.addWidget(self.splitterRedac)
|
||||
self.storylineView = storylineView(self.splitterRedacV)
|
||||
self.storylineView.setObjectName("storylineView")
|
||||
self.verticalLayout_15.addWidget(self.splitterRedacV)
|
||||
self.tabMain.addTab(self.lytTabRedac, "")
|
||||
self.lytTabDebug = QtWidgets.QWidget()
|
||||
self.lytTabDebug.setObjectName("lytTabDebug")
|
||||
|
@ -1159,7 +1166,7 @@ class Ui_MainWindow(object):
|
|||
|
||||
self.retranslateUi(MainWindow)
|
||||
self.stack.setCurrentIndex(1)
|
||||
self.tabMain.setCurrentIndex(4)
|
||||
self.tabMain.setCurrentIndex(6)
|
||||
self.tabSummary.setCurrentIndex(0)
|
||||
self.tabPersos.setCurrentIndex(0)
|
||||
self.tabPlot.setCurrentIndex(0)
|
||||
|
@ -1294,19 +1301,20 @@ class Ui_MainWindow(object):
|
|||
self.actCloseProject.setText(_translate("MainWindow", "&Close project"))
|
||||
self.actCompile.setText(_translate("MainWindow", "Co&mpile"))
|
||||
self.actCompile.setShortcut(_translate("MainWindow", "F6"))
|
||||
self.actToolFrequency.setText(_translate("MainWindow", "Frequency Analyzer"))
|
||||
self.actToolFrequency.setText(_translate("MainWindow", "&Frequency Analyzer"))
|
||||
|
||||
from manuskript.ui.welcome import welcome
|
||||
from manuskript.ui.views.metadataView import metadataView
|
||||
from manuskript.ui.views.basicItemView import basicItemView
|
||||
from manuskript.ui.editors.mainEditor import mainEditor
|
||||
from manuskript.ui.views.plotTreeView import plotTreeView
|
||||
from manuskript.ui.sldImportance import sldImportance
|
||||
from manuskript.ui.views.textEditCompleter import textEditCompleter
|
||||
from manuskript.ui.views.treeView import treeView
|
||||
from manuskript.ui.views.persoTreeView import persoTreeView
|
||||
from manuskript.ui.search import search
|
||||
from manuskript.ui.cheatSheet import cheatSheet
|
||||
from manuskript.ui.views.outlineView import outlineView
|
||||
from manuskript.ui.views.textEditView import textEditView
|
||||
from manuskript.ui.views.lineEditView import lineEditView
|
||||
from manuskript.ui.cheatSheet import cheatSheet
|
||||
from manuskript.ui.editors.mainEditor import mainEditor
|
||||
from manuskript.ui.views.outlineView import outlineView
|
||||
from manuskript.ui.views.treeView import treeView
|
||||
from manuskript.ui.views.textEditView import textEditView
|
||||
from manuskript.ui.views.storylineView import storylineView
|
||||
from manuskript.ui.views.persoTreeView import persoTreeView
|
||||
from manuskript.ui.views.plotTreeView import plotTreeView
|
||||
from manuskript.ui.search import search
|
||||
from manuskript.ui.views.basicItemView import basicItemView
|
||||
from manuskript.ui.welcome import welcome
|
||||
from manuskript.ui.views.textEditCompleter import textEditCompleter
|
||||
from manuskript.ui.sldImportance import sldImportance
|
||||
from manuskript.ui.views.metadataView import metadataView
|
||||
|
|
|
@ -124,7 +124,7 @@
|
|||
<enum>QTabWidget::Rounded</enum>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>4</number>
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="documentMode">
|
||||
<bool>true</bool>
|
||||
|
@ -1770,88 +1770,100 @@
|
|||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QSplitter" name="splitterRedac">
|
||||
<widget class="QSplitter" name="splitterRedacV">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<widget class="QWidget" name="treeRedacWidget" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_30">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="treeView" name="treeRedacOutline">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::EditKeyPressed</set>
|
||||
</property>
|
||||
<attribute name="headerVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_31">
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnRedacAddFolder">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="folder-new">
|
||||
<normaloff>../../../../../../../.designer/backup</normaloff>../../../../../../../.designer/backup</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnRedacAddText">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="document-new">
|
||||
<normaloff>../../../../../../../.designer/backup</normaloff>../../../../../../../.designer/backup</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnRedacRemoveItem">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="list-remove">
|
||||
<normaloff>../../../../../../../.designer/backup</normaloff>../../../../../../../.designer/backup</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_7">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
<property name="childrenCollapsible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<widget class="QSplitter" name="splitterRedacH">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="childrenCollapsible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="treeRedacWidget" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_30">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="treeView" name="treeRedacOutline">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::EditKeyPressed</set>
|
||||
</property>
|
||||
<attribute name="headerVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_31">
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnRedacAddFolder">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="folder-new">
|
||||
<normaloff>../../../../../../../.designer/backup</normaloff>../../../../../../../.designer/backup</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnRedacAddText">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="document-new">
|
||||
<normaloff>../../../../../../../.designer/backup</normaloff>../../../../../../../.designer/backup</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnRedacRemoveItem">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="list-remove">
|
||||
<normaloff>../../../../../../../.designer/backup</normaloff>../../../../../../../.designer/backup</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_7">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="mainEditor" name="mainEditor" native="true"/>
|
||||
<widget class="metadataView" name="redacMetadata" native="true"/>
|
||||
</widget>
|
||||
<widget class="mainEditor" name="mainEditor" native="true"/>
|
||||
<widget class="metadataView" name="redacMetadata" native="true"/>
|
||||
<widget class="storylineView" name="storylineView" native="true"/>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -2323,7 +2335,7 @@ QListView::item:hover {
|
|||
</action>
|
||||
<action name="actToolFrequency">
|
||||
<property name="text">
|
||||
<string>Frequency Analyzer</string>
|
||||
<string>&Frequency Analyzer</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
|
@ -2405,6 +2417,12 @@ QListView::item:hover {
|
|||
<extends>QTextEdit</extends>
|
||||
<header>manuskript.ui.views.textEditCompleter.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>storylineView</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>manuskript.ui.views.storylineView.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
|
|
265
manuskript/ui/views/storylineView.py
Normal file
265
manuskript/ui/views/storylineView.py
Normal file
|
@ -0,0 +1,265 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
from PyQt5.QtCore import Qt, QTimer, QRectF
|
||||
from PyQt5.QtGui import QBrush, QPen, QFontMetrics, QFontMetricsF
|
||||
from PyQt5.QtWidgets import QWidget, QGraphicsScene, QGraphicsSimpleTextItem, QMenu, QAction, QGraphicsRectItem, \
|
||||
QGraphicsLineItem, QGraphicsEllipseItem
|
||||
|
||||
from manuskript.enums import Outline
|
||||
from manuskript.functions import randomColor
|
||||
from manuskript.models import references
|
||||
from manuskript.ui.views.storylineView_ui import Ui_storylineView
|
||||
|
||||
|
||||
class storylineView(QWidget, Ui_storylineView):
|
||||
def __init__(self, parent=None):
|
||||
QWidget.__init__(self, parent)
|
||||
self.setupUi(self)
|
||||
self._mdlPlots = None
|
||||
self.scene = QGraphicsScene()
|
||||
self.view.setScene(self.scene)
|
||||
|
||||
self.reloadTimer = QTimer()
|
||||
self.reloadTimer.timeout.connect(self.refresh)
|
||||
self.reloadTimer.setSingleShot(True)
|
||||
self.reloadTimer.setInterval(500)
|
||||
|
||||
self.btnRefresh.clicked.connect(self.refresh)
|
||||
self.sldTxtSize.sliderMoved.connect(self.reloadTimer.start)
|
||||
|
||||
self.generateMenu()
|
||||
|
||||
def generateMenu(self):
|
||||
m = QMenu()
|
||||
|
||||
for i in [
|
||||
self.tr("Show Plots"),
|
||||
self.tr("Show Characters"),
|
||||
self.tr("Show Objects"),
|
||||
]:
|
||||
a = QAction(i, m)
|
||||
a.setCheckable(True)
|
||||
a.setEnabled(False)
|
||||
m.addAction(a)
|
||||
|
||||
self.btnSettings.setMenu(m)
|
||||
|
||||
def setModels(self, mdlOutline, mdlPersos, mdlPlots):
|
||||
self._mdlPlots = mdlPlots
|
||||
# self._mdlPlots.dataChanged.connect(self.refresh)
|
||||
# self._mdlPlots.rowsInserted.connect(self.refresh)
|
||||
|
||||
self._mdlOutline = mdlOutline
|
||||
self._mdlOutline.dataChanged.connect(self.reloadTimer.start)
|
||||
|
||||
self._mdlPersos = mdlPersos
|
||||
self._mdlPersos.dataChanged.connect(self.reloadTimer.start)
|
||||
|
||||
|
||||
def refresh(self):
|
||||
if not self._mdlPlots or not self._mdlOutline or not self._mdlPersos:
|
||||
pass
|
||||
|
||||
LINE_HEIGHT = 18
|
||||
SPACING = 3
|
||||
TEXT_WIDTH = self.sldTxtSize.value()
|
||||
CIRCLE_WIDTH = 10
|
||||
LEVEL_HEIGHT = 12
|
||||
|
||||
s = self.scene
|
||||
s.clear()
|
||||
|
||||
# Get Max Level (max depth)
|
||||
root = self._mdlOutline.rootItem
|
||||
def maxLevel(item, level=0, max=0):
|
||||
if level > max:
|
||||
max = level
|
||||
for c in item.children():
|
||||
m = maxLevel(c, level + 1)
|
||||
if m > max:
|
||||
max = m
|
||||
return max
|
||||
|
||||
MAX_LEVEL = maxLevel(root)
|
||||
|
||||
# Generate left entries
|
||||
# (As of now, plot only)
|
||||
plotsID = self._mdlPlots.getPlotsByImportance()
|
||||
trackedItems = []
|
||||
fm = QFontMetrics(s.font())
|
||||
max_name = 0
|
||||
|
||||
for importance in plotsID:
|
||||
for ID in importance:
|
||||
name = self._mdlPlots.getPlotNameByID(ID)
|
||||
ref = references.plotReference(ID, searchable=True)
|
||||
|
||||
trackedItems.append((ID, ref, name))
|
||||
max_name = max(fm.width(name), max_name)
|
||||
|
||||
ROWS_HEIGHT = len(trackedItems) * (LINE_HEIGHT + SPACING )
|
||||
TITLE_WIDTH = max_name + 2 * SPACING
|
||||
|
||||
|
||||
# Add Folders and Texts
|
||||
outline = OutlineRect(0, 0, 0, ROWS_HEIGHT + SPACING + MAX_LEVEL * LEVEL_HEIGHT)
|
||||
s.addItem(outline)
|
||||
outline.setPos(TITLE_WIDTH + SPACING, 0)
|
||||
|
||||
refCircles = [] # a list of all references, to be added later on the lines
|
||||
|
||||
# A Function to add a rect with centered elided text
|
||||
def addRectText(x, w, parent, text="", level=0, tooltip=""):
|
||||
deltaH = LEVEL_HEIGHT if level else 0
|
||||
r = OutlineRect(0, 0, w, parent.rect().height()-deltaH, parent, title=text)
|
||||
r.setPos(x, deltaH)
|
||||
|
||||
txt = QGraphicsSimpleTextItem(text, r)
|
||||
f = txt.font()
|
||||
f.setPointSize(8)
|
||||
fm = QFontMetricsF(f)
|
||||
elidedText = fm.elidedText(text, Qt.ElideMiddle, w)
|
||||
txt.setFont(f)
|
||||
txt.setText(elidedText)
|
||||
txt.setPos(r.boundingRect().center() - txt.boundingRect().center())
|
||||
txt.setY(0)
|
||||
return r
|
||||
|
||||
# A function to returns an item's width, by counting its children
|
||||
def itemWidth(item):
|
||||
if item.isFolder():
|
||||
r = 0
|
||||
for c in item.children():
|
||||
r += itemWidth(c)
|
||||
return r or TEXT_WIDTH
|
||||
else:
|
||||
return TEXT_WIDTH
|
||||
|
||||
def listItems(item, rect, level=0):
|
||||
delta = 0
|
||||
for child in item.children():
|
||||
w = itemWidth(child)
|
||||
|
||||
if child.isFolder():
|
||||
parent = addRectText(delta, w, rect, child.title(), level, tooltip=child.title())
|
||||
parent.setToolTip(references.tooltip(references.textReference(child.ID())))
|
||||
listItems(child, parent, level + 1)
|
||||
|
||||
else:
|
||||
rectChild = addRectText(delta, TEXT_WIDTH, rect, "", level, tooltip=child.title())
|
||||
rectChild.setToolTip(references.tooltip(references.textReference(child.ID())))
|
||||
|
||||
# Find tracked references in that scene (or parent folders)
|
||||
for ID, ref, name in trackedItems:
|
||||
|
||||
result = []
|
||||
c = child
|
||||
while c:
|
||||
result += references.findReferencesTo(ref, c, recursive=False)
|
||||
c = c.parent()
|
||||
|
||||
if result:
|
||||
ref2 = result[0]
|
||||
|
||||
# Create a RefCircle with the reference
|
||||
c = RefCircle(TEXT_WIDTH / 2, - CIRCLE_WIDTH / 2, CIRCLE_WIDTH, ID=ref2)
|
||||
|
||||
# Store it, with the position of that item, to display it on the line later on
|
||||
refCircles.append((ref, c, rect.mapToItem(outline, rectChild.pos())))
|
||||
|
||||
delta += w
|
||||
|
||||
listItems(root, outline)
|
||||
|
||||
OUTLINE_WIDTH = itemWidth(root)
|
||||
|
||||
# Add Plots
|
||||
i = 0
|
||||
itemsRect = s.addRect(0, 0, 0, 0)
|
||||
itemsRect.setPos(0, MAX_LEVEL * LEVEL_HEIGHT + SPACING)
|
||||
|
||||
for ID, ref, name in trackedItems:
|
||||
color = randomColor()
|
||||
|
||||
# Rect
|
||||
r = QGraphicsRectItem(0, 0, TITLE_WIDTH, LINE_HEIGHT, itemsRect)
|
||||
r.setPen(QPen(Qt.NoPen))
|
||||
r.setBrush(QBrush(color))
|
||||
r.setPos(0, i * LINE_HEIGHT + i * SPACING)
|
||||
i += 1
|
||||
|
||||
# Text
|
||||
txt = QGraphicsSimpleTextItem(name, r)
|
||||
txt.setPos(r.boundingRect().center() - txt.boundingRect().center())
|
||||
|
||||
# Line
|
||||
line = PlotLine(0, 0,
|
||||
OUTLINE_WIDTH + SPACING, 0)
|
||||
line.setPos(TITLE_WIDTH, r.mapToScene(r.rect().center()).y())
|
||||
s.addItem(line)
|
||||
line.setPen(QPen(color, 5))
|
||||
line.setToolTip(self.tr("Plot: ") + name)
|
||||
|
||||
# We add the circles / references to text, on the line
|
||||
for ref2, circle, pos in refCircles:
|
||||
if ref2 == ref:
|
||||
circle.setParentItem(line)
|
||||
circle.setPos(pos.x(), 0)
|
||||
|
||||
# self.view.fitInView(0, 0, TOTAL_WIDTH, i * LINE_HEIGHT, Qt.KeepAspectRatioByExpanding) # KeepAspectRatio
|
||||
self.view.setSceneRect(0, 0, 0, 0)
|
||||
|
||||
|
||||
class OutlineRect(QGraphicsRectItem):
|
||||
def __init__(self, x, y, w, h, parent=None, title=None):
|
||||
QGraphicsRectItem.__init__(self, x, y, w, h, parent)
|
||||
self.setBrush(Qt.white)
|
||||
self.setAcceptHoverEvents(True)
|
||||
self._title = title
|
||||
|
||||
def hoverEnterEvent(self, event):
|
||||
self.setBrush(Qt.lightGray)
|
||||
|
||||
def hoverLeaveEvent(self, event):
|
||||
self.setBrush(Qt.white)
|
||||
|
||||
|
||||
class RefCircle(QGraphicsEllipseItem):
|
||||
def __init__(self, x, y, diameter, parent=None, ID=None):
|
||||
QGraphicsEllipseItem.__init__(self, x, y, diameter, diameter, parent)
|
||||
self.setBrush(Qt.white)
|
||||
self._ref = references.textReference(ID)
|
||||
self.setToolTip(references.tooltip(self._ref))
|
||||
self.setPen(QPen(Qt.black, 2))
|
||||
self.setAcceptHoverEvents(True)
|
||||
|
||||
def multiplyDiameter(self, factor):
|
||||
r1 = self.rect()
|
||||
r2 = QRectF(0, 0, r1.width() * factor, r1.height() * factor)
|
||||
self.setRect(r2)
|
||||
self.setPos(self.pos() + r1.center() - r2.center())
|
||||
|
||||
def mouseDoubleClickEvent(self, event):
|
||||
references.open(self._ref)
|
||||
|
||||
def hoverEnterEvent(self, event):
|
||||
self.multiplyDiameter(2)
|
||||
|
||||
def hoverLeaveEvent(self, event):
|
||||
self.multiplyDiameter(.5)
|
||||
|
||||
|
||||
class PlotLine(QGraphicsLineItem):
|
||||
def __init__(self, x1, y1, x2, y2, parent=None):
|
||||
QGraphicsLineItem.__init__(self, x1, y1, x2, y2, parent)
|
||||
self.setAcceptHoverEvents(True)
|
||||
|
||||
def hoverEnterEvent(self, QGraphicsSceneHoverEvent):
|
||||
p = self.pen()
|
||||
p.setWidth(10)
|
||||
self.setPen(p)
|
||||
|
||||
def hoverLeaveEvent(self, QGraphicsSceneHoverEvent):
|
||||
p = self.pen()
|
||||
p.setWidth(5)
|
||||
self.setPen(p)
|
69
manuskript/ui/views/storylineView_ui.py
Normal file
69
manuskript/ui/views/storylineView_ui.py
Normal file
|
@ -0,0 +1,69 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file 'manuskript/ui/views/storylineView_ui.ui'
|
||||
#
|
||||
# Created: Sun Feb 28 09:13:29 2016
|
||||
# by: PyQt5 UI code generator 5.2.1
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
class Ui_storylineView(object):
|
||||
def setupUi(self, storylineView):
|
||||
storylineView.setObjectName("storylineView")
|
||||
storylineView.resize(1040, 130)
|
||||
self.horizontalLayout = QtWidgets.QHBoxLayout(storylineView)
|
||||
self.horizontalLayout.setSpacing(0)
|
||||
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
|
||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout()
|
||||
self.verticalLayout.setSpacing(0)
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.btnZoomIn = QtWidgets.QPushButton(storylineView)
|
||||
self.btnZoomIn.setMaximumSize(QtCore.QSize(32, 32))
|
||||
self.btnZoomIn.setFlat(True)
|
||||
self.btnZoomIn.setObjectName("btnZoomIn")
|
||||
self.verticalLayout.addWidget(self.btnZoomIn)
|
||||
self.btnZoomOut = QtWidgets.QPushButton(storylineView)
|
||||
self.btnZoomOut.setMaximumSize(QtCore.QSize(32, 32))
|
||||
self.btnZoomOut.setFlat(True)
|
||||
self.btnZoomOut.setObjectName("btnZoomOut")
|
||||
self.verticalLayout.addWidget(self.btnZoomOut)
|
||||
self.btnRefresh = QtWidgets.QPushButton(storylineView)
|
||||
self.btnRefresh.setMaximumSize(QtCore.QSize(32, 32))
|
||||
self.btnRefresh.setText("")
|
||||
icon = QtGui.QIcon.fromTheme("view-refresh")
|
||||
self.btnRefresh.setIcon(icon)
|
||||
self.btnRefresh.setFlat(True)
|
||||
self.btnRefresh.setObjectName("btnRefresh")
|
||||
self.verticalLayout.addWidget(self.btnRefresh)
|
||||
self.sldTxtSize = QtWidgets.QSlider(storylineView)
|
||||
self.sldTxtSize.setMinimum(1)
|
||||
self.sldTxtSize.setMaximum(100)
|
||||
self.sldTxtSize.setProperty("value", 20)
|
||||
self.sldTxtSize.setOrientation(QtCore.Qt.Vertical)
|
||||
self.sldTxtSize.setObjectName("sldTxtSize")
|
||||
self.verticalLayout.addWidget(self.sldTxtSize)
|
||||
self.btnSettings = QtWidgets.QPushButton(storylineView)
|
||||
self.btnSettings.setMaximumSize(QtCore.QSize(32, 32))
|
||||
self.btnSettings.setText("")
|
||||
icon = QtGui.QIcon.fromTheme("preferences-system")
|
||||
self.btnSettings.setIcon(icon)
|
||||
self.btnSettings.setFlat(True)
|
||||
self.btnSettings.setObjectName("btnSettings")
|
||||
self.verticalLayout.addWidget(self.btnSettings)
|
||||
self.horizontalLayout.addLayout(self.verticalLayout)
|
||||
self.view = QtWidgets.QGraphicsView(storylineView)
|
||||
self.view.setObjectName("view")
|
||||
self.horizontalLayout.addWidget(self.view)
|
||||
|
||||
self.retranslateUi(storylineView)
|
||||
QtCore.QMetaObject.connectSlotsByName(storylineView)
|
||||
|
||||
def retranslateUi(self, storylineView):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
storylineView.setWindowTitle(_translate("storylineView", "Form"))
|
||||
self.btnZoomIn.setText(_translate("storylineView", "+"))
|
||||
self.btnZoomOut.setText(_translate("storylineView", "-"))
|
||||
|
136
manuskript/ui/views/storylineView_ui.ui
Normal file
136
manuskript/ui/views/storylineView_ui.ui
Normal file
|
@ -0,0 +1,136 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>storylineView</class>
|
||||
<widget class="QWidget" name="storylineView">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1040</width>
|
||||
<height>130</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnZoomIn">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>+</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnZoomOut">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>-</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnRefresh">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="view-refresh">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="sldTxtSize">
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnSettings">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="preferences-system">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGraphicsView" name="view"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
Binary file not shown.
Loading…
Reference in a new issue