Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revisit character network #669

Merged
merged 37 commits into from
Sep 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
81b1147
Extract ZoomBar
zkovari Aug 30, 2023
44981c1
Network package
zkovari Aug 30, 2023
5bda941
Update socket
zkovari Aug 30, 2023
74d4967
progress
zkovari Aug 30, 2023
6ae9f5c
Extract networkview
zkovari Aug 30, 2023
de33584
Extract NetworkScene
zkovari Aug 30, 2023
d0edeae
Use new Network components in eventsmap
zkovari Aug 30, 2023
7fb152f
Fix secondary selectors for eventsmap
zkovari Aug 31, 2023
9ce796e
alt
zkovari Aug 31, 2023
de6de37
recolor socket on hover
zkovari Aug 31, 2023
b80bc17
progress
zkovari Aug 31, 2023
048bba7
more extract
zkovari Aug 31, 2023
141bf03
more extract
zkovari Aug 31, 2023
0c8be79
arrange connectors
zkovari Aug 31, 2023
cb36a8a
rename _linkDisplayedMode
zkovari Aug 31, 2023
8f66e6e
new socket instance
zkovari Aug 31, 2023
1f640b3
fix socket painting issue
zkovari Aug 31, 2023
6883291
fix
zkovari Aug 31, 2023
5aa8969
arrow
zkovari Aug 31, 2023
54a42c1
arrow
zkovari Aug 31, 2023
94035b1
BaseItemEditor
zkovari Sep 1, 2023
69df2a9
progress
zkovari Sep 1, 2023
4cf84cf
penstyle editor
zkovari Sep 1, 2023
f03cc95
penwidth
zkovari Sep 1, 2023
fa104b7
tooltip for additions
zkovari Sep 1, 2023
220a3c6
Start relations
zkovari Sep 1, 2023
c2952de
cancel addition properly
zkovari Sep 2, 2023
ffdf887
restore connector editor
zkovari Sep 2, 2023
0da83ac
socket angle and better connector
zkovari Sep 2, 2023
8d3c153
Turn relation selector to a menu
zkovari Sep 3, 2023
12d6679
icon selector
zkovari Sep 3, 2023
ea905b8
color picker
zkovari Sep 3, 2023
0303c9f
color picker
zkovari Sep 3, 2023
09f1f17
hide color picker from icon selector
zkovari Sep 4, 2023
7a9382b
more relation types
zkovari Sep 4, 2023
26ec0fe
progress
zkovari Sep 4, 2023
70a9654
progress
zkovari Sep 4, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/main/python/plotlyst/core/domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -1736,6 +1736,11 @@ class RelationsNetwork:
nodes: List[CharacterNode] = field(default_factory=list)


@dataclass
class Relation(SelectionItem):
pass


@dataclass
class DocsPreferences:
grammar_check: bool = True
Expand Down
24 changes: 6 additions & 18 deletions src/main/python/plotlyst/view/characters_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@
from PyQt6.QtGui import QKeySequence
from PyQt6.QtWidgets import QWidget
from overrides import overrides
from qthandy import busy, gc, incr_font, bold, vbox, vspacer
from qthandy import busy, gc, incr_font, bold
from qthandy.filter import InstantTooltipEventFilter, OpacityEventFilter
from qtmenu import MenuWidget

from src.main.python.plotlyst.common import PLOTLYST_SECONDARY_COLOR
from src.main.python.plotlyst.core.domain import Novel, Character, RelationsNetwork, CharacterNode
from src.main.python.plotlyst.core.domain import Novel, Character
from src.main.python.plotlyst.env import app_env
from src.main.python.plotlyst.event.core import EventListener, Event, emit_event
from src.main.python.plotlyst.event.handler import event_dispatchers
Expand All @@ -50,7 +51,7 @@
from src.main.python.plotlyst.view.widget.character import CharacterComparisonWidget, LayoutType, \
CharacterComparisonAttribute
from src.main.python.plotlyst.view.widget.character.comp import CharactersTreeView
from src.main.python.plotlyst.view.widget.character.relations import RelationsView, RelationsSelectorBox
from src.main.python.plotlyst.view.widget.character.network import CharacterNetworkView
from src.main.python.plotlyst.view.widget.characters import CharacterTimelineWidget, CharactersProgressWidget


Expand Down Expand Up @@ -172,22 +173,9 @@ def __init__(self, novel: Novel):
for btn in self.ui.btnGroupComparison.buttons():
btn.installEventFilter(OpacityEventFilter(btn, ignoreCheckedButton=True))

self._relations = RelationsView(self.novel)
self._relations = CharacterNetworkView(self.novel)
self.ui.relationsParent.layout().addWidget(self._relations)

self._relationsSelector = RelationsSelectorBox(self.novel)
vbox(self.ui.wdgGraphSelectorParent).addWidget(self._relationsSelector)
self.ui.wdgGraphSelectorParent.layout().addWidget(vspacer())
self._relationsSelector.currentChanged.connect(lambda i, w: self._relations.refresh(w.network()))

self._relations.relationsScene().charactersChanged.connect(self._relationsSelector.refreshCharacters)

node = CharacterNode(50, 50)
if self.novel.characters:
node.set_character(self.novel.characters[0])
network1 = RelationsNetwork('Network 1', icon='ph.share-network-bold', nodes=[node])
self._relationsSelector.addNetwork(network1)
self._relationsSelector.addNetwork(RelationsNetwork('Network 2', icon='ph.share-network-bold'))
self.ui.wdgGraphSelectorParent.setVisible(False)

self.ui.networkSplitter.setSizes([100, 500])

Expand Down
18 changes: 0 additions & 18 deletions src/main/python/plotlyst/view/style/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,6 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from typing import Union

from PyQt6.QtCore import Qt
from PyQt6.QtGui import QColor
from PyQt6.QtWidgets import QAbstractButton

style = '''
QPushButton::menu-indicator[no-menu] {
Expand Down Expand Up @@ -422,16 +417,3 @@
}

'''


def apply_button_color(button: QAbstractButton, color: Union[str, QColor, Qt.GlobalColor]):
if isinstance(color, QColor):
color = color.name()
button.setStyleSheet(f'''
QToolButton {{
background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1,
stop: 0 {color});
border: 1px solid darkGrey;
border-radius: 12px;
}}
''')
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .view import CharacterNetworkView
188 changes: 188 additions & 0 deletions src/main/python/plotlyst/view/widget/character/network/editor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
"""
Plotlyst
Copyright (C) 2021-2023 Zsolt Kovari

This file is part of Plotlyst.

Plotlyst is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Plotlyst is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from typing import Optional

from PyQt6.QtCore import pyqtSignal
from PyQt6.QtGui import QAction, QColor
from PyQt6.QtWidgets import QButtonGroup
from qthandy import vline, retain_when_hidden
from qtmenu import GridMenuWidget

from src.main.python.plotlyst.core.domain import RelationsNetwork, Relation
from src.main.python.plotlyst.view.common import tool_btn, action
from src.main.python.plotlyst.view.dialog.utility import IconSelectorDialog
from src.main.python.plotlyst.view.icons import IconRegistry
from src.main.python.plotlyst.view.widget.graphics import BaseItemEditor, SolidPenStyleSelector, DashPenStyleSelector, \
DotPenStyleSelector, ConnectorItem, PenWidthEditor, RelationsButton, SecondarySelectorWidget
from src.main.python.plotlyst.view.widget.utility import ColorPicker


class RelationSelector(GridMenuWidget):
relationSelected = pyqtSignal(Relation)

def __init__(self, network: RelationsNetwork = None, parent=None):
super().__init__(parent)
self._network = network
self._romance = Relation('Romance', icon='ei.heart', icon_color='#d1495b')
self._breakUp = Relation('Breakup', icon='fa5s.heart-broken', icon_color='#d1495b')
self._affection = Relation('Affection', icon='mdi.sparkles', icon_color='#d1495b')
self._crush = Relation('Crush', icon='fa5.grin-hearts', icon_color='#d1495b')
self._unrequited = Relation('Unrequited love', icon='mdi.heart-half-full', icon_color='#d1495b')

self._colleague = Relation('Colleague', icon='fa5s.briefcase', icon_color='#9c6644')
self._student = Relation('Student', icon='fa5s.graduation-cap', icon_color='black')
self._foil = Relation('Foil', icon='fa5s.yin-yang', icon_color='#947eb0')

self._friendship = Relation('Friendship', icon='fa5s.user-friends', icon_color='#457b9d')
self._sidekick = Relation('Sidekick', icon='ei.asl', icon_color='#b0a990')
self._guide = Relation('Guide', icon='mdi.compass-rose', icon_color='#80ced7')
self._supporter = Relation('Supporter', icon='fa5s.thumbs-up', icon_color='#266dd3')
self._adversary = Relation('Adversary', icon='fa5s.thumbs-down', icon_color='#9e1946')
self._betrayal = Relation('Betrayal', icon='mdi6.knife', icon_color='grey')
self._conflict = Relation('Conflict', icon='mdi.sword-cross', icon_color='#f3a712')

self._newAction(self._romance, 0, 0)
self._newAction(self._affection, 0, 1)
self._newAction(self._crush, 0, 2)
self._newAction(self._breakUp, 1, 0)
self._newAction(self._unrequited, 1, 1, 2)
self.addSeparator(2, 0, colSpan=3)
self._newAction(self._friendship, 3, 0)
self._newAction(self._sidekick, 3, 1)
self._newAction(self._guide, 3, 2)
self._newAction(self._supporter, 4, 0)
self._newAction(self._adversary, 4, 1)
self._newAction(self._betrayal, 5, 0)
self._newAction(self._conflict, 5, 1)
self._newAction(self._foil, 6, 0)
self.addSeparator(7, 0, colSpan=3)
self._newAction(self._colleague, 8, 0)
self._newAction(self._student, 8, 1)

def _newAction(self, relation: Relation, row: int, col: int, colSpan: int = 1, showText: bool = True) -> QAction:
text = relation.text if showText else ''
action_ = action(text, IconRegistry.from_name(relation.icon, relation.icon_color))
action_.setToolTip(relation.text)
self.addAction(action_, row, col, colSpan=colSpan)
action_.triggered.connect(lambda: self.relationSelected.emit(relation))

return action_


class ConnectorEditor(BaseItemEditor):
def __init__(self, network: RelationsNetwork, parent=None):
super().__init__(parent)
self._connector: Optional[ConnectorItem] = None

self._btnRelationType = RelationsButton()
self._btnColor = tool_btn(IconRegistry.from_name('fa5s.circle', color='darkBlue'), 'Change style',
transparent_=True)
self._colorPicker = ColorPicker(self, maxColumn=5)
self._colorPicker.colorPicked.connect(self._colorChanged)
self._colorSecondaryWidget = SecondarySelectorWidget(self)
self._colorSecondaryWidget.addWidget(self._colorPicker, 0, 0)
self.addSecondaryWidget(self._btnColor, self._colorSecondaryWidget)
self._btnIcon = tool_btn(IconRegistry.from_name('mdi.emoticon-outline'), 'Change icon', transparent_=True)
self._btnIcon.clicked.connect(self._showIconSelector)

self._solidLine = SolidPenStyleSelector()
self._dashLine = DashPenStyleSelector()
self._dotLine = DotPenStyleSelector()
self._lineBtnGroup = QButtonGroup()
self._lineBtnGroup.addButton(self._solidLine)
self._lineBtnGroup.addButton(self._dashLine)
self._lineBtnGroup.addButton(self._dotLine)
self._lineBtnGroup.buttonClicked.connect(self._penStyleChanged)

self._sbWidth = PenWidthEditor()
self._sbWidth.valueChanged.connect(self._widthChanged)

self._relationSelector = RelationSelector(network, self._btnRelationType)
self._relationSelector.relationSelected.connect(self._relationChanged)

self._toolbar.layout().addWidget(self._btnRelationType)
self._toolbar.layout().addWidget(vline())
self._toolbar.layout().addWidget(self._btnColor)
self._toolbar.layout().addWidget(self._btnIcon)
self._toolbar.layout().addWidget(vline())
self._toolbar.layout().addWidget(self._solidLine)
self._toolbar.layout().addWidget(self._dashLine)
self._toolbar.layout().addWidget(self._dotLine)
self._toolbar.layout().addWidget(vline())
self._toolbar.layout().addWidget(self._sbWidth)

def setItem(self, connector: ConnectorItem):
self._connector = None
self._hideSecondarySelectors()

self._sbWidth.setValue(connector.penWidth())

icon: str = connector.icon()
self._updateColor(connector.color().name())
if icon:
self._updateIcon(icon)
else:
self._resetIcon()

penStyle = connector.penStyle()
for line in [self._solidLine, self._dashLine, self._dotLine]:
if penStyle == line.penStyle():
line.setChecked(True)
break
self._connector = connector

def _relationChanged(self, relation: Relation):
if self._connector:
self._connector.setRelation(relation)
self._updateIcon(relation.icon)
self._updateColor(relation.icon_color)

def _penStyleChanged(self):
btn = self._lineBtnGroup.checkedButton()
if btn and self._connector:
self._connector.setPenStyle(btn.penStyle())

def _widthChanged(self, value: int):
if self._connector:
self._connector.setPenWidth(value)

def _colorChanged(self, color: QColor):
if self._connector:
self._connector.setColor(color)
self._updateColor(color.name())
pass

def _showIconSelector(self):
dialog = IconSelectorDialog()
retain_when_hidden(dialog.selector.colorPicker)
dialog.selector.colorPicker.setVisible(False)
result = dialog.display()
if result and self._connector:
self._connector.setIcon(result[0])
self._updateIcon(result[0])

def _updateIcon(self, icon: str):
self._btnIcon.setIcon(IconRegistry.from_name(icon))

def _resetIcon(self):
self._btnIcon.setIcon(IconRegistry.from_name('mdi.emoticon-outline'))

def _updateColor(self, color: str):
self._btnColor.setIcon(IconRegistry.from_name('fa5s.circle', color))
Loading
Loading