Skip to content

Commit

Permalink
plot selection
Browse files Browse the repository at this point in the history
  • Loading branch information
zkovari committed Sep 18, 2023
1 parent 9b2788f commit 2311b13
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 138 deletions.
7 changes: 5 additions & 2 deletions src/main/python/plotlyst/view/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,9 +370,12 @@ def insert_before(parent: QWidget, widget: QWidget, reference: QWidget):
parent.layout().insertWidget(i, widget)


def insert_after(parent: QWidget, widget: QWidget, reference: QWidget):
def insert_after(parent: QWidget, widget: QWidget, reference: QWidget, alignment=None):
i = parent.layout().indexOf(reference)
parent.layout().insertWidget(i + 1, widget)
if alignment is not None:
parent.layout().insertWidget(i + 1, widget, alignment=alignment)
else:
parent.layout().insertWidget(i + 1, widget)


def tool_btn(icon: QIcon, tooltip: str = '', checkable: bool = False, base: bool = False,
Expand Down
25 changes: 4 additions & 21 deletions src/main/python/plotlyst/view/scene_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@
from PyQt6.QtWidgets import QWidget, QTableView
from overrides import overrides
from qtanim import fade_in
from qthandy import flow, clear_layout, underline, incr_font, margins
from qthandy import flow, underline, incr_font, margins
from qtmenu import MenuWidget, ScrollableMenuWidget

from src.main.python.plotlyst.core.client import json_client
from src.main.python.plotlyst.core.domain import Novel, Scene, Document, StoryBeat, \
Character, ScenePlotReference, TagReference, ScenePurposeType, ScenePurpose
Character, TagReference, ScenePurposeType, ScenePurpose
from src.main.python.plotlyst.env import app_env
from src.main.python.plotlyst.event.core import emit_info, EventListener, Event, emit_event
from src.main.python.plotlyst.event.handler import event_dispatchers
Expand All @@ -45,7 +45,6 @@
from src.main.python.plotlyst.view.widget.labels import CharacterLabel
from src.main.python.plotlyst.view.widget.scene.editor import ScenePurposeSelectorWidget, ScenePurposeTypeButton, \
SceneStorylineEditor, SceneAgendaEditor
from src.main.python.plotlyst.view.widget.scene.plot import ScenePlotSelector
from src.main.python.plotlyst.view.widget.scenes import SceneTagSelector


Expand Down Expand Up @@ -87,8 +86,6 @@ def __init__(self, novel: Novel, scene: Optional[Scene] = None):
self.ui.lblTitleEmoji.setText(emoji.emojize(':clapper_board:'))
self.ui.lblSynopsisEmoji.setFont(self._emoji_font)
self.ui.lblSynopsisEmoji.setText(emoji.emojize(':scroll:'))
self.ui.lblPlotEmoji.setFont(self._emoji_font)
self.ui.lblPlotEmoji.setText(emoji.emojize(':chart_increasing:'))

self.ui.wdgStructure.setBeatsCheckable(True)
self.ui.wdgStructure.setStructure(self.novel)
Expand Down Expand Up @@ -140,16 +137,14 @@ def __init__(self, novel: Novel, scene: Optional[Scene] = None):
self._btnPurposeType.selectionRequested.connect(self._resetPurposeEditor)
self.ui.wdgMidbar.layout().insertWidget(0, self._btnPurposeType)

self._storylineEditor = SceneStorylineEditor()
self._storylineEditor = SceneStorylineEditor(self.novel)
self.ui.tabStorylines.layout().addWidget(self._storylineEditor)

self._agencyEditor = SceneAgendaEditor(self.novel)
self.ui.tabCharacter.layout().addWidget(self._agencyEditor)

self.ui.btnClose.clicked.connect(self._on_close)

flow(self.ui.wdgPlotContainer)

self.ui.wdgSceneStructure.setUnsetCharacterSlot(self._pov_not_selected_notification)

self._update_view(scene)
Expand Down Expand Up @@ -185,12 +180,8 @@ def _update_view(self, scene: Optional[Scene] = None):
self.ui.sbDay.setValue(self.scene.day)

self.ui.wdgSceneStructure.setScene(self.novel, self.scene)
clear_layout(self.ui.wdgPlotContainer)
for plot_v in self.scene.plot_values:
self._add_plot_selector(plot_v)
self._add_plot_selector()

self.tag_selector.setScene(self.scene)
self._storylineEditor.setScene(self.scene)

self.ui.lineTitle.setText(self.scene.title)
self.ui.textSynopsis.setText(self.scene.synopsis)
Expand Down Expand Up @@ -259,14 +250,6 @@ def _pov_not_selected_notification(self):
emit_info('POV character must be selected first')
qtanim.shake(self.ui.wdgPov)

def _add_plot_selector(self, plot_value: Optional[ScenePlotReference] = None):
if plot_value or len(self.novel.plots) > len(self.scene.plot_values):
plot_selector = ScenePlotSelector(self.novel, self.scene, simplified=len(self.scene.plot_values) > 0)
plot_selector.plotSelected.connect(self._add_plot_selector)
if plot_value:
plot_selector.setPlot(plot_value)
self.ui.wdgPlotContainer.layout().addWidget(plot_selector)

def _on_pov_changed(self, pov: Character):
self.scene.pov = pov

Expand Down
53 changes: 38 additions & 15 deletions src/main/python/plotlyst/view/widget/scene/editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from abc import abstractmethod
from enum import Enum
from functools import partial
from typing import List, Optional, Any

Expand All @@ -35,17 +34,18 @@
from src.main.python.plotlyst.common import raise_unrecognized_arg
from src.main.python.plotlyst.core.domain import Scene, Novel, ScenePurpose, advance_story_scene_purpose, \
ScenePurposeType, reaction_story_scene_purpose, character_story_scene_purpose, setup_story_scene_purpose, \
emotion_story_scene_purpose, exposition_story_scene_purpose, scene_purposes, Character
emotion_story_scene_purpose, exposition_story_scene_purpose, scene_purposes, Character, Plot
from src.main.python.plotlyst.event.core import EventListener, Event, emit_event
from src.main.python.plotlyst.event.handler import event_dispatchers
from src.main.python.plotlyst.events import SceneChangedEvent
from src.main.python.plotlyst.service.persistence import RepositoryPersistenceManager
from src.main.python.plotlyst.view.common import DelayedSignalSlotConnector, action, wrap, label, scrolled, \
ButtonPressResizeEventFilter
ButtonPressResizeEventFilter, insert_after
from src.main.python.plotlyst.view.icons import IconRegistry
from src.main.python.plotlyst.view.widget.characters import CharacterSelectorButton
from src.main.python.plotlyst.view.widget.display import Icon
from src.main.python.plotlyst.view.widget.input import RemovalButton
from src.main.python.plotlyst.view.widget.scene.plot import ScenePlotSelectorButton


class SceneMiniEditor(QWidget, EventListener):
Expand Down Expand Up @@ -362,11 +362,6 @@ def __init__(self, parent=None):
self._wdgPurposes.layout().addWidget(spacer())


class SceneElementIconDisplayMode(Enum):
Center = 0
Left = 1


class SceneElementWidget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
Expand Down Expand Up @@ -434,7 +429,7 @@ def leaveEvent(self, event: QEvent) -> None:
else:
self._btnClose.setVisible(False)

def setIcon(self, icon: str, colorActive: str):
def setIcon(self, icon: str, colorActive: str = 'black'):
self._colorActive = QColor(colorActive)
self._iconActive.setIcon(IconRegistry.from_name(icon, colorActive))
self._iconIdle.setIcon(IconRegistry.from_name(icon, 'lightgrey'))
Expand Down Expand Up @@ -497,13 +492,32 @@ def _updateValue(self, value: Any):


class PlotSceneElementEditor(TextBasedSceneElementWidget):
def __init__(self, parent=None):
def __init__(self, novel: Novel, parent=None):
super().__init__(parent)
self._novel = novel

self._btnPlotSelector = ScenePlotSelectorButton(self._novel)
self._btnPlotSelector.plotSelected.connect(self._plotSelected)
self._btnPlotSelector.setFixedHeight(self._titleActive.sizeHint().height())
self._titleActive.setHidden(True)
insert_after(self._pageEditor, self._btnPlotSelector, reference=self._titleActive,
alignment=Qt.AlignmentFlag.AlignCenter)

def setScene(self, scene: Scene):
self._btnPlotSelector.setScene(scene)

def _plotSelected(self, plot: Plot):
self.setIcon(plot.icon, plot.icon_color)
font = self._btnPlotSelector.font()
font.setPointSize(self._titleActive.font().pointSize())
self._btnPlotSelector.setFont(font)


class AbstractSceneElementsEditor(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self._scene: Optional[Scene] = None

vbox(self)
sp(self).h_exp()
self._scrollarea, self._wdgElementsParent = scrolled(self, frameless=True)
Expand All @@ -520,15 +534,19 @@ def __init__(self, parent=None):
self._wdgElementsParent.layout().addWidget(self._lblBottom)
self._wdgElementsParent.layout().addWidget(self._wdgElementsBottomRow)

def setScene(self, scene: Scene):
self._scene = scene


class SceneStorylineEditor(AbstractSceneElementsEditor):
def __init__(self, parent=None):
def __init__(self, novel: Novel, parent=None):
super().__init__(parent)
self._lblBottom.setText('Character relations')
self._novel = novel
# self._lblBottom.setText('Character relations')

self._plotElement = PlotSceneElementEditor()
self._plotElement = PlotSceneElementEditor(self._novel)
self._plotElement.setText('Plot')
self._plotElement.setIcon('fa5s.theater-masks', 'grey')
self._plotElement.setIcon('fa5s.theater-masks')
self._plotElement.setPlaceholderText('Describe how this scene is related to the selected plot')

self._themeElement = TextBasedSceneElementWidget()
Expand All @@ -541,13 +559,18 @@ def __init__(self, parent=None):

self._consequencesElement = TextBasedSceneElementWidget()
self._consequencesElement.setText('Consequences')
self._consequencesElement.setIcon('mdi.ray-start-arrow', 'black')
self._consequencesElement.setIcon('mdi.ray-start-arrow')

self._wdgElementsTopRow.layout().addWidget(self._plotElement)
self._wdgElementsTopRow.layout().addWidget(self._themeElement)
self._wdgElementsTopRow.layout().addWidget(self._outcomeElement)
self._wdgElementsTopRow.layout().addWidget(self._consequencesElement)

@overrides
def setScene(self, scene: Scene):
super().setScene(scene)
self._plotElement.setScene(scene)


class SceneAgendaEditor(AbstractSceneElementsEditor):
def __init__(self, novel: Novel, parent=None):
Expand Down
113 changes: 56 additions & 57 deletions src/main/python/plotlyst/view/widget/scene/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,18 @@
import qtanim
from PyQt6.QtCore import Qt, pyqtSignal
from PyQt6.QtGui import QColor, QMouseEvent, QShowEvent
from PyQt6.QtWidgets import QWidget, QToolButton, QTextEdit, QLabel
from PyQt6.QtWidgets import QWidget, QToolButton, QTextEdit, QLabel, QPushButton
from overrides import overrides
from qthandy import vbox, hbox, pointy, transparent, retain_when_hidden, spacer, sp, decr_icon, ask_confirmation, line
from qthandy import vbox, hbox, transparent, retain_when_hidden, spacer, sp, decr_icon, line, pointy, underline
from qthandy.filter import OpacityEventFilter
from qtmenu import MenuWidget

from src.main.python.plotlyst.core.domain import Novel, Scene, ScenePlotReference, PlotValue, ScenePlotValueCharge, Plot
from src.main.python.plotlyst.view.common import action, fade_out_and_gc
from src.main.python.plotlyst.view.common import action
from src.main.python.plotlyst.view.icons import IconRegistry
from src.main.python.plotlyst.view.style.base import apply_white_menu
from src.main.python.plotlyst.view.widget.button import SecondaryActionToolButton, SecondaryActionPushButton
from src.main.python.plotlyst.view.widget.button import SecondaryActionToolButton
from src.main.python.plotlyst.view.widget.display import IconText
from src.main.python.plotlyst.view.widget.labels import PlotValueLabel, SelectionItemLabel, ScenePlotValueLabel
from src.main.python.plotlyst.view.widget.labels import PlotValueLabel


class ScenePlotValueChargeWidget(QWidget):
Expand Down Expand Up @@ -157,68 +156,68 @@ def _commentChanged(self):
self.plotReference.data.comment = self.textComment.toPlainText()


class ScenePlotSelector(QWidget):
plotSelected = pyqtSignal()
class ScenePlotSelectorMenu(MenuWidget):
plotSelected = pyqtSignal(Plot)

def __init__(self, novel: Novel, scene: Scene, simplified: bool = False, parent=None):
super(ScenePlotSelector, self).__init__(parent)
self.novel = novel
self.scene = scene
def __init__(self, novel: Novel, parent=None):
super().__init__(parent)
self._novel = novel
self._scene: Optional[Scene] = None

self.aboutToShow.connect(self._beforeShow)

def setScene(self, scene: Scene):
self._scene = scene

def _beforeShow(self):
if self._scene is None:
return
self.clear()
occupied_plot_ids = [x.plot.id for x in self._scene.plot_values]
self.addSection('Link storylines to this scene')
self.addSeparator()
for plot in self._novel.plots:
action_ = action(plot.text, IconRegistry.from_name(plot.icon, plot.icon_color),
partial(self.plotSelected.emit, plot))
if plot.id in occupied_plot_ids:
action_.setDisabled(True)
self.addAction(action_)


class ScenePlotSelectorButton(QPushButton):
plotSelected = pyqtSignal(Plot)

def __init__(self, novel: Novel, parent=None):
super().__init__(parent)
self._novel = novel
self._scene: Optional[Scene] = None
self.plotValue: Optional[ScenePlotReference] = None
hbox(self)

self.label: Optional[SelectionItemLabel] = None
transparent(self)
self.setProperty('no-menu', True)
self.setText('Storyline')

self.btnLinkPlot = SecondaryActionPushButton(self)
if simplified:
self.btnLinkPlot.setIcon(IconRegistry.plus_circle_icon('grey'))
else:
self.btnLinkPlot.setText('Associate plot')
self.layout().addWidget(self.btnLinkPlot)
self.installEventFilter(OpacityEventFilter(parent=self, leaveOpacity=0.7))

self.btnLinkPlot.installEventFilter(
OpacityEventFilter(parent=self.btnLinkPlot, leaveOpacity=0.4 if simplified else 0.7))
if self._novel.plots:
pointy(self)
underline(self)
self._menu = ScenePlotSelectorMenu(self._novel, self)
self._menu.plotSelected.connect(self._plotSelected)

self._menu = MenuWidget(self.btnLinkPlot)
self._menu.aboutToShow.connect(self._beforeShow)
def setScene(self, scene: Scene):
self._scene = scene
self._menu.setScene(scene)

def setPlot(self, plotValue: ScenePlotReference):
self.plotValue = plotValue
self.label = ScenePlotValueLabel(plotValue, self)
pointy(self.label)
self.label.clicked.connect(self._plotValueClicked)

self.label.removalRequested.connect(self._remove)
self.layout().addWidget(self.label)
self.btnLinkPlot.setHidden(True)
self.setText(plotValue.plot.text)
underline(self, False)

def _plotSelected(self, plot: Plot):
plotValue = ScenePlotReference(plot)
self.scene.plot_values.append(plotValue)
# TODO add back later
# self._scene.plot_values.append(plotValue)
self.setPlot(plotValue)

self.plotSelected.emit()
self._plotValueClicked()

def _plotValueClicked(self):
menu = MenuWidget(self.label)
apply_white_menu(menu)
menu.addWidget(ScenePlotValueEditor(self.plotValue))
menu.exec()

def _beforeShow(self):
self._menu.clear()
occupied_plot_ids = [x.plot.id for x in self.scene.plot_values]
self._menu.addSection('Link plotlines to this scene')
self._menu.addSeparator()
for plot in self.novel.plots:
action_ = action(plot.text, IconRegistry.from_name(plot.icon, plot.icon_color),
partial(self._plotSelected, plot))
if plot.id in occupied_plot_ids:
action_.setDisabled(True)
self._menu.addAction(action_)

def _remove(self):
if ask_confirmation(f"Remove scene association for plot '{self.plotValue.plot.text}'?"):
self.scene.plot_values.remove(self.plotValue)
fade_out_and_gc(self.parent(), self)
self.plotSelected.emit(plot)
Loading

0 comments on commit 2311b13

Please sign in to comment.