2016-03-04 04:38:38 +13:00
|
|
|
|
#!/usr/bin/env python
|
|
|
|
|
# --!-- coding: utf8 --!--
|
|
|
|
|
from PyQt5.QtCore import QModelIndex, Qt, QAbstractItemModel, QVariant
|
|
|
|
|
from PyQt5.QtGui import QIcon, QPixmap, QColor
|
|
|
|
|
|
2019-12-22 04:42:49 +13:00
|
|
|
|
from manuskript.functions import randomColor, iconColor, mainWindow, search
|
|
|
|
|
from manuskript.enums import Character as C, Model
|
|
|
|
|
from manuskript.searchLabels import CharacterSearchLabels
|
2016-03-04 04:38:38 +13:00
|
|
|
|
|
2019-12-22 04:42:49 +13:00
|
|
|
|
from manuskript.models.searchableModel import searchableModel
|
|
|
|
|
from manuskript.models.searchableItem import searchableItem
|
2016-03-04 04:38:38 +13:00
|
|
|
|
|
2019-12-22 04:42:49 +13:00
|
|
|
|
class characterModel(QAbstractItemModel, searchableModel):
|
2016-03-04 04:38:38 +13:00
|
|
|
|
|
|
|
|
|
def __init__(self, parent):
|
|
|
|
|
QAbstractItemModel.__init__(self, parent)
|
|
|
|
|
|
2016-03-10 02:10:22 +13:00
|
|
|
|
# CharacterItems are stored in this list
|
2016-03-04 04:38:38 +13:00
|
|
|
|
self.characters = []
|
|
|
|
|
|
|
|
|
|
###############################################################################
|
|
|
|
|
# QAbstractItemModel subclassed
|
|
|
|
|
###############################################################################
|
|
|
|
|
|
|
|
|
|
def rowCount(self, parent=QModelIndex()):
|
|
|
|
|
if parent.isValid():
|
|
|
|
|
c = parent.internalPointer()
|
|
|
|
|
return len(c.infos)
|
|
|
|
|
else:
|
|
|
|
|
return len(self.characters)
|
|
|
|
|
|
|
|
|
|
def columnCount(self, parent=QModelIndex()):
|
|
|
|
|
if parent.isValid():
|
|
|
|
|
# Returns characters infos
|
|
|
|
|
return 2
|
|
|
|
|
else:
|
2016-03-06 21:21:10 +13:00
|
|
|
|
return len(C)
|
2016-03-04 04:38:38 +13:00
|
|
|
|
|
|
|
|
|
def data(self, index, role=Qt.DisplayRole):
|
|
|
|
|
c = index.internalPointer()
|
|
|
|
|
if type(c) == Character:
|
|
|
|
|
if role == Qt.DisplayRole:
|
|
|
|
|
if index.column() in c._data:
|
|
|
|
|
return c._data[index.column()]
|
|
|
|
|
else:
|
2016-03-04 05:18:30 +13:00
|
|
|
|
return ""
|
2016-03-04 04:38:38 +13:00
|
|
|
|
|
2016-03-24 23:37:18 +13:00
|
|
|
|
elif role == Qt.DecorationRole:
|
|
|
|
|
if index.column() == C.name.value:
|
|
|
|
|
return c.icon
|
|
|
|
|
else:
|
|
|
|
|
return QVariant()
|
|
|
|
|
|
2016-03-04 04:38:38 +13:00
|
|
|
|
elif type(c) == CharacterInfo:
|
|
|
|
|
if role == Qt.DisplayRole or role == Qt.EditRole:
|
|
|
|
|
if index.column() == 0:
|
|
|
|
|
return c.description
|
|
|
|
|
elif index.column() == 1:
|
|
|
|
|
return c.value
|
|
|
|
|
|
|
|
|
|
def setData(self, index, value, role=Qt.EditRole):
|
|
|
|
|
c = index.internalPointer()
|
|
|
|
|
if type(c) == Character:
|
|
|
|
|
if role == Qt.EditRole:
|
|
|
|
|
# We update only if data is different
|
|
|
|
|
if index.column() not in c._data or c._data[index.column()] != value:
|
|
|
|
|
c._data[index.column()] = value
|
|
|
|
|
self.dataChanged.emit(index, index)
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
elif type(c) == CharacterInfo:
|
|
|
|
|
if role == Qt.EditRole:
|
|
|
|
|
if index.column() == 0:
|
|
|
|
|
c.description = value
|
|
|
|
|
elif index.column() == 1:
|
|
|
|
|
c.value = value
|
|
|
|
|
self.dataChanged.emit(index, index)
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def index(self, row, column, parent=QModelIndex()):
|
|
|
|
|
if not parent.isValid():
|
|
|
|
|
return self.createIndex(row, column, self.characters[row])
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
c = parent.internalPointer()
|
|
|
|
|
if row < len(c.infos):
|
|
|
|
|
return self.createIndex(row, column, c.infos[row])
|
|
|
|
|
else:
|
|
|
|
|
return QModelIndex()
|
|
|
|
|
|
|
|
|
|
def indexFromItem(self, item, column=0):
|
|
|
|
|
if not item:
|
|
|
|
|
return QModelIndex()
|
|
|
|
|
|
|
|
|
|
row = self.characters.index(item)
|
|
|
|
|
col = column
|
|
|
|
|
return self.createIndex(row, col, item)
|
|
|
|
|
|
|
|
|
|
def parent(self, index):
|
|
|
|
|
if not index.isValid():
|
|
|
|
|
return QModelIndex()
|
|
|
|
|
|
|
|
|
|
child = index.internalPointer()
|
|
|
|
|
|
|
|
|
|
if type(child) == Character:
|
|
|
|
|
return QModelIndex()
|
|
|
|
|
|
|
|
|
|
elif type(child) == CharacterInfo:
|
|
|
|
|
return child.character.index()
|
|
|
|
|
|
|
|
|
|
def flags(self, index):
|
|
|
|
|
if index.parent().isValid():
|
|
|
|
|
return QAbstractItemModel.flags(self, index) | Qt.ItemIsEditable
|
|
|
|
|
else:
|
|
|
|
|
return QAbstractItemModel.flags(self, index)
|
|
|
|
|
|
|
|
|
|
###############################################################################
|
2018-01-07 06:48:40 +13:00
|
|
|
|
# CHARACTER QUERIES
|
2016-03-04 04:38:38 +13:00
|
|
|
|
###############################################################################
|
|
|
|
|
|
|
|
|
|
def character(self, row):
|
|
|
|
|
return self.characters[row]
|
|
|
|
|
|
|
|
|
|
def name(self, row):
|
|
|
|
|
return self.character(row).name()
|
|
|
|
|
|
|
|
|
|
def icon(self, row):
|
|
|
|
|
return self.character(row).icon
|
|
|
|
|
|
|
|
|
|
def ID(self, row):
|
|
|
|
|
return self.character(row).ID()
|
|
|
|
|
|
|
|
|
|
def importance(self, row):
|
|
|
|
|
return self.character(row).importance()
|
|
|
|
|
|
2020-03-31 16:55:30 +13:00
|
|
|
|
def pov(self, row):
|
|
|
|
|
return self.character(row).pov()
|
|
|
|
|
|
2016-03-04 04:38:38 +13:00
|
|
|
|
###############################################################################
|
2018-01-07 06:48:40 +13:00
|
|
|
|
# MODEL QUERIES
|
2016-03-04 04:38:38 +13:00
|
|
|
|
###############################################################################
|
|
|
|
|
|
|
|
|
|
def getCharactersByImportance(self):
|
|
|
|
|
"""
|
|
|
|
|
Lists characters by importance.
|
|
|
|
|
|
|
|
|
|
@return: array of array of ´character´, by importance.
|
|
|
|
|
"""
|
|
|
|
|
r = [[], [], []]
|
2020-03-31 16:55:30 +13:00
|
|
|
|
|
2016-03-04 04:38:38 +13:00
|
|
|
|
for c in self.characters:
|
|
|
|
|
r[2-int(c.importance())].append(c)
|
2020-03-31 16:55:30 +13:00
|
|
|
|
|
2016-03-04 04:38:38 +13:00
|
|
|
|
return r
|
|
|
|
|
|
|
|
|
|
def getCharacterByID(self, ID):
|
2021-02-22 11:45:34 +13:00
|
|
|
|
if ID != None:
|
2016-03-04 05:41:19 +13:00
|
|
|
|
ID = str(ID)
|
|
|
|
|
for c in self.characters:
|
|
|
|
|
if c.ID() == ID:
|
|
|
|
|
return c
|
2020-03-31 16:55:30 +13:00
|
|
|
|
|
2016-03-04 04:38:38 +13:00
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
###############################################################################
|
|
|
|
|
# ADDING / REMOVING
|
|
|
|
|
###############################################################################
|
|
|
|
|
|
2021-02-22 13:58:28 +13:00
|
|
|
|
def addCharacter(self, importance = 0, name="New character"):
|
2016-03-04 04:38:38 +13:00
|
|
|
|
"""
|
|
|
|
|
Creates a new character
|
2020-06-01 08:33:14 +12:00
|
|
|
|
@param importance: the importance level of the character
|
2016-03-10 23:45:40 +13:00
|
|
|
|
@return: the character
|
2016-03-04 04:38:38 +13:00
|
|
|
|
"""
|
2021-02-22 13:58:28 +13:00
|
|
|
|
if not name:
|
|
|
|
|
name="New Character"
|
|
|
|
|
c = Character(model=self, name=self.tr(name), importance = importance)
|
|
|
|
|
self.beginInsertRows(QModelIndex(), len(
|
|
|
|
|
self.characters), len(self.characters))
|
2016-03-04 04:38:38 +13:00
|
|
|
|
self.characters.append(c)
|
|
|
|
|
self.endInsertRows()
|
2016-03-10 23:45:40 +13:00
|
|
|
|
return c
|
2016-03-04 04:38:38 +13:00
|
|
|
|
|
|
|
|
|
def removeCharacter(self, ID):
|
|
|
|
|
"""
|
|
|
|
|
Removes character whose ID is ID...
|
|
|
|
|
@param ID: the ID of the character to remove
|
|
|
|
|
@return: nothing
|
|
|
|
|
"""
|
|
|
|
|
c = self.getCharacterByID(ID)
|
2021-02-22 13:58:28 +13:00
|
|
|
|
self.beginRemoveRows(QModelIndex(), self.characters.index(
|
|
|
|
|
c), self.characters.index(c))
|
2016-03-04 04:38:38 +13:00
|
|
|
|
self.characters.remove(c)
|
2016-03-10 02:10:22 +13:00
|
|
|
|
self.endRemoveRows()
|
2016-03-04 04:38:38 +13:00
|
|
|
|
|
|
|
|
|
###############################################################################
|
|
|
|
|
# CHARACTER INFOS
|
|
|
|
|
###############################################################################
|
|
|
|
|
|
|
|
|
|
def headerData(self, section, orientation, role=Qt.DisplayRole):
|
|
|
|
|
if role == Qt.DisplayRole and orientation == Qt.Horizontal:
|
|
|
|
|
if section == 0:
|
|
|
|
|
return self.tr("Name")
|
|
|
|
|
elif section == 1:
|
|
|
|
|
return self.tr("Value")
|
|
|
|
|
else:
|
|
|
|
|
return C(section).name
|
|
|
|
|
|
|
|
|
|
def addCharacterInfo(self, ID):
|
|
|
|
|
c = self.getCharacterByID(ID)
|
|
|
|
|
self.beginInsertRows(c.index(), len(c.infos), len(c.infos))
|
2021-02-22 13:58:28 +13:00
|
|
|
|
c.infos.append(CharacterInfo(
|
|
|
|
|
c, description="Description", value="Value"))
|
2016-03-04 04:38:38 +13:00
|
|
|
|
self.endInsertRows()
|
|
|
|
|
|
|
|
|
|
mainWindow().updatePersoInfoView()
|
|
|
|
|
|
|
|
|
|
def removeCharacterInfo(self, ID):
|
|
|
|
|
c = self.getCharacterByID(ID)
|
|
|
|
|
|
|
|
|
|
rm = []
|
|
|
|
|
for idx in mainWindow().tblPersoInfos.selectedIndexes():
|
|
|
|
|
if not idx.row() in rm:
|
|
|
|
|
rm.append(idx.row())
|
|
|
|
|
|
|
|
|
|
rm.sort()
|
|
|
|
|
rm.reverse()
|
|
|
|
|
for r in rm:
|
|
|
|
|
self.beginRemoveRows(c.index(), r, r)
|
|
|
|
|
c.infos.pop(r)
|
|
|
|
|
self.endRemoveRows()
|
|
|
|
|
|
2019-12-22 04:42:49 +13:00
|
|
|
|
def searchableItems(self):
|
|
|
|
|
return self.characters
|
|
|
|
|
|
2016-03-04 04:38:38 +13:00
|
|
|
|
###############################################################################
|
|
|
|
|
# CHARACTER
|
|
|
|
|
###############################################################################
|
|
|
|
|
|
2019-12-22 04:42:49 +13:00
|
|
|
|
class Character(searchableItem):
|
2020-06-01 08:33:14 +12:00
|
|
|
|
def __init__(self, model, name="No name", importance = 0):
|
2016-03-04 04:38:38 +13:00
|
|
|
|
self._model = model
|
2016-03-06 05:46:02 +13:00
|
|
|
|
self.lastPath = ""
|
2016-03-04 04:38:38 +13:00
|
|
|
|
|
|
|
|
|
self._data = {}
|
|
|
|
|
self._data[C.name.value] = name
|
|
|
|
|
self.assignUniqueID()
|
|
|
|
|
self.assignRandomColor()
|
2020-06-01 08:33:14 +12:00
|
|
|
|
self._data[C.importance.value] = str(importance)
|
2020-03-31 16:55:30 +13:00
|
|
|
|
self._data[C.pov.value] = "True"
|
2016-03-04 04:38:38 +13:00
|
|
|
|
|
|
|
|
|
self.infos = []
|
|
|
|
|
|
2019-12-22 04:42:49 +13:00
|
|
|
|
super().__init__(CharacterSearchLabels)
|
|
|
|
|
|
2016-03-04 04:38:38 +13:00
|
|
|
|
def name(self):
|
|
|
|
|
return self._data[C.name.value]
|
|
|
|
|
|
2021-02-22 13:58:28 +13:00
|
|
|
|
def setName(self, value):
|
|
|
|
|
self._data[C.name.value] = value
|
|
|
|
|
|
2016-03-04 04:38:38 +13:00
|
|
|
|
def importance(self):
|
|
|
|
|
return self._data[C.importance.value]
|
|
|
|
|
|
|
|
|
|
def ID(self):
|
|
|
|
|
return self._data[C.ID.value]
|
|
|
|
|
|
|
|
|
|
def index(self, column=0):
|
|
|
|
|
return self._model.indexFromItem(self, column)
|
|
|
|
|
|
2019-12-22 04:42:49 +13:00
|
|
|
|
def data(self, column):
|
|
|
|
|
if column == "Info":
|
|
|
|
|
return self.infos
|
|
|
|
|
else:
|
|
|
|
|
return self._data.get(column, None)
|
|
|
|
|
|
2016-03-04 04:38:38 +13:00
|
|
|
|
def assignRandomColor(self):
|
|
|
|
|
"""
|
|
|
|
|
Assigns a random color the the character.
|
|
|
|
|
"""
|
|
|
|
|
color = randomColor(QColor(Qt.white))
|
|
|
|
|
self.setColor(color)
|
|
|
|
|
|
|
|
|
|
def setColor(self, color):
|
|
|
|
|
"""
|
|
|
|
|
Sets the character's color
|
|
|
|
|
@param color: QColor.
|
|
|
|
|
"""
|
|
|
|
|
px = QPixmap(32, 32)
|
|
|
|
|
px.fill(color)
|
|
|
|
|
self.icon = QIcon(px)
|
|
|
|
|
try:
|
|
|
|
|
self._model.dataChanged.emit(self.index(), self.index())
|
|
|
|
|
except:
|
|
|
|
|
# If it is the initialisation, won't be able to emit
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def color(self):
|
|
|
|
|
"""
|
|
|
|
|
Returns character's color in QColor
|
|
|
|
|
@return: QColor
|
|
|
|
|
"""
|
|
|
|
|
return iconColor(self.icon)
|
|
|
|
|
|
2020-03-31 16:55:30 +13:00
|
|
|
|
def setPOVEnabled(self, enabled):
|
|
|
|
|
if enabled != self.pov():
|
|
|
|
|
if enabled:
|
|
|
|
|
self._data[C.pov.value] = 'True'
|
|
|
|
|
else:
|
|
|
|
|
self._data[C.pov.value] = 'False'
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
self._model.dataChanged.emit(self.index(), self.index())
|
|
|
|
|
except:
|
|
|
|
|
# If it is the initialisation, won't be able to emit
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def pov(self):
|
|
|
|
|
return self._data[C.pov.value] == 'True'
|
|
|
|
|
|
2016-03-04 04:38:38 +13:00
|
|
|
|
def assignUniqueID(self, parent=QModelIndex()):
|
|
|
|
|
"""Assigns an unused character ID."""
|
|
|
|
|
vals = []
|
|
|
|
|
for c in self._model.characters:
|
2016-03-04 05:41:19 +13:00
|
|
|
|
vals.append(int(c.ID()))
|
2016-03-04 04:38:38 +13:00
|
|
|
|
|
|
|
|
|
k = 0
|
|
|
|
|
while k in vals:
|
|
|
|
|
k += 1
|
|
|
|
|
|
2016-03-04 05:41:19 +13:00
|
|
|
|
self._data[C.ID.value] = str(k)
|
2016-03-04 04:38:38 +13:00
|
|
|
|
|
2016-03-04 05:18:30 +13:00
|
|
|
|
def listInfos(self):
|
|
|
|
|
r = []
|
|
|
|
|
for i in self.infos:
|
|
|
|
|
r.append((i.description, i.value))
|
|
|
|
|
return r
|
|
|
|
|
|
2019-12-22 04:42:49 +13:00
|
|
|
|
def searchTitle(self, column):
|
|
|
|
|
return self.name()
|
|
|
|
|
|
|
|
|
|
def searchOccurrences(self, searchRegex, column):
|
|
|
|
|
results = []
|
|
|
|
|
|
|
|
|
|
data = self.searchData(column)
|
|
|
|
|
if isinstance(data, list):
|
|
|
|
|
for i in range(0, len(data)):
|
|
|
|
|
# For detailed info we will highlight the full row, so we pass the row index
|
|
|
|
|
# to the highlighter instead of the (startPos, endPos) of the match itself.
|
|
|
|
|
results += [self.wrapSearchOccurrence(column, i, 0, context) for
|
|
|
|
|
(startPos, endPos, context) in search(searchRegex, data[i].description)]
|
|
|
|
|
results += [self.wrapSearchOccurrence(column, i, 0, context) for
|
|
|
|
|
(startPos, endPos, context) in search(searchRegex, data[i].value)]
|
|
|
|
|
else:
|
|
|
|
|
results += super().searchOccurrences(searchRegex, column)
|
|
|
|
|
|
|
|
|
|
return results
|
|
|
|
|
|
|
|
|
|
def searchID(self):
|
|
|
|
|
return self.ID()
|
|
|
|
|
|
|
|
|
|
def searchPath(self, column):
|
|
|
|
|
return [self.translate("Characters"), self.name(), self.translate(self.searchColumnLabel(column))]
|
|
|
|
|
|
|
|
|
|
def searchData(self, column):
|
|
|
|
|
if column == C.infos:
|
|
|
|
|
return self.infos
|
|
|
|
|
else:
|
|
|
|
|
return self.data(column)
|
|
|
|
|
|
|
|
|
|
def searchModel(self):
|
|
|
|
|
return Model.Character
|
|
|
|
|
|
2021-02-22 13:58:28 +13:00
|
|
|
|
|
2016-03-04 04:38:38 +13:00
|
|
|
|
class CharacterInfo():
|
|
|
|
|
def __init__(self, character, description="", value=""):
|
|
|
|
|
self.description = description
|
|
|
|
|
self.value = value
|
2017-11-21 03:42:30 +13:00
|
|
|
|
self.character = character
|