mirror of
https://github.com/olivierkes/manuskript.git
synced 2024-06-03 11:34:33 +12:00
Story line can display characters
This commit is contained in:
parent
b3ebcaee10
commit
f40a43b6ed
|
@ -373,56 +373,125 @@ def infos(ref):
|
||||||
return qApp.translate("references", "Unknown reference: {}.").format(ref)
|
return qApp.translate("references", "Unknown reference: {}.").format(ref)
|
||||||
|
|
||||||
|
|
||||||
def tooltip(ref):
|
def shortInfos(ref):
|
||||||
"""Returns a tooltip in HTML for the reference ``ref``."""
|
"""Returns infos about reference ``ref``.
|
||||||
|
Returns -1 if ``ref`` is not a valid reference, and None if it is valid but unknown."""
|
||||||
match = re.fullmatch(RegEx, ref)
|
match = re.fullmatch(RegEx, ref)
|
||||||
|
|
||||||
if not match:
|
if not match:
|
||||||
return qApp.translate("references", "Not a reference: {}.").format(ref)
|
return -1
|
||||||
|
|
||||||
_type = match.group(1)
|
_type = match.group(1)
|
||||||
_ref = match.group(2)
|
_ref = match.group(2)
|
||||||
|
|
||||||
|
infos = {}
|
||||||
|
infos["ID"] = _ref
|
||||||
|
|
||||||
if _type == TextLetter:
|
if _type == TextLetter:
|
||||||
|
|
||||||
|
infos["type"] = TextLetter
|
||||||
|
|
||||||
m = mainWindow().mdlOutline
|
m = mainWindow().mdlOutline
|
||||||
idx = m.getIndexByID(_ref)
|
idx = m.getIndexByID(_ref)
|
||||||
|
|
||||||
if not idx.isValid():
|
if not idx.isValid():
|
||||||
return qApp.translate("references", "Unknown reference: {}.").format(ref)
|
return None
|
||||||
|
|
||||||
item = idx.internalPointer()
|
item = idx.internalPointer()
|
||||||
|
|
||||||
if item.isFolder():
|
if item.isFolder():
|
||||||
tt = qApp.translate("references", "Folder: <b>{}</b>").format(item.title())
|
infos["text_type"] = "folder"
|
||||||
else:
|
else:
|
||||||
tt = qApp.translate("references", "Text: <b>{}</b>").format(item.title())
|
infos["text_type"] = "text"
|
||||||
tt += "<br><i>{}</i>".format(item.path())
|
|
||||||
|
|
||||||
return tt
|
infos["title"] = item.title()
|
||||||
|
infos["path"] = item.path()
|
||||||
|
return infos
|
||||||
|
|
||||||
elif _type == PersoLetter:
|
elif _type == PersoLetter:
|
||||||
|
|
||||||
|
infos["type"] = PersoLetter
|
||||||
|
|
||||||
m = mainWindow().mdlPersos
|
m = mainWindow().mdlPersos
|
||||||
item = m.item(int(_ref), Perso.name.value)
|
item = m.item(int(_ref), Perso.name.value)
|
||||||
if item:
|
if item:
|
||||||
return qApp.translate("references", "Character: <b>{}</b>").format(item.text())
|
infos["title"] = item.text()
|
||||||
|
infos["name"] = item.text()
|
||||||
|
return infos
|
||||||
|
|
||||||
elif _type == PlotLetter:
|
elif _type == PlotLetter:
|
||||||
|
|
||||||
|
infos["type"] = PlotLetter
|
||||||
|
|
||||||
m = mainWindow().mdlPlots
|
m = mainWindow().mdlPlots
|
||||||
name = m.getPlotNameByID(_ref)
|
name = m.getPlotNameByID(_ref)
|
||||||
if name:
|
if name:
|
||||||
return qApp.translate("references", "Plot: <b>{}</b>").format(name)
|
infos["title"] = name
|
||||||
|
return infos
|
||||||
|
|
||||||
elif _type == WorldLetter:
|
elif _type == WorldLetter:
|
||||||
|
|
||||||
|
infos["type"] = WorldLetter
|
||||||
|
|
||||||
m = mainWindow().mdlWorld
|
m = mainWindow().mdlWorld
|
||||||
item = m.itemByID(_ref)
|
item = m.itemByID(_ref)
|
||||||
if item:
|
if item:
|
||||||
name = item.text()
|
name = item.text()
|
||||||
path = m.path(item)
|
path = m.path(item)
|
||||||
return qApp.translate("references", "World: <b>{name}</b>{path}").format(
|
infos["title"] = name
|
||||||
name=name,
|
infos["path"] = path
|
||||||
path=" <span style='color:gray;'>({})</span>".format(path) if path else "")
|
return infos
|
||||||
|
|
||||||
return qApp.translate("references", "<b>Unknown reference:</b> {}.").format(ref)
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def title(ref):
|
||||||
|
"""Returns a the title (or name) for the reference ``ref``."""
|
||||||
|
infos = shortInfos(ref)
|
||||||
|
if infos and infos != -1 and "title" in infos:
|
||||||
|
return infos["title"]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def type(ref):
|
||||||
|
infos = shortInfos(ref)
|
||||||
|
if infos and infos != -1:
|
||||||
|
return infos["type"]
|
||||||
|
|
||||||
|
def ID(ref):
|
||||||
|
infos = shortInfos(ref)
|
||||||
|
if infos and infos != -1:
|
||||||
|
return infos["ID"]
|
||||||
|
|
||||||
|
def tooltip(ref):
|
||||||
|
"""Returns a tooltip in HTML for the reference ``ref``."""
|
||||||
|
infos = shortInfos(ref)
|
||||||
|
|
||||||
|
if not infos:
|
||||||
|
return qApp.translate("references", "<b>Unknown reference:</b> {}.").format(ref)
|
||||||
|
|
||||||
|
if infos == -1:
|
||||||
|
return qApp.translate("references", "Not a reference: {}.").format(ref)
|
||||||
|
|
||||||
|
|
||||||
|
if infos["type"] == TextLetter:
|
||||||
|
if infos["text_type"] == "folder":
|
||||||
|
tt = qApp.translate("references", "Folder: <b>{}</b>").format(infos["title"])
|
||||||
|
else:
|
||||||
|
tt = qApp.translate("references", "Text: <b>{}</b>").format(infos["title"])
|
||||||
|
tt += "<br><i>{}</i>".format(infos["path"])
|
||||||
|
return tt
|
||||||
|
|
||||||
|
elif infos["type"] == PersoLetter:
|
||||||
|
return qApp.translate("references", "Character: <b>{}</b>").format(infos["title"])
|
||||||
|
|
||||||
|
elif infos["type"] == PlotLetter:
|
||||||
|
return qApp.translate("references", "Plot: <b>{}</b>").format(infos["title"])
|
||||||
|
|
||||||
|
elif infos["type"] == WorldLetter:
|
||||||
|
return qApp.translate("references", "World: <b>{name}</b>{path}").format(
|
||||||
|
name=infos["title"],
|
||||||
|
path=" <span style='color:gray;'>({})</span>".format(infos["path"]) if infos["path"] else "")
|
||||||
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# --!-- coding: utf8 --!--
|
# --!-- coding: utf8 --!--
|
||||||
from PyQt5.QtCore import Qt, QTimer, QRectF
|
from PyQt5.QtCore import Qt, QTimer, QRectF
|
||||||
from PyQt5.QtGui import QBrush, QPen, QFontMetrics, QFontMetricsF
|
from PyQt5.QtGui import QBrush, QPen, QFontMetrics, QFontMetricsF, QColor
|
||||||
from PyQt5.QtWidgets import QWidget, QGraphicsScene, QGraphicsSimpleTextItem, QMenu, QAction, QGraphicsRectItem, \
|
from PyQt5.QtWidgets import QWidget, QGraphicsScene, QGraphicsSimpleTextItem, QMenu, QAction, QGraphicsRectItem, \
|
||||||
QGraphicsLineItem, QGraphicsEllipseItem
|
QGraphicsLineItem, QGraphicsEllipseItem
|
||||||
|
|
||||||
|
@ -32,15 +32,17 @@ class storylineView(QWidget, Ui_storylineView):
|
||||||
def generateMenu(self):
|
def generateMenu(self):
|
||||||
m = QMenu()
|
m = QMenu()
|
||||||
|
|
||||||
for i in [
|
self.actPlots = QAction(self.tr("Show Plots"), m)
|
||||||
self.tr("Show Plots"),
|
self.actPlots.setCheckable(True)
|
||||||
self.tr("Show Characters"),
|
self.actPlots.setChecked(True)
|
||||||
self.tr("Show Objects"),
|
self.actPlots.toggled.connect(self.reloadTimer.start)
|
||||||
]:
|
m.addAction(self.actPlots)
|
||||||
a = QAction(i, m)
|
|
||||||
a.setCheckable(True)
|
self.actCharacters = QAction(self.tr("Show Characters"), m)
|
||||||
a.setEnabled(False)
|
self.actCharacters.setCheckable(True)
|
||||||
m.addAction(a)
|
self.actCharacters.setChecked(False)
|
||||||
|
self.actCharacters.toggled.connect(self.reloadTimer.start)
|
||||||
|
m.addAction(self.actCharacters)
|
||||||
|
|
||||||
self.btnSettings.setMenu(m)
|
self.btnSettings.setMenu(m)
|
||||||
|
|
||||||
|
@ -55,6 +57,33 @@ class storylineView(QWidget, Ui_storylineView):
|
||||||
self._mdlPersos = mdlPersos
|
self._mdlPersos = mdlPersos
|
||||||
self._mdlPersos.dataChanged.connect(self.reloadTimer.start)
|
self._mdlPersos.dataChanged.connect(self.reloadTimer.start)
|
||||||
|
|
||||||
|
def plotReferences(self):
|
||||||
|
"Returns a list of plot references"
|
||||||
|
if not self._mdlPlots:
|
||||||
|
pass
|
||||||
|
|
||||||
|
plotsID = self._mdlPlots.getPlotsByImportance()
|
||||||
|
r = []
|
||||||
|
for importance in plotsID:
|
||||||
|
for ID in importance:
|
||||||
|
ref = references.plotReference(ID)
|
||||||
|
r.append(ref)
|
||||||
|
|
||||||
|
return r
|
||||||
|
|
||||||
|
def persosReferences(self):
|
||||||
|
"Returns a list of character references"
|
||||||
|
if not self._mdlPersos:
|
||||||
|
pass
|
||||||
|
|
||||||
|
IDs = self._mdlPersos.getPersosByImportance()
|
||||||
|
r = []
|
||||||
|
for importance in IDs:
|
||||||
|
for ID in importance:
|
||||||
|
ref = references.persoReference(ID)
|
||||||
|
r.append(ref)
|
||||||
|
|
||||||
|
return r
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
if not self._mdlPlots or not self._mdlOutline or not self._mdlPersos:
|
if not self._mdlPlots or not self._mdlOutline or not self._mdlPersos:
|
||||||
|
@ -82,24 +111,24 @@ class storylineView(QWidget, Ui_storylineView):
|
||||||
|
|
||||||
MAX_LEVEL = maxLevel(root)
|
MAX_LEVEL = maxLevel(root)
|
||||||
|
|
||||||
# Generate left entries
|
# Get the list of tracked items (array of references)
|
||||||
# (As of now, plot only)
|
|
||||||
plotsID = self._mdlPlots.getPlotsByImportance()
|
|
||||||
trackedItems = []
|
trackedItems = []
|
||||||
fm = QFontMetrics(s.font())
|
|
||||||
max_name = 0
|
|
||||||
|
|
||||||
for importance in plotsID:
|
if self.actPlots.isChecked():
|
||||||
for ID in importance:
|
trackedItems += self.plotReferences()
|
||||||
name = self._mdlPlots.getPlotNameByID(ID)
|
|
||||||
ref = references.plotReference(ID, searchable=True)
|
|
||||||
|
|
||||||
trackedItems.append((ID, ref, name))
|
if self.actCharacters.isChecked():
|
||||||
max_name = max(fm.width(name), max_name)
|
trackedItems += self.persosReferences()
|
||||||
|
|
||||||
ROWS_HEIGHT = len(trackedItems) * (LINE_HEIGHT + SPACING )
|
ROWS_HEIGHT = len(trackedItems) * (LINE_HEIGHT + SPACING )
|
||||||
TITLE_WIDTH = max_name + 2 * SPACING
|
|
||||||
|
|
||||||
|
fm = QFontMetrics(s.font())
|
||||||
|
max_name = 0
|
||||||
|
for ref in trackedItems:
|
||||||
|
name = references.title(ref)
|
||||||
|
max_name = max(fm.width(name), max_name)
|
||||||
|
|
||||||
|
TITLE_WIDTH = max_name + 2 * SPACING
|
||||||
|
|
||||||
# Add Folders and Texts
|
# Add Folders and Texts
|
||||||
outline = OutlineRect(0, 0, 0, ROWS_HEIGHT + SPACING + MAX_LEVEL * LEVEL_HEIGHT)
|
outline = OutlineRect(0, 0, 0, ROWS_HEIGHT + SPACING + MAX_LEVEL * LEVEL_HEIGHT)
|
||||||
|
@ -150,9 +179,22 @@ class storylineView(QWidget, Ui_storylineView):
|
||||||
rectChild.setToolTip(references.tooltip(references.textReference(child.ID())))
|
rectChild.setToolTip(references.tooltip(references.textReference(child.ID())))
|
||||||
|
|
||||||
# Find tracked references in that scene (or parent folders)
|
# Find tracked references in that scene (or parent folders)
|
||||||
for ID, ref, name in trackedItems:
|
for ref in trackedItems:
|
||||||
|
|
||||||
result = []
|
result = []
|
||||||
|
|
||||||
|
# Tests if POV
|
||||||
|
scenePOV = False # Will hold true of character is POV of the current text, not containing folder
|
||||||
|
if references.type(ref) == references.PersoLetter:
|
||||||
|
ID = references.ID(ref)
|
||||||
|
c = child
|
||||||
|
while c:
|
||||||
|
if c.POV() == ID:
|
||||||
|
result.append(c.ID())
|
||||||
|
if c == child: scenePOV = True
|
||||||
|
c = c.parent()
|
||||||
|
|
||||||
|
# Search in notes/references
|
||||||
c = child
|
c = child
|
||||||
while c:
|
while c:
|
||||||
result += references.findReferencesTo(ref, c, recursive=False)
|
result += references.findReferencesTo(ref, c, recursive=False)
|
||||||
|
@ -162,7 +204,7 @@ class storylineView(QWidget, Ui_storylineView):
|
||||||
ref2 = result[0]
|
ref2 = result[0]
|
||||||
|
|
||||||
# Create a RefCircle with the reference
|
# Create a RefCircle with the reference
|
||||||
c = RefCircle(TEXT_WIDTH / 2, - CIRCLE_WIDTH / 2, CIRCLE_WIDTH, ID=ref2)
|
c = RefCircle(TEXT_WIDTH / 2, - CIRCLE_WIDTH / 2, CIRCLE_WIDTH, ID=ref2, important=scenePOV)
|
||||||
|
|
||||||
# Store it, with the position of that item, to display it on the line later on
|
# 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())))
|
refCircles.append((ref, c, rect.mapToItem(outline, rectChild.pos())))
|
||||||
|
@ -173,22 +215,27 @@ class storylineView(QWidget, Ui_storylineView):
|
||||||
|
|
||||||
OUTLINE_WIDTH = itemWidth(root)
|
OUTLINE_WIDTH = itemWidth(root)
|
||||||
|
|
||||||
# Add Plots
|
# Add Tracked items
|
||||||
i = 0
|
i = 0
|
||||||
itemsRect = s.addRect(0, 0, 0, 0)
|
itemsRect = s.addRect(0, 0, 0, 0)
|
||||||
itemsRect.setPos(0, MAX_LEVEL * LEVEL_HEIGHT + SPACING)
|
itemsRect.setPos(0, MAX_LEVEL * LEVEL_HEIGHT + SPACING)
|
||||||
|
|
||||||
for ID, ref, name in trackedItems:
|
for ref in trackedItems:
|
||||||
color = randomColor()
|
if references.type(ref) == references.PersoLetter:
|
||||||
|
color = QColor(self._mdlPersos.getPersoColorByID(references.ID(ref)))
|
||||||
|
else:
|
||||||
|
color = randomColor()
|
||||||
|
|
||||||
# Rect
|
# Rect
|
||||||
r = QGraphicsRectItem(0, 0, TITLE_WIDTH, LINE_HEIGHT, itemsRect)
|
r = QGraphicsRectItem(0, 0, TITLE_WIDTH, LINE_HEIGHT, itemsRect)
|
||||||
r.setPen(QPen(Qt.NoPen))
|
r.setPen(QPen(Qt.NoPen))
|
||||||
r.setBrush(QBrush(color))
|
r.setBrush(QBrush(color))
|
||||||
r.setPos(0, i * LINE_HEIGHT + i * SPACING)
|
r.setPos(0, i * LINE_HEIGHT + i * SPACING)
|
||||||
|
r.setToolTip(references.tooltip(ref))
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
# Text
|
# Text
|
||||||
|
name = references.title(ref)
|
||||||
txt = QGraphicsSimpleTextItem(name, r)
|
txt = QGraphicsSimpleTextItem(name, r)
|
||||||
txt.setPos(r.boundingRect().center() - txt.boundingRect().center())
|
txt.setPos(r.boundingRect().center() - txt.boundingRect().center())
|
||||||
|
|
||||||
|
@ -198,7 +245,7 @@ class storylineView(QWidget, Ui_storylineView):
|
||||||
line.setPos(TITLE_WIDTH, r.mapToScene(r.rect().center()).y())
|
line.setPos(TITLE_WIDTH, r.mapToScene(r.rect().center()).y())
|
||||||
s.addItem(line)
|
s.addItem(line)
|
||||||
line.setPen(QPen(color, 5))
|
line.setPen(QPen(color, 5))
|
||||||
line.setToolTip(self.tr("Plot: ") + name)
|
line.setToolTip(references.tooltip(ref))
|
||||||
|
|
||||||
# We add the circles / references to text, on the line
|
# We add the circles / references to text, on the line
|
||||||
for ref2, circle, pos in refCircles:
|
for ref2, circle, pos in refCircles:
|
||||||
|
@ -225,13 +272,15 @@ class OutlineRect(QGraphicsRectItem):
|
||||||
|
|
||||||
|
|
||||||
class RefCircle(QGraphicsEllipseItem):
|
class RefCircle(QGraphicsEllipseItem):
|
||||||
def __init__(self, x, y, diameter, parent=None, ID=None):
|
def __init__(self, x, y, diameter, parent=None, ID=None, important=False):
|
||||||
QGraphicsEllipseItem.__init__(self, x, y, diameter, diameter, parent)
|
QGraphicsEllipseItem.__init__(self, x, y, diameter, diameter, parent)
|
||||||
self.setBrush(Qt.white)
|
self.setBrush(Qt.white)
|
||||||
self._ref = references.textReference(ID)
|
self._ref = references.textReference(ID)
|
||||||
self.setToolTip(references.tooltip(self._ref))
|
self.setToolTip(references.tooltip(self._ref))
|
||||||
self.setPen(QPen(Qt.black, 2))
|
self.setPen(QPen(Qt.black, 2))
|
||||||
self.setAcceptHoverEvents(True)
|
self.setAcceptHoverEvents(True)
|
||||||
|
if important:
|
||||||
|
self.setBrush(Qt.black)
|
||||||
|
|
||||||
def multiplyDiameter(self, factor):
|
def multiplyDiameter(self, factor):
|
||||||
r1 = self.rect()
|
r1 = self.rect()
|
||||||
|
|
Loading…
Reference in a new issue