Skip to content

comments

Full documentation pages are generated for docstring reference only and may contain symbols imported from other modules. Imported symbols are not distinguished from locally defined symbols and will appear in any module that they are imported into. For better information on where symbols should be imported from, review the sourcecode on the github.

FoSpy.ui.app.editors.comments

BaseEditorWidget

Bases: QWidget

Source code in FoSpy/ui/app/editors/_base.py
class BaseEditorWidget(QWidget):
    def __init__(self, block_widget, editor_widget):
        super().__init__()

        self.blk_widget = block_widget

        self.base_layout = QVBoxLayout(self)
        self.base_layout.setContentsMargins(0, 0, 0, 0)
        self.base_layout.setSpacing(10)

        self.editor = editor_widget
        self.refresh_editor()
        self.base_layout.addWidget(self.editor, stretch=0)

        self.setLayout(self.base_layout)

        button_layout = QHBoxLayout()
        button_layout.addStretch()

        self.apply_btn = QPushButton("Apply")
        self.apply_btn.clicked.connect(self.apply)
        button_layout.addWidget(self.apply_btn)

        self.okay_btn = QPushButton("OK")
        self.okay_btn.clicked.connect(self.ok)
        button_layout.addWidget(self.okay_btn)

        self.cancel_btn = QPushButton("Cancel")
        self.cancel_btn.clicked.connect(self.cancel)
        button_layout.addWidget(self.cancel_btn)

        self.base_layout.addLayout(button_layout, stretch=0)

        self.hintPanel = QVBoxLayout()
        self.hintLabel = QLabel()
        self.hintButton = QPushButton("More...")

        self.hintPanel.addWidget(self.hintLabel)
        self.hintPanel.addWidget(self.hintButton)
        self.hintButton.hide()
        self.hintPanel.addStretch()

        self.base_layout.addLayout(self.hintPanel, stretch=1)

        self.setLayout(self.base_layout)

    def ok(self):
        self.apply()
        self.cancel()

    def cancel(self):
        self.refresh_editor()
        if hasattr(self, "blk_widget"):
            self.blk_widget.deactivate_editor(self)

    def apply(self):
        # subclass pushes content to line_edit, then calls super().apply()
        try:
            self.on_apply()
            self.hint()
        except Exception as e:
            self.hint(str(e), "Failed to apply changes:", exc=e)
        self.refresh_editor()

    def refresh_editor(self):
        pass

    def hint(self, text="", header=None, exc=None):

        txt = f"<h4>{header}</h4>\n" if header else ""

        txt += text

        self.hintLabel.setText(txt)

        if exc is not None:
            self.hintButton.show()

            def raise_exc(*_, e=exc):
                raise e

            self.hintButton.clicked.connect(raise_exc)
        else:
            self.hintButton.hide()

    @staticmethod
    def hard_refresh(func):
        def decorated(self, *args, **kwargs):
            def pending(f=func, *a, **k):
                f(self,*a, **k)

            win = self.blk_widget.win
            blk = self.blk_widget.blk
            return win.hard_refresh(blk, func=pending, to_editor=self)
        return decorated

    def _hard_refresh(self, func:callable=lambda:None):
        from .comments import CommentEditorWidget

        blk = self.blk_widget.blk
        win = self.blk_widget.win

        if (self.blk_widget.active.count() > 1 and
            not win._custom_popup(
                "Warning!",
                "This change requires a refresh of the entire window for this block. "
                "You will lose changes made in other editor tabs.\n\n"
                "Continue?",
                ("Apply Changes and Refresh", True),
                cancel=True
        )):
            return

        func()

        win._set_flag(win.root_block, "refresh", True)
        win.go_to_block(win.root_block)

        blk_widget = win.find_widget(blk=blk, go_to=True)

        if isinstance(self, BasePropEditor):
            blk_widget.activate_prop_editor(self.prop_name)
        elif isinstance(self, CommentEditorWidget):
            blk_widget.activate_comment_editor(self.editor.prop_name)
        else:
            blk_widget.activate_misc_editor(self.editor_id)

        return blk_widget

apply_btn instance-attribute

apply_btn = QPushButton('Apply')

base_layout instance-attribute

base_layout = QVBoxLayout(self)

blk_widget instance-attribute

blk_widget = block_widget

cancel_btn instance-attribute

cancel_btn = QPushButton('Cancel')

editor instance-attribute

editor = editor_widget

hintButton instance-attribute

hintButton = QPushButton('More...')

hintLabel instance-attribute

hintLabel = QLabel()

hintPanel instance-attribute

hintPanel = QVBoxLayout()

okay_btn instance-attribute

okay_btn = QPushButton('OK')

__init__

__init__(block_widget, editor_widget)
Source code in FoSpy/ui/app/editors/_base.py
def __init__(self, block_widget, editor_widget):
    super().__init__()

    self.blk_widget = block_widget

    self.base_layout = QVBoxLayout(self)
    self.base_layout.setContentsMargins(0, 0, 0, 0)
    self.base_layout.setSpacing(10)

    self.editor = editor_widget
    self.refresh_editor()
    self.base_layout.addWidget(self.editor, stretch=0)

    self.setLayout(self.base_layout)

    button_layout = QHBoxLayout()
    button_layout.addStretch()

    self.apply_btn = QPushButton("Apply")
    self.apply_btn.clicked.connect(self.apply)
    button_layout.addWidget(self.apply_btn)

    self.okay_btn = QPushButton("OK")
    self.okay_btn.clicked.connect(self.ok)
    button_layout.addWidget(self.okay_btn)

    self.cancel_btn = QPushButton("Cancel")
    self.cancel_btn.clicked.connect(self.cancel)
    button_layout.addWidget(self.cancel_btn)

    self.base_layout.addLayout(button_layout, stretch=0)

    self.hintPanel = QVBoxLayout()
    self.hintLabel = QLabel()
    self.hintButton = QPushButton("More...")

    self.hintPanel.addWidget(self.hintLabel)
    self.hintPanel.addWidget(self.hintButton)
    self.hintButton.hide()
    self.hintPanel.addStretch()

    self.base_layout.addLayout(self.hintPanel, stretch=1)

    self.setLayout(self.base_layout)

_hard_refresh

_hard_refresh(func=lambda: None)
Source code in FoSpy/ui/app/editors/_base.py
def _hard_refresh(self, func:callable=lambda:None):
    from .comments import CommentEditorWidget

    blk = self.blk_widget.blk
    win = self.blk_widget.win

    if (self.blk_widget.active.count() > 1 and
        not win._custom_popup(
            "Warning!",
            "This change requires a refresh of the entire window for this block. "
            "You will lose changes made in other editor tabs.\n\n"
            "Continue?",
            ("Apply Changes and Refresh", True),
            cancel=True
    )):
        return

    func()

    win._set_flag(win.root_block, "refresh", True)
    win.go_to_block(win.root_block)

    blk_widget = win.find_widget(blk=blk, go_to=True)

    if isinstance(self, BasePropEditor):
        blk_widget.activate_prop_editor(self.prop_name)
    elif isinstance(self, CommentEditorWidget):
        blk_widget.activate_comment_editor(self.editor.prop_name)
    else:
        blk_widget.activate_misc_editor(self.editor_id)

    return blk_widget

apply

apply()
Source code in FoSpy/ui/app/editors/_base.py
def apply(self):
    # subclass pushes content to line_edit, then calls super().apply()
    try:
        self.on_apply()
        self.hint()
    except Exception as e:
        self.hint(str(e), "Failed to apply changes:", exc=e)
    self.refresh_editor()

cancel

cancel()
Source code in FoSpy/ui/app/editors/_base.py
def cancel(self):
    self.refresh_editor()
    if hasattr(self, "blk_widget"):
        self.blk_widget.deactivate_editor(self)

hard_refresh staticmethod

hard_refresh(func)
Source code in FoSpy/ui/app/editors/_base.py
@staticmethod
def hard_refresh(func):
    def decorated(self, *args, **kwargs):
        def pending(f=func, *a, **k):
            f(self,*a, **k)

        win = self.blk_widget.win
        blk = self.blk_widget.blk
        return win.hard_refresh(blk, func=pending, to_editor=self)
    return decorated

hint

hint(text='', header=None, exc=None)
Source code in FoSpy/ui/app/editors/_base.py
def hint(self, text="", header=None, exc=None):

    txt = f"<h4>{header}</h4>\n" if header else ""

    txt += text

    self.hintLabel.setText(txt)

    if exc is not None:
        self.hintButton.show()

        def raise_exc(*_, e=exc):
            raise e

        self.hintButton.clicked.connect(raise_exc)
    else:
        self.hintButton.hide()

ok

ok()
Source code in FoSpy/ui/app/editors/_base.py
def ok(self):
    self.apply()
    self.cancel()

refresh_editor

refresh_editor()
Source code in FoSpy/ui/app/editors/_base.py
def refresh_editor(self):
    pass

CommentEditorWidget

Bases: BaseEditorWidget

Source code in FoSpy/ui/app/editors/comments.py
class CommentEditorWidget(BaseEditorWidget):
    def __init__(self, block_widget, blk, prop_name, btn):
        super().__init__(block_widget,
            CommentsEditorPanel(blk, prop_name)
        )
        self.btn = btn
        self.editor.btn = btn
        self.on_apply = lambda: None
        self.base_layout.setStretchFactor(self.hintPanel, 0)

    def refresh_editor(self):
        self.editor.refresh_view()

    def apply(self):
        self.editor.send_comments()
        super().apply()

    def is_changed(self):
        blk_comments = self.editor.get_comments()

        editor_comments = list(self.editor.comment_rows.values())

        return blk_comments != editor_comments

btn instance-attribute

btn = btn

on_apply instance-attribute

on_apply = lambda: None

__init__

__init__(block_widget, blk, prop_name, btn)
Source code in FoSpy/ui/app/editors/comments.py
def __init__(self, block_widget, blk, prop_name, btn):
    super().__init__(block_widget,
        CommentsEditorPanel(blk, prop_name)
    )
    self.btn = btn
    self.editor.btn = btn
    self.on_apply = lambda: None
    self.base_layout.setStretchFactor(self.hintPanel, 0)

apply

apply()
Source code in FoSpy/ui/app/editors/comments.py
def apply(self):
    self.editor.send_comments()
    super().apply()

is_changed

is_changed()
Source code in FoSpy/ui/app/editors/comments.py
def is_changed(self):
    blk_comments = self.editor.get_comments()

    editor_comments = list(self.editor.comment_rows.values())

    return blk_comments != editor_comments

refresh_editor

refresh_editor()
Source code in FoSpy/ui/app/editors/comments.py
def refresh_editor(self):
    self.editor.refresh_view()

CommentsEditorPanel

Bases: QWidget

Source code in FoSpy/ui/app/editors/comments.py
class CommentsEditorPanel(QWidget):
    def __init__(self, blk, prop_name):
        super().__init__()

        self.blk = blk
        self.prop_name = prop_name
        self.comment_rows = {}
        self.row_layouts = {}

        layout = QVBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(layout)

        scroll = QScrollArea(self)
        scroll.setWidgetResizable(True)
        scroll.setFrameShape(QScrollArea.Shape.NoFrame)
        scroll.setContentsMargins(0, 0, 0, 0)
        scroll.setSizeAdjustPolicy(QScrollArea.SizeAdjustPolicy.AdjustToContents)

        scroll_content = QWidget()
        self.main_layout = QVBoxLayout(scroll_content)
        self.main_layout.setContentsMargins(0, 0, 0, 0)
        self.main_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
        scroll_content.setLayout(self.main_layout)

        scroll.setWidget(scroll_content)

        layout.addWidget(scroll, stretch=1)

        bottom_layout = QHBoxLayout()
        bottom_layout.setContentsMargins(0, 0, 0, 0)
        bottom_layout.addStretch()

        add_btn = QPushButton("Add Comment")
        add_btn.clicked.connect(lambda *_: self._add_comment())
        bottom_layout.addWidget(add_btn)

        clear_btn = QPushButton("Clear All")
        clear_btn.clicked.connect(lambda *_: self.clear_comments())
        bottom_layout.addWidget(clear_btn)

        layout.addLayout(bottom_layout)

        self.refresh_view()

    def get_comments(self):
        return self.blk._meta.comments.get(self.prop_name, [])

    def send_comments(self):
        self.blk._meta.comments[self.prop_name] = list(self.comment_rows.values())

    def delete_comment(self, comment_edit):
        row_layout = self.row_layouts[comment_edit]
        while row_layout.count() > 0:
            w = row_layout.itemAt(0).widget()
            if w:
                row_layout.removeWidget(w)
                w.deleteLater()
        self.main_layout.removeItem(row_layout)
        row_layout.deleteLater()
        self.comment_rows.pop(comment_edit, None)

        if hasattr(self, "btn") and self.comment_rows == {}:
            txt = self.btn.text()
            txt = txt.replace("!", "")
            self.btn.setText(txt)


    def set_comment(self, comment_edit):
        txt = comment_edit.text()
        self.comment_rows[comment_edit] = txt


    def _add_comment(self, txt=""):
        row_layout = QHBoxLayout()
        row_layout.setContentsMargins(0, 0, 0, 0)
        row_layout.addWidget(QLabel("//"), stretch=0)

        comment_edit = QLineEdit(txt)
        comment_edit.setContentsMargins(0, 0, 0, 0)
        comment_edit.editingFinished.connect(
            lambda *_, c=comment_edit: self.set_comment(c)
        )
        row_layout.addWidget(comment_edit, stretch=1)
        self.set_comment(comment_edit)

        del_btn = QPushButton("🗑")
        del_btn.clicked.connect(lambda *_, c=comment_edit: self.delete_comment(c))
        row_layout.addWidget(del_btn, stretch=0)

        add_btn = QPushButton("+^")
        add_btn.clicked.connect(
            lambda *_, i=len(self.comment_rows):
            self.insert_comment(idx=i)
        )
        row_layout.addWidget(add_btn, stretch=0)

        self.main_layout.addLayout(row_layout)
        self.row_layouts[comment_edit] = row_layout

        if hasattr(self, "btn"):
            txt = self.btn.text()
            if "!" not in txt:
                txt = "!" + txt
                self.btn.setText(txt)

    def insert_comment(self, txt="", idx=None):
        cut = []
        if idx is not None:
            for comment, text in zip(
                list(self.comment_rows.keys())[idx-1:],
                list(self.comment_rows.values())[idx-1:]):
                self.delete_comment(comment)
                cut.append(text)

        self._add_comment(txt)
        for text in cut:
            self._add_comment(text)

    def clear_comments(self):
        for comment in list(self.comment_rows.keys()):
            self.delete_comment(comment)

    def refresh_view(self):
        self.clear_comments()

        self.comment_rows = {}

        for txt in self.get_comments():
            self._add_comment(txt)

blk instance-attribute

blk = blk

comment_rows instance-attribute

comment_rows = {}

main_layout instance-attribute

main_layout = QVBoxLayout(scroll_content)

prop_name instance-attribute

prop_name = prop_name

row_layouts instance-attribute

row_layouts = {}

__init__

__init__(blk, prop_name)
Source code in FoSpy/ui/app/editors/comments.py
def __init__(self, blk, prop_name):
    super().__init__()

    self.blk = blk
    self.prop_name = prop_name
    self.comment_rows = {}
    self.row_layouts = {}

    layout = QVBoxLayout(self)
    layout.setContentsMargins(0, 0, 0, 0)
    self.setLayout(layout)

    scroll = QScrollArea(self)
    scroll.setWidgetResizable(True)
    scroll.setFrameShape(QScrollArea.Shape.NoFrame)
    scroll.setContentsMargins(0, 0, 0, 0)
    scroll.setSizeAdjustPolicy(QScrollArea.SizeAdjustPolicy.AdjustToContents)

    scroll_content = QWidget()
    self.main_layout = QVBoxLayout(scroll_content)
    self.main_layout.setContentsMargins(0, 0, 0, 0)
    self.main_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
    scroll_content.setLayout(self.main_layout)

    scroll.setWidget(scroll_content)

    layout.addWidget(scroll, stretch=1)

    bottom_layout = QHBoxLayout()
    bottom_layout.setContentsMargins(0, 0, 0, 0)
    bottom_layout.addStretch()

    add_btn = QPushButton("Add Comment")
    add_btn.clicked.connect(lambda *_: self._add_comment())
    bottom_layout.addWidget(add_btn)

    clear_btn = QPushButton("Clear All")
    clear_btn.clicked.connect(lambda *_: self.clear_comments())
    bottom_layout.addWidget(clear_btn)

    layout.addLayout(bottom_layout)

    self.refresh_view()

_add_comment

_add_comment(txt='')
Source code in FoSpy/ui/app/editors/comments.py
def _add_comment(self, txt=""):
    row_layout = QHBoxLayout()
    row_layout.setContentsMargins(0, 0, 0, 0)
    row_layout.addWidget(QLabel("//"), stretch=0)

    comment_edit = QLineEdit(txt)
    comment_edit.setContentsMargins(0, 0, 0, 0)
    comment_edit.editingFinished.connect(
        lambda *_, c=comment_edit: self.set_comment(c)
    )
    row_layout.addWidget(comment_edit, stretch=1)
    self.set_comment(comment_edit)

    del_btn = QPushButton("🗑")
    del_btn.clicked.connect(lambda *_, c=comment_edit: self.delete_comment(c))
    row_layout.addWidget(del_btn, stretch=0)

    add_btn = QPushButton("+^")
    add_btn.clicked.connect(
        lambda *_, i=len(self.comment_rows):
        self.insert_comment(idx=i)
    )
    row_layout.addWidget(add_btn, stretch=0)

    self.main_layout.addLayout(row_layout)
    self.row_layouts[comment_edit] = row_layout

    if hasattr(self, "btn"):
        txt = self.btn.text()
        if "!" not in txt:
            txt = "!" + txt
            self.btn.setText(txt)

clear_comments

clear_comments()
Source code in FoSpy/ui/app/editors/comments.py
def clear_comments(self):
    for comment in list(self.comment_rows.keys()):
        self.delete_comment(comment)

delete_comment

delete_comment(comment_edit)
Source code in FoSpy/ui/app/editors/comments.py
def delete_comment(self, comment_edit):
    row_layout = self.row_layouts[comment_edit]
    while row_layout.count() > 0:
        w = row_layout.itemAt(0).widget()
        if w:
            row_layout.removeWidget(w)
            w.deleteLater()
    self.main_layout.removeItem(row_layout)
    row_layout.deleteLater()
    self.comment_rows.pop(comment_edit, None)

    if hasattr(self, "btn") and self.comment_rows == {}:
        txt = self.btn.text()
        txt = txt.replace("!", "")
        self.btn.setText(txt)

get_comments

get_comments()
Source code in FoSpy/ui/app/editors/comments.py
def get_comments(self):
    return self.blk._meta.comments.get(self.prop_name, [])

insert_comment

insert_comment(txt='', idx=None)
Source code in FoSpy/ui/app/editors/comments.py
def insert_comment(self, txt="", idx=None):
    cut = []
    if idx is not None:
        for comment, text in zip(
            list(self.comment_rows.keys())[idx-1:],
            list(self.comment_rows.values())[idx-1:]):
            self.delete_comment(comment)
            cut.append(text)

    self._add_comment(txt)
    for text in cut:
        self._add_comment(text)

refresh_view

refresh_view()
Source code in FoSpy/ui/app/editors/comments.py
def refresh_view(self):
    self.clear_comments()

    self.comment_rows = {}

    for txt in self.get_comments():
        self._add_comment(txt)

send_comments

send_comments()
Source code in FoSpy/ui/app/editors/comments.py
def send_comments(self):
    self.blk._meta.comments[self.prop_name] = list(self.comment_rows.values())

set_comment

set_comment(comment_edit)
Source code in FoSpy/ui/app/editors/comments.py
def set_comment(self, comment_edit):
    txt = comment_edit.text()
    self.comment_rows[comment_edit] = txt