Story line can display characters

This commit is contained in:
Olivier Keshavjee 2016-02-29 23:25:23 +01:00
parent b3ebcaee10
commit f40a43b6ed
2 changed files with 161 additions and 43 deletions

View file

@ -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 "")
############################################################################### ###############################################################################

View file

@ -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()