Added logic for character selection in its view

Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
This commit is contained in:
TheJackiMonster 2021-05-17 23:59:12 +02:00
parent ef119917cf
commit 9dc2544d1c
No known key found for this signature in database
GPG key ID: D850A5F772E880F9
11 changed files with 302 additions and 31 deletions

View file

@ -4,6 +4,7 @@
from manuskript.data.characters import Characters, Character
from manuskript.data.color import Color
from manuskript.data.goal import GoalKind, Goal
from manuskript.data.importance import Importance
from manuskript.data.info import Info
from manuskript.data.labels import LabelHost, Label
from manuskript.data.outline import Outline, OutlineFolder, OutlineText

View file

@ -5,6 +5,7 @@ import os
from collections import OrderedDict
from manuskript.data.color import Color
from manuskript.data.importance import Importance
from manuskript.data.unique_id import UniqueIDHost
from manuskript.io.mmdFile import MmdFile
@ -48,9 +49,11 @@ class Character:
if ID is None:
raise IOError("Character is missing ID!")
importance = Character.loadAttribute(metadata, "Importance", None)
self.UID = self.characters.host.loadID(int(ID))
self.name = Character.loadAttribute(metadata, "Name", None)
self.importance = Character.loadAttribute(metadata, "Importance", None)
self.importance = Importance.fromRawString(importance)
self.POV = Character.loadAttribute(metadata, "POV", None)
self.motivation = Character.loadAttribute(metadata, "Motivation", None)
self.goal = Character.loadAttribute(metadata, "Goal", None)
@ -72,7 +75,7 @@ class Character:
metadata["Name"] = self.name
metadata["ID"] = str(self.UID.value)
metadata["Importance"] = self.importance
metadata["Importance"] = Importance.toRawString(self.importance)
metadata["POV"] = self.POV
metadata["Motivation"] = self.motivation
metadata["Goal"] = self.goal
@ -96,10 +99,16 @@ class Characters:
def __init__(self, path):
self.dir_path = os.path.join(path, "characters")
self.host = UniqueIDHost()
self.characters = list()
self.data = dict()
def __iter__(self):
return self.data.values().__iter__()
def getByID(self, ID: int) -> Character:
return self.data.get(ID, None)
def load(self):
self.characters.clear()
self.data.clear()
for name in os.listdir(self.dir_path):
path = os.path.join(self.dir_path, name)
@ -114,8 +123,8 @@ class Characters:
except FileNotFoundError:
continue
self.characters.append(character)
self.data[character.UID.value] = character
def save(self):
for character in self.characters:
for character in self.data.values():
character.save()

View file

@ -11,6 +11,9 @@ class Color:
self.green = green
self.blue = blue
def getRGB(self):
return (self.red << 24) | (self.green << 16) | (self.blue << 8)
def __str__(self):
return "#%02x%02x%02x" % (self.red, self.green, self.blue)

View file

@ -0,0 +1,29 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
from enum import Enum, unique
@unique
class Importance(Enum):
MINOR = 0
SECONDARY = 1
MAIN = 2
@classmethod
def asValue(cls, importance):
return 0 if importance is None else importance.value
@classmethod
def fromRawString(cls, raw: str):
if raw is None:
return None
try:
return Importance(int(raw))
except ValueError:
return None
@classmethod
def toRawString(cls, importance):
return None if importance is None else str(importance.value)

View file

@ -5,17 +5,11 @@ import os
from lxml import etree
from enum import Enum, unique
from manuskript.data.importance import Importance
from manuskript.data.unique_id import UniqueIDHost, UniqueID
from manuskript.io.xmlFile import XmlFile
@unique
class PlotImportance(Enum):
MINOR = 0
SECONDARY = 1
MAIN = 2
class PlotStep:
def __init__(self, plot, UID: UniqueID, name: str, meta: str = "", summary: str = ""):
@ -32,7 +26,7 @@ class PlotStep:
class PlotLine:
def __init__(self, plots, UID: UniqueID, name: str, importance: PlotImportance = PlotImportance.MINOR):
def __init__(self, plots, UID: UniqueID, name: str, importance: Importance = Importance.MINOR):
self.plots = plots
self.host = UniqueIDHost()
@ -72,12 +66,12 @@ class Plots:
self.host = UniqueIDHost()
self.lines = dict()
def addLine(self, name: str, importance: PlotImportance = PlotImportance.MINOR):
def addLine(self, name: str, importance: Importance = Importance.MINOR):
line = PlotLine(self, self.host.newID(), name, importance)
self.lines[line.UID.value] = line
return line
def loadLine(self, ID: int, name: str, importance: PlotImportance = PlotImportance.MINOR):
def loadLine(self, ID: int, name: str, importance: Importance = Importance.MINOR):
line = PlotLine(self, self.host.loadID(ID), name, importance)
self.lines[line.UID.value] = line
return line
@ -110,7 +104,7 @@ class Plots:
if ID is None:
return
importance = PlotImportance(int(element.get("importance", 0)))
importance = Importance.fromRawString(element.get("importance", None))
line = plots.loadLine(int(ID), element.get("name"), importance)
line.description = element.get("description")
line.result = element.get("result")
@ -169,7 +163,7 @@ class Plots:
cls.saveElementAttribute(element, "name", line.name)
cls.saveElementAttribute(element, "ID", line.UID.value)
cls.saveElementAttribute(element, "importance", line.importance.value)
cls.saveElementAttribute(element, "importance", Importance.toRawString(line.importance))
cls.saveElementAttribute(element, "characters", characters)
cls.saveElementAttribute(element, "description", line.description)
cls.saveElementAttribute(element, "result", line.result)

View file

@ -1,6 +1,8 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from manuskript.ui.util import pixbufFromColor
from manuskript.ui.generalView import GeneralView
from manuskript.ui.summaryView import SummaryView
from manuskript.ui.charactersView import CharactersView
@ -10,3 +12,6 @@ from manuskript.ui.outlineView import OutlineView
from manuskript.ui.editorView import EditorView
from manuskript.ui.mainWindow import MainWindow

View file

@ -6,20 +6,61 @@ import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
from manuskript.data import Characters, Character, Importance, Color
from manuskript.ui.util import rgbaFromColor, pixbufFromColor
from manuskript.util import validString, invalidString, validInt, invalidInt
class CharactersView:
def __init__(self):
def __init__(self, characters: Characters):
self.characters = characters
self.character = None
builder = Gtk.Builder()
builder.add_from_file("ui/characters.glade")
self.widget = builder.get_object("characters_view")
self.charactersStore = builder.get_object("characters_store")
for character in self.characters:
tree_iter = self.charactersStore.append()
if tree_iter is None:
continue
self.charactersStore.set_value(tree_iter, 0, character.UID.value)
self.charactersStore.set_value(tree_iter, 1, validString(character.name))
self.charactersStore.set_value(tree_iter, 2, pixbufFromColor(character.color))
self.charactersStore.set_value(tree_iter, 3, Importance.asValue(character.importance))
self.mainCharactersStore = builder.get_object("main_characters_store")
self.secondaryCharactersStore = builder.get_object("secondary_characters_store")
self.minorCharactersStore = builder.get_object("minor_characters_store")
self.mainCharactersStore.set_visible_func(lambda model, iter, userdata: model[iter][3] == 2)
self.secondaryCharactersStore.set_visible_func(lambda model, iter, userdata: model[iter][3] == 1)
self.minorCharactersStore.set_visible_func(lambda model, iter, userdata: model[iter][3] == 0)
self.mainCharactersStore.refilter()
self.secondaryCharactersStore.refilter()
self.minorCharactersStore.refilter()
self.characterSelections = [
builder.get_object("minor_character_selection"),
builder.get_object("secondary_character_selection"),
builder.get_object("main_character_selection")
]
for selection in self.characterSelections:
selection.connect("changed", self.characterSelectionChanged)
self.colorButton = builder.get_object("color")
self.importanceCombo = builder.get_object("importance")
self.allowPOVCheck = builder.get_object("allow_POV")
self.colorButton.connect("color-set", self.colorSet)
self.colorSetSignal = self.colorButton.connect("color-set", self.colorSet)
self.importanceCombo.connect("changed", self.importanceChanged)
self.allowPOVCheck.connect("toggled", self.allowPOVToggled)
@ -45,10 +86,71 @@ class CharactersView:
self.summaryBuffer = builder.get_object("summary")
self.notesBuffer = builder.get_object("notes")
self.unloadCharacterData()
def loadCharacterData(self, character: Character):
self.character = None
self.colorButton.set_rgba(rgbaFromColor(character.color))
self.allowPOVCheck.set_active(character.allowPOV())
self.nameBuffer.set_text(validString(character.name), -1)
self.motivationBuffer.set_text(validString(character.motivation), -1)
self.goalBuffer.set_text(validString(character.goal), -1)
self.conflictBuffer.set_text(validString(character.conflict), -1)
self.epiphanyBuffer.set_text(validString(character.epiphany), -1)
self.oneSentenceBuffer.set_text(validString(character.summarySentence), -1)
self.oneParagraphBuffer.set_text(validString(character.summaryParagraph), -1)
self.summaryBuffer.set_text(validString(character.summaryFull), -1)
self.notesBuffer.set_text(validString(character.notes), -1)
self.character = character
def unloadCharacterData(self):
self.character = None
self.colorButton.set_rgba(rgbaFromColor(Color(0, 0, 0)))
self.allowPOVCheck.set_active(False)
self.nameBuffer.set_text("", -1)
self.motivationBuffer.set_text("", -1)
self.goalBuffer.set_text("", -1)
self.conflictBuffer.set_text("", -1)
self.epiphanyBuffer.set_text("", -1)
self.oneSentenceBuffer.set_text("", -1)
self.oneParagraphBuffer.set_text("", -1)
self.summaryBuffer.set_text("", -1)
self.notesBuffer.set_text("", -1)
def characterSelectionChanged(self, selection: Gtk.TreeSelection):
model, tree_iter = selection.get_selected()
if tree_iter is None:
self.unloadCharacterData()
return
for other in self.characterSelections:
if other != selection:
other.unselect_all()
character = self.characters.getByID(model[tree_iter][0])
if character is None:
self.unloadCharacterData()
else:
self.loadCharacterData(character)
def colorSet(self, button: Gtk.ColorButton):
if self.character is None:
return
color = button.get_rgba()
print("{} {} {} {}".format(color.red, color.green, color.blue, color.alpha))
red = int(color.red * 255)
green = int(color.green * 255)
blue = int(color.blue * 255)
self.character.color = Color(red, green, blue)
def importanceChanged(self, combo: Gtk.ComboBox):
tree_iter = combo.get_active_iter()
@ -62,9 +164,10 @@ class CharactersView:
print("blub " + name)
def allowPOVToggled(self, button: Gtk.ToggleButton):
state = button.get_active()
if self.character is None:
return
print("OK: {}".format(state))
self.character.POV = button.get_active()
def addDetailsClicked(self, button: Gtk.Button):
tree_iter = self.detailsStore.append()

View file

@ -49,7 +49,7 @@ class MainWindow:
self.generalView = MainWindow.packViewIntoSlot(builder, "general_slot", GeneralView, self.project.info)
self.summaryView = MainWindow.packViewIntoSlot(builder, "summary_slot", SummaryView, self.project.summary)
self.charactersView = MainWindow.packViewIntoSlot(builder, "characters_slot", CharactersView)
self.charactersView = MainWindow.packViewIntoSlot(builder, "characters_slot", CharactersView, self.project.characters)
self.plotView = MainWindow.packViewIntoSlot(builder, "plot_slot", PlotView)
self.worldView = MainWindow.packViewIntoSlot(builder, "world_slot", WorldView)
self.outlineView = MainWindow.packViewIntoSlot(builder, "outline_slot", OutlineView)

27
manuskript/ui/util.py Normal file
View file

@ -0,0 +1,27 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import gi
gi.require_version('Gdk', '3.0')
gi.require_version('GdkPixbuf', '2.0')
from gi.repository import GdkPixbuf, Gdk
from manuskript.data import Color
def rgbaFromColor(color: Color) -> Gdk.RGBA:
rgba = Gdk.RGBA()
rgba.red = color.red / 255.
rgba.green = color.green / 255.
rgba.blue = color.blue / 255.
rgba.alpha = 1.
return rgba
def pixbufFromColor(color: Color) -> GdkPixbuf:
if color is None:
return None
pixbuf = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB, False, 8, 16, 16)
pixbuf.fill(color.getRGB())
return pixbuf

View file

@ -10,3 +10,11 @@ def validString(invalid: str) -> str:
def invalidString(valid: str) -> str:
return None if len(valid) == 0 else valid
def validInt(invalid: int) -> int:
return 0 if invalid is None else invalid
def invalidInt(valid: int) -> int:
return None if valid == 0 else valid

View file

@ -25,6 +25,27 @@ along with Manuskript. If not, see <http://www.gnu.org/licenses/>.
<!-- interface-name Manuskript -->
<!-- interface-description Manuskript is an open-source tool for writers. -->
<!-- interface-copyright 2015-2021 Olivier Keshavjee et al. -->
<object class="GtkListStore" id="characters_store">
<columns>
<!-- column-name ID -->
<column type="gint"/>
<!-- column-name name -->
<column type="gchararray"/>
<!-- column-name color -->
<column type="GdkPixbuf"/>
<!-- column-name importance -->
<column type="gint"/>
</columns>
</object>
<object class="GtkTreeModelFilter" id="main_characters_store">
<property name="child-model">characters_store</property>
</object>
<object class="GtkTreeModelFilter" id="minor_characters_store">
<property name="child-model">characters_store</property>
</object>
<object class="GtkTreeModelFilter" id="secondary_characters_store">
<property name="child-model">characters_store</property>
</object>
<object class="GtkTextBuffer" id="conflict"/>
<object class="GtkListStore" id="details_store">
<columns>
@ -109,9 +130,30 @@ along with Manuskript. If not, see <http://www.gnu.org/licenses/>.
</packing>
</child>
<child>
<object class="GtkListBox">
<object class="GtkTreeView">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="can-focus">True</property>
<property name="model">main_characters_store</property>
<property name="headers-visible">False</property>
<child internal-child="selection">
<object class="GtkTreeSelection" id="main_character_selection"/>
</child>
<child>
<object class="GtkTreeViewColumn">
<child>
<object class="GtkCellRendererPixbuf"/>
<attributes>
<attribute name="pixbuf">2</attribute>
</attributes>
</child>
<child>
<object class="GtkCellRendererText"/>
<attributes>
<attribute name="text">1</attribute>
</attributes>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
@ -132,9 +174,30 @@ along with Manuskript. If not, see <http://www.gnu.org/licenses/>.
</packing>
</child>
<child>
<object class="GtkListBox">
<object class="GtkTreeView">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="can-focus">True</property>
<property name="model">secondary_characters_store</property>
<property name="headers-visible">False</property>
<child internal-child="selection">
<object class="GtkTreeSelection" id="secondary_character_selection"/>
</child>
<child>
<object class="GtkTreeViewColumn">
<child>
<object class="GtkCellRendererPixbuf"/>
<attributes>
<attribute name="pixbuf">2</attribute>
</attributes>
</child>
<child>
<object class="GtkCellRendererText"/>
<attributes>
<attribute name="text">1</attribute>
</attributes>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
@ -155,12 +218,33 @@ along with Manuskript. If not, see <http://www.gnu.org/licenses/>.
</packing>
</child>
<child>
<object class="GtkListBox">
<object class="GtkTreeView">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="can-focus">True</property>
<property name="model">minor_characters_store</property>
<property name="headers-visible">False</property>
<child internal-child="selection">
<object class="GtkTreeSelection" id="minor_character_selection"/>
</child>
<child>
<object class="GtkTreeViewColumn">
<child>
<object class="GtkCellRendererPixbuf"/>
<attributes>
<attribute name="pixbuf">2</attribute>
</attributes>
</child>
<child>
<object class="GtkCellRendererText"/>
<attributes>
<attribute name="text">1</attribute>
</attributes>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">5</property>
</packing>
@ -349,6 +433,7 @@ summary</property>
<property name="height-request">100</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="wrap-mode">word-char</property>
<property name="buffer">one_paragraph_summary</property>
</object>
</child>
@ -374,6 +459,7 @@ summary</property>
<property name="height-request">100</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="wrap-mode">word-char</property>
<property name="buffer">one_sentence_summary</property>
</object>
</child>
@ -399,6 +485,7 @@ summary</property>
<property name="height-request">100</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="wrap-mode">word-char</property>
<property name="buffer">epiphany</property>
</object>
</child>
@ -424,6 +511,7 @@ summary</property>
<property name="height-request">100</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="wrap-mode">word-char</property>
<property name="buffer">conflict</property>
</object>
</child>
@ -449,6 +537,7 @@ summary</property>
<property name="height-request">100</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="wrap-mode">word-char</property>
<property name="buffer">goal</property>
</object>
</child>
@ -474,6 +563,7 @@ summary</property>
<property name="height-request">100</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="wrap-mode">word-char</property>
<property name="buffer">motivation</property>
</object>
</child>
@ -631,6 +721,7 @@ summary</property>
<object class="GtkTextView">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="wrap-mode">word-char</property>
<property name="buffer">summary</property>
</object>
</child>
@ -667,6 +758,7 @@ summary</property>
<object class="GtkTextView">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="wrap-mode">word-char</property>
<property name="buffer">notes</property>
</object>
</child>