Skip to content

Commit

Permalink
Merge pull request #33 from wojtryb/development
Browse files Browse the repository at this point in the history
Deploy v1.1.1
  • Loading branch information
wojtryb authored Feb 4, 2023
2 parents 29608a1 + 2ec26b8 commit d505547
Show file tree
Hide file tree
Showing 13 changed files with 156 additions and 72 deletions.
23 changes: 23 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
name: Bug report
about: Create a report so that the bug can be fixed in future relase.
title: ''
labels: bug
assignees: wojtryb

---

**General info**
- Plugin version: ... [example: v1.1.0]
- Operating System: ... [example: Windows 10, Ubuntu 22.04, macOS Sierra]

**Bug description**
Describe the issue you encounter.
- If the plugin crashes and you get a traceback window - make sure to paste its content here.
- You can attach screenshots if it helps to describe your issue.

Ideally:
- check if the bug also occurs on the `development` branch (on github overview page, switch from 'main' to 'development', then download and install the plugin)
- try to break down when the bug happens (always/occasionally), and what is needed for it to happen

When the bug is fixed, close the issue. After not getting a response from you for a week, I will close it myself.
15 changes: 15 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"python.formatting.provider": "autopep8",
"python.linting.flake8Enabled": true,
"editor.formatOnSave": true,
"python.analysis.typeCheckingMode": "basic",
"python.analysis.diagnosticSeverityOverrides": {
"reportMissingImports": "none"
},
"[python]": {
"editor.rulers": [
72,
79
],
},
}
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Change Log

## [1.1.1] - 2023-02-04
### Fixed
- Fix a crash when any PieMenu is empty.
- Support multiple krita windows.
- Fix transform modes ocasionally stopping to work.
- Handle invalid configuration.
- Add missing icon for transform tool.

## [1.1.0] - 2023-01-20
### Added
- Edit mode for PieMenus - click or drag the value icon to enter it. While in edit mode, the values can be dragged across the PieMenu to change their order.
Expand Down
21 changes: 7 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,16 @@ The plugin adds new shortcuts of the following types:

[![PIE MENUS - introducing Shortcut Composer](http://img.youtube.com/vi/hrjBycVYFZM/0.jpg)](https://www.youtube.com/watch?v=hrjBycVYFZM "PIE MENUS - introducing Shortcut Composer")

## What's new in v1.1.0
### Added
- Edit mode for PieMenus - click or drag the value icon to enter it. While in edit mode, the values can be dragged across the PieMenu to change their order.
- Action values tab in Configure Shortcut Composer for adding and removing values in PieMenus and MultipleAssignment actions.
- PieValues backtrounds are now animated.
- New PieMenu to create a layer with chosen blending mode.

## What's new in v1.1.1
### Fixed
- Allow scrolling through masks while using layey mouse trackers
- Make sure that presets in PieMenu are displayed only once.
- Allow using "Enclose and fill" tool
- Fix icon of "Colorize Mask" tool
- Fix icon of "Colorize Mask" and "Color Sampler" tools
- Fix scaling issues of Text labels in PieMenu
- Fix a crash when any PieMenu is empty.
- Support multiple krita windows.
- Fix transform modes ocasionally stopping to work.
- Handle invalid configuration.
- Add missing icon for transform tool.

## Requirements
Shortcut Composer **v1.1.0** Requires krita **5.1.0** or later.
Shortcut Composer **v1.1.1** Requires krita **5.1.0** or later.

OS support state:
- [x] Windows 10/11
Expand Down
20 changes: 12 additions & 8 deletions shortcut_composer/api_krita/actions/transform_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ class TransformModeActions:
Tools are available as krita actions, but not added to the toolbar.
"""

def __init__(self) -> None:
def __init__(self, window) -> None:
self._finder = TransformModeFinder()
self._actions: Dict[TransformMode, QWidgetAction] = {}
self._create_actions(window)

def create_actions(self, window) -> None:
def _create_actions(self, window) -> None:
"""Create krita actions which activate new tools."""
_ACTION_MAP = {
TransformMode.FREE: self.set_free,
Expand Down Expand Up @@ -78,27 +79,30 @@ class TransformModeFinder:
- Button used to apply the changes of transform tool
As the widget do not exist during plugin intialization phase,
fetching the elements needs to happen at runtime.
fetching the elements needs to happen at runtime. If wrappers get
deleted by C++, the fetching may need to be done again.
"""

def __init__(self) -> None:
self._mode_buttons: Dict[TransformMode, QToolButton] = {}
self._initialized = False

self._transform_options: QWidget
self._apply_button: QPushButton

def ensure_initialized(self, mode: TransformMode) -> None:
"""Fetch widget, apply and mode buttons if not done already."""
if not self._initialized:
try:
self._transform_options.size()
self._apply_button.size()
except (RuntimeError, AttributeError):
last_tool = Krita.active_tool
Krita.active_tool = Tool.TRANSFORM
self._transform_options = self._fetch_transform_options()
self._apply_button = self._fetch_apply_button()
self._initialized = True
Krita.active_tool = last_tool

if mode not in self._mode_buttons:
try:
self._mode_buttons[mode].size()
except (RuntimeError, KeyError):
self._mode_buttons[mode] = self._fetch_mode_button(mode)

def activate_mode(self, mode: TransformMode, apply: bool) -> None:
Expand Down
1 change: 1 addition & 0 deletions shortcut_composer/api_krita/enums/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ def icon(self) -> QIcon:
Tool.FREEHAND_SELECTION: "tool_outline_selection",
Tool.GRADIENT: "krita_tool_gradient",
Tool.LINE: "krita_tool_line",
Tool.TRANSFORM: "krita_tool_transform",
Tool.MOVE: "krita_tool_move",
Tool.RECTANGULAR_SELECTION: "tool_rect_selection",
Tool.CONTIGUOUS_SELECTION: "tool_contiguous_selection",
Expand Down
10 changes: 9 additions & 1 deletion shortcut_composer/api_krita/wrappers/tool_descriptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,24 @@ class ToolFinder:
def __init__(self) -> None:
"""Remember the reference to toolbox krita object."""
self.instance = Api.instance()
self.toolbox = self._init_toolbox()
self.toolbox: QWidget

def find_active_tool_name(self) -> str:
"""Find and return name of currently active tool."""
self._ensure_toolbox()
for qobj in self.toolbox.findChildren(QToolButton):
if qobj.metaObject().className() == "KoToolBoxButton":
if qobj.isChecked():
return qobj.objectName()
raise RuntimeError("No active tool found.")

def _ensure_toolbox(self):
"""Fetch toolbox if it was not fetched or got deleted."""
try:
self.toolbox.size()
except (RuntimeError, AttributeError):
self.toolbox = self._init_toolbox()

def _init_toolbox(self) -> QWidget:
"""Find and return reference to unwrapped toolbox object."""
qwindow = self.instance.activeWindow().qwindow()
Expand Down
9 changes: 7 additions & 2 deletions shortcut_composer/composer_utils/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,16 @@ def default(self) -> Union[float, int, str]:

def read(self) -> Any:
"""Read current value from krita config file."""
return type(self.default)(Krita.read_setting(
setting = Krita.read_setting(
group="ShortcutComposer",
name=self.value,
default=str(self.default),
))
)
try:
return type(self.default)(setting)
except ValueError:
print(f"Can't parse {setting} to {type(self.default)}")
return self.default

def write(self, value: Any) -> None:
"""Write given value to krita config file."""
Expand Down
6 changes: 4 additions & 2 deletions shortcut_composer/composer_utils/utils/action_values.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class ActionValues(QWidget):
def __init__(self, allowed_values: Set[str], config: Config) -> None:
super().__init__()
layout = QHBoxLayout()
self.allowed_values = set(allowed_values)
self.allowed_values = allowed_values
self.config = config

self.available_list = ValueList(movable=False, parent=self)
Expand Down Expand Up @@ -84,7 +84,9 @@ def refresh(self):
current_list = current.split("\t")
if current_list == ['']:
current_list = []
self.current_list.addItems(current_list)
for item in current_list:
if item in self.allowed_values:
self.current_list.addItem(item)

self.available_list.clear()
allowed_items = sorted(self.allowed_values - set(current_list))
Expand Down
5 changes: 4 additions & 1 deletion shortcut_composer/data_components/writable_values.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,7 @@ def __init__(self, values: Config, enum_type: Type[T]) -> None:
if value_string == '':
return
values_list = value_string.split("\t")
self.extend([enum_type[value] for value in values_list])
try:
self.extend([enum_type[value] for value in values_list])
except KeyError:
print(f"{values_list} not in {enum_type}")
23 changes: 7 additions & 16 deletions shortcut_composer/manual.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,17 @@ <h1 id="shortcut-composer">Shortcut composer</h1>
<li><code>Temporary key</code> - temporarily activates a krita property with long press or toggles it on/off with
short press.</li>
</ul>
<h2 id="what-s-new-in-v1-1-0">What&#39;s new in v1.1.0</h2>
<h3 id="added">Added</h3>
<ul>
<li>Edit mode for PieMenus - click or drag the value icon to enter it. While in edit mode, the values can be dragged
across the PieMenu to change their order.</li>
<li>Action values tab in Configure Shortcut Composer for adding and removing values in PieMenus and
MultipleAssignment
actions.</li>
<li>PieValues backtrounds are now animated.</li>
</ul>
<h2 id="what-s-new-in-v1-1-1">What&#39;s new in v1.1.1</h2>
<h3 id="fixed">Fixed</h3>
<ul>
<li>Allow scrolling through masks while using layey mouse trackers </li>
<li>Make sure that presets in PieMenu are displayed only once.</li>
<li>Allow using &quot;Enclose and fill&quot; tool</li>
<li>Fix icon of &quot;Colorize Mask&quot; tool</li>
<li>Fix scaling issues of Text labels in PieMenu</li>
<li>Fix a crash when any PieMenu is empty.</li>
<li>Support multiple krita windows.</li>
<li>Fix transform modes ocasionally stopping to work.</li>
<li>Handle invalid configuration.</li>
<li>Add missing icon for transform tool.</li>
</ul>
<h2 id="requirements">Requirements</h2>
<p>Shortcut Composer <strong>v1.1.0</strong> Requires krita <strong>5.1.0</strong> or later.</p>
<p>Shortcut Composer <strong>v1.1.1</strong> Requires krita <strong>5.1.0</strong> or later.</p>
<p>OS support state:</p>
<ul>
<li>[x] Windows 10/11</li>
Expand Down
80 changes: 54 additions & 26 deletions shortcut_composer/shortcut_composer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# SPDX-FileCopyrightText: © 2022 Wojciech Trybus <[email protected]>
# SPDX-License-Identifier: GPL-3.0-or-later

from typing import List
from dataclasses import dataclass
from PyQt5.QtWidgets import QWidgetAction

from api_krita import Krita, Extension # type: ignore
Expand All @@ -10,35 +12,52 @@
from input_adapter import ActionManager


@dataclass
class GarbageProtector:
"""
Stores plugin objects, to protect them from garbage collector.
- TransformModeActions which create and store actions for transform modes
- QDialog with plugin settings,
- Action for displaying the settings dialog above
- Manager for complex actions which which holds and binds them to krita
- Action for reloading the complex action implementations
"""
transform_modes: TransformModeActions
settings_dialog: SettingsDialog
settings_action: QWidgetAction
action_manager: ActionManager
reload_action: QWidgetAction

def is_alive(self):
"""Return False if the action was deleted by C++"""
try:
self.settings_action.isEnabled()
except RuntimeError:
return False
return True


class ShortcutComposer(Extension):
"""Krita extension that adds complex actions invoked with keyboard."""

_pie_settings_dialog: SettingsDialog
_settings_action: QWidgetAction
_reload_action: QWidgetAction
_manager: ActionManager
def __init__(self, parent) -> None:
"""Add callback to reload actions on theme change."""
super().__init__(parent)
self._protectors: List[GarbageProtector] = []
Krita.add_theme_change_callback(self._reload_composer)

def setup(self) -> None: """Obligatory abstract method override."""

def createActions(self, window) -> None:
"""
Start the extension. Called by krita during plugin init phase.
"""Create window components. Called by krita for each new window."""
self._protectors.append(GarbageProtector(
transform_modes=TransformModeActions(window),
settings_dialog=(settings := SettingsDialog()),
settings_action=self._create_settings_action(window, settings),
action_manager=ActionManager(window),
reload_action=self._create_reload_action(window)))

- Create usual actions for transform modes using `TransformModeActions`
- Create usual actions for reloading the extension and settings dialog
- Add a callback to reload plugin when krita theme changes
- Create complex action manager which holds and binds them to krita
"""
self._transform_modes = TransformModeActions()
self._transform_modes.create_actions(window)

self._pie_settings_dialog = SettingsDialog()
self._reload_action = self._create_reload_action(window)
self._settings_action = self._create_settings_action(window)

Krita.add_theme_change_callback(self._reload_composer)

self._manager = ActionManager(window)
self._reload_composer()

def _create_reload_action(self, window) -> QWidgetAction:
Expand All @@ -49,15 +68,24 @@ def _create_reload_action(self, window) -> QWidgetAction:
group="tools/scripts",
callback=self._reload_composer)

def _create_settings_action(self, window) -> QWidgetAction:
def _create_settings_action(
self,
window,
settings_dialog: SettingsDialog
) -> QWidgetAction:
"""Create krita action which opens the extension settings dialog."""
return Krita.create_action(
window=window,
name="Configure Shortcut Composer",
group="tools/scripts",
callback=self._pie_settings_dialog.show)
callback=settings_dialog.show)

def _reload_composer(self) -> None:
"""Reload all core actions."""
for action in create_actions():
self._manager.bind_action(action)
"""Reload all core actions for every window."""
for protector in reversed(self._protectors):
if not protector.is_alive():
self._protectors.remove(protector)

for protector in self._protectors:
for action in create_actions():
protector.action_manager.bind_action(action)
7 changes: 5 additions & 2 deletions shortcut_composer/templates/pie_menu_utils/pie_style.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,11 @@ def _pick_icon_radius(self) -> int:
icon_radius: int = round(
50 * self._base_size
* self.icon_radius_scale
* Config.PIE_ICON_GLOBAL_SCALE.read()
)
* Config.PIE_ICON_GLOBAL_SCALE.read())

if not self._icons_amount:
return icon_radius

max_icon_size = round(self.pie_radius * math.pi / self._icons_amount)
return min(icon_radius, max_icon_size)

Expand Down

0 comments on commit d505547

Please sign in to comment.