Coverage for paintmaskanimator/main_window_ui_build.py: 97%
694 statements
« prev ^ index » next coverage.py v7.17.0, created at 2026-11-02 04:50 +0000
« prev ^ index » next coverage.py v7.17.0, created at 2026-11-02 04:50 +0000
1"""Window construction for MainWindow: actions, menus, layout and signal wiring.
3Split out of ``main_window.py`` as a mixin. These methods build the UI itself --
4the ``QAction`` set and shortcut actions, the menu bar, the dock/canvas layout in
5``build_ui``, the action panel, and the ``connect`` pass that wires every widget
6signal to its handler. They run once during ``MainWindow.__init__`` (plus on
7theme changes) against a live ``MainWindow`` instance, and hold no state of their
8own -- everything is assigned onto ``self``.
9"""
10from typing import Any
11from PySide6.QtCore import QSize, QTimer, Qt
12from PySide6.QtGui import QAction, QGuiApplication, QKeySequence
13from PySide6.QtWidgets import (
14 QFrame,
15 QHBoxLayout,
16 QLabel,
17 QMessageBox,
18 QPushButton,
19 QScrollArea,
20 QSlider,
21 QSplitter,
22 QVBoxLayout,
23 QWidget,
24)
25from .constants import APP_DISPLAY_NAME, APP_NAME, GITHUB_REPO, HOLD_ZOOM_SHORTCUT
26from ._main_window_members import MainWindowMembers
27import PySide6QtAds as QtAds
28from . import icons, theme, view_preview
29from .i18n import tr
30from .main_window_view_preview import channel_labels
31from .preview_controls import ChannelPreviewButton, ExposureControl
32from .theme import StatusBar
33from .preferences_dialog import PreferencesDialog
34from .tool_window import ToolWindow
35from .toolpanel import ToolPanel, tool_label
36from .errors import OPERATION_ERRORS
37from .logging_setup import get_logger
39log = get_logger(__name__)
41_OPERATION_ERRORS = OPERATION_ERRORS
44class UIBuildMixin(MainWindowMembers):
45 def make_shortcut_action(self, name, callback, shortcut=""):
46 action = QAction(name, self)
47 if shortcut:
48 action.setShortcut(shortcut)
49 action.setShortcutContext(Qt.ShortcutContext.ApplicationShortcut)
50 action.triggered.connect(callback)
51 self.addAction(action)
52 return action
54 def build_action_panel(self):
55 # Built-in actions are now editable default scripts seeded into the
56 # actions folder and registered via ActionPanel.reload_python_actions().
57 # See paintmaskanimator.actionpanel.BUILTIN_SCRIPTS.
58 pass
60 def build_actions(self):
61 self.a_new=QAction(tr("新規作成…"),self);self.a_new.setShortcut("Ctrl+N");self.a_new.triggered.connect(self.new_doc)
62 self.a_resize=QAction(tr("キャンバスサイズの変更…"),self);self.a_resize.triggered.connect(self.resize_doc)
63 self.a_undo=QAction(tr("元に戻す"),self);self.a_undo.setShortcut("Ctrl+Z");self.a_undo.triggered.connect(self.used_color.undo)
64 self.a_redo=QAction(tr("やり直す"),self);self.a_redo.setShortcuts([QKeySequence("Ctrl+Y"),QKeySequence("Ctrl+Shift+Z")]);self.a_redo.triggered.connect(self.used_color.redo)
65 self.a_copy=QAction(tr("コピー"),self);self.a_copy.setShortcut("Ctrl+C");self.a_copy.triggered.connect(self.canvas.copy_selection)
66 self.a_cut=QAction(tr("切り取り"),self);self.a_cut.setShortcut("Ctrl+X");self.a_cut.triggered.connect(self.canvas.cut_selection)
67 self.a_paste=QAction(tr("貼り付け"),self);self.a_paste.setShortcut("Ctrl+V");self.a_paste.triggered.connect(self.paste_from_clipboard)
68 self.a_open_project=QAction(tr("プロジェクトを開く…"),self)
69 self.a_open_project.setShortcut("Ctrl+O")
70 self.a_open_project.triggered.connect(self.project.open_dialog)
72 self.a_import_images=QAction(tr("画像を読み込む…"),self)
73 self.a_import_images.triggered.connect(self.import_images_dialog)
74 self.a_import_folder=QAction(tr("画像フォルダーを読み込む…"),self)
75 self.a_import_folder.triggered.connect(self.import_image_folder_dialog)
76 self.a_import_images_raw=QAction(tr("変換せず読み込む(下書きレイヤー)…"),self)
77 self.a_import_images_raw.triggered.connect(self.import_images_raw_dialog)
78 self.a_import_psd=QAction(tr("PSDを読み込む…"),self)
79 self.a_import_psd.triggered.connect(self.importer.psd_dialog)
80 self.a_import_clip=QAction(tr("CLIP STUDIOアニメーションを読み込む…"),self)
81 self.a_import_clip.triggered.connect(self.importer.clip_animation_dialog)
82 self.a_open_cut_folder=QAction(tr("カットフォルダーを開く…"),self)
83 self.a_open_cut_folder.triggered.connect(self.importer.cut_folder_dialog)
85 self.a_save_project=QAction(tr("上書き保存"),self)
86 self.a_save_project.setShortcut("Ctrl+S")
87 self.a_save_project.triggered.connect(self.project.save)
89 self.a_save_project_as=QAction(tr("名前を付けて保存…"),self)
90 self.a_save_project_as.setShortcut("Ctrl+Shift+S")
91 self.a_save_project_as.triggered.connect(self.project.save_as)
93 self.a_save=QAction(tr("現在のコマをPNG書き出し…"),self)
94 self.a_save.triggered.connect(self.save_png)
95 self.a_save_tga=QAction(tr("現在のコマをTGA書き出し…"),self)
96 self.a_save_tga.triggered.connect(self.save_tga)
97 self.a_export_png_seq=QAction(tr("連番PNG+CSV書き出し…"),self);self.a_export_png_seq.triggered.connect(lambda:self.export.key_sequence("PNG"))
98 self.a_export_tga_seq=QAction(tr("連番TGA+CSV書き出し…"),self);self.a_export_tga_seq.triggered.connect(lambda:self.export.key_sequence("TGA"))
99 self.a_export_cut_folder=QAction(tr("カットフォルダーへ書き出し…"),self)
100 self.a_export_cut_folder.triggered.connect(self.export.cut_folder)
101 self.a_export_shadow=QAction(tr("影素材を書き出し…"),self)
102 self.a_export_shadow.triggered.connect(self.export.shadow_sequence)
103 self.a_export_xdts=QAction(tr("XDTSタイムシートを書き出す…"),self)
104 self.a_export_xdts.triggered.connect(self.export.xdts_dialog)
105 self.a_export_psd=QAction(tr("PSDを書き出す…"),self)
106 self.a_export_psd.triggered.connect(self.export.psd_dialog)
107 self.a_prev=QAction(tr("前のフレーム"),self);self.a_prev.setShortcut("1");self.a_prev.triggered.connect(self.timeline_ops.previous_frame)
108 self.a_next=QAction(tr("次のフレーム"),self);self.a_next.setShortcut("2");self.a_next.triggered.connect(self.timeline_ops.next_frame)
109 self.a_pressure=QAction(tr("筆圧設定…"),self);self.a_pressure.triggered.connect(self.pressure)
110 self.a_memo = self.make_shortcut_action(
111 tr("メモを開く"), self.show_memo_window
112 )
113 self.a_isolate_color=QAction(tr("選択色だけ表示"),self)
114 self.a_isolate_color.triggered.connect(self.colors.isolate_selected_color)
115 self.a_clear_color_filter=QAction(tr("特定色表示を解除"),self)
116 self.a_clear_color_filter.triggered.connect(self.colors.clear_selected_color_filter)
117 self.a_silhouette=QAction(tr("背景以外を黒シルエット表示"),self); self.a_silhouette.setCheckable(True); self.a_silhouette.setShortcut("Ctrl+B")
118 self.a_silhouette.triggered.connect(self.line_ops.toggle_silhouette)
119 self.a_preview_map=QAction(tr("隣接色分けで表示"),self); self.a_preview_map.setCheckable(True)
120 self.a_preview_map.triggered.connect(self.view_preview.set_map_colors)
121 self.a_exposure_toggle=QAction(tr("露光量の調整を切り替え"),self)
122 self.a_exposure_toggle.triggered.connect(self.view_preview.toggle_exposure)
123 self.a_channel_red=QAction(tr("赤チャンネルを表示"),self)
124 self.a_channel_red.triggered.connect(lambda: self.view_preview.set_channel("red"))
125 self.a_channel_green=QAction(tr("緑チャンネルを表示"),self)
126 self.a_channel_green.triggered.connect(lambda: self.view_preview.set_channel("green"))
127 self.a_channel_blue=QAction(tr("青チャンネルを表示"),self)
128 self.a_channel_blue.triggered.connect(lambda: self.view_preview.set_channel("blue"))
129 self.a_channel_alpha=QAction(tr("アルファチャンネルを表示"),self)
130 self.a_channel_alpha.triggered.connect(lambda: self.view_preview.set_channel("alpha"))
131 self.a_remove_dust=QAction(tr("ゴミ取り/塗り抜け…"),self)
132 self.a_remove_dust.triggered.connect(self.line_ops.remove_dust_fill_surrounding)
133 self.a_auto_paint=QAction(tr("自動で塗る…"),self)
134 self.a_auto_paint.setStatusTip(tr(
135 "黒と赤・緑・青などの色トレス線を境界にして、見本のセルから同じレイヤーのほかのセルを"
136 "塗ります。"
137 ))
138 self.a_auto_paint.triggered.connect(self.auto_paint.open)
139 self.a_keep_initial_state=QAction(tr("今の絵を初期状態として保持"),self)
140 self.a_keep_initial_state.setStatusTip(tr(
141 "全レイヤーの今の絵を、色塗り前の初期状態として保持します。自動塗りは初期状態の"
142 "線で部分を分けるので、精度が上がります。"
143 ))
144 self.a_keep_initial_state.triggered.connect(lambda: self.initial_state.keep())
145 self.a_import_initial_state=QAction(tr("線画カットフォルダーを初期状態として読み込む…"),self)
146 self.a_import_initial_state.setStatusTip(tr(
147 "レイヤー名とセル番号が一致する線画を初期状態に設定します。今の絵とタイムラインは維持します。"
148 ))
149 self.a_import_initial_state.triggered.connect(self.initial_state.import_line_art_dialog)
150 self.a_import_clip_initial_state=QAction(tr("CLIPの線画を初期状態として読み込む…"),self)
151 self.a_import_clip_initial_state.setStatusTip(tr(
152 "CLIPのフォルダー名とセル番号が一致する線画を初期状態に設定します。今の絵とタイムラインは維持します。"
153 ))
154 self.a_import_clip_initial_state.triggered.connect(self.initial_state.import_clip_line_art_dialog)
155 self.a_clear_initial_state=QAction(tr("保持した初期状態を消す"),self)
156 self.a_clear_initial_state.triggered.connect(self.initial_state.clear)
157 self.a_reference_check=QAction(tr("見本との違いチェック…"),self)
158 self.a_reference_check.triggered.connect(self.reference_check.run)
159 self.a_shortcuts=QAction(tr("ショートカット設定…"),self);self.a_shortcuts.triggered.connect(self.shortcuts)
160 # macOS ではアプリ名のメニューへ移る(⌘,)。
161 self.a_preferences=QAction(tr("環境設定…"),self)
162 self.a_preferences.setMenuRole(QAction.MenuRole.PreferencesRole)
163 self.a_preferences.setShortcut("Ctrl+,")
164 self.a_preferences.triggered.connect(lambda: self.show_preferences())
165 self.tool_actions={}
166 defaults={"brush":"P","line":"U","shape":"O","bucket":"G","closed_fill":"K","lasso_fill":"F","lasso":"L","rect_select":"R","auto_select":"W","eyedropper":"I","dust":"D"}
167 for tid,_source_label in ToolPanel.TOOLS:
168 a=QAction(tr("ツール:{tool}").format(tool=tool_label(tid)),self);a.setShortcut(defaults.get(tid,""));a.setShortcutContext(Qt.ShortcutContext.ApplicationShortcut);a.triggered.connect(lambda _=False,t=tid,action=a:self.tool_hold.select(t,action));self.addAction(a);self.tool_actions[tid]=a
169 self.general_actions=[(tr("新規作成"),self.a_new),(tr("プロジェクトを開く"),self.a_open_project),(tr("上書き保存"),self.a_save_project),(tr("元に戻す"),self.a_undo),(tr("やり直す"),self.a_redo),(tr("前のフレーム"),self.a_prev),(tr("次のフレーム"),self.a_next)]
170 self.tool_action_list=[
171 (tr("ツール:{tool}").format(tool=tool_label(tid)), self.tool_actions[tid])
172 for tid, _source_label in ToolPanel.TOOLS
173 ]
175 # 押している間だけ有効になるキャンバス操作。QActionは設定値の保持に使い、
176 # 通常のアプリケーションショートカットとしては登録しない。
177 self.a_hold_hand = QAction(tr("ハンド(押している間)"), self)
178 self.a_hold_hand.setProperty("holdOperation", True)
179 self.a_hold_hand.setProperty("holdShortcutText", "Space")
180 self.a_hold_hand.setShortcut(QKeySequence("Space"))
181 self.a_hold_zoom = QAction(tr("拡大縮小(押している間)"), self)
182 self.a_hold_zoom.setProperty("holdOperation", True)
183 self.a_hold_zoom.setProperty("holdShortcutText", HOLD_ZOOM_SHORTCUT)
184 self.a_hold_zoom.setShortcut(QKeySequence(HOLD_ZOOM_SHORTCUT))
185 self.a_hold_rotate = QAction(tr("回転(押している間)"), self)
186 self.a_hold_rotate.setProperty("holdOperation", True)
187 self.a_hold_rotate.setProperty("holdShortcutText", "Shift+Space")
188 self.a_hold_rotate.setShortcut(QKeySequence("Shift+Space"))
189 self.a_hold_eyedropper = QAction(tr("スポイト(押している間)"), self)
190 self.a_hold_eyedropper.setProperty("holdOperation", True)
191 self.a_hold_eyedropper.setProperty("holdShortcutText", "Alt")
192 self.a_hold_eyedropper.setShortcut(QKeySequence("Alt"))
193 self.canvas_operation_actions = [
194 (tr("ハンド"), self.a_hold_hand),
195 (tr("拡大縮小"), self.a_hold_zoom),
196 (tr("回転"), self.a_hold_rotate),
197 (tr("スポイト"), self.a_hold_eyedropper),
198 ]
200 # File and edit actions not previously exposed in the shortcut dialog.
201 for action in (
202 self.a_new, self.a_open_project, self.a_open_cut_folder,
203 self.a_import_images,
204 self.a_save_project, self.a_save_project_as,
205 self.a_resize, self.a_undo, self.a_redo,
206 self.a_cut, self.a_copy, self.a_paste, self.a_save, self.a_save_tga,
207 self.a_export_png_seq, self.a_export_tga_seq, self.a_export_cut_folder,
208 self.a_export_shadow,
209 self.a_prev, self.a_next,
210 self.a_pressure, self.a_isolate_color, self.a_clear_color_filter,
211 self.a_silhouette, self.a_remove_dust, self.a_preview_map,
212 self.a_exposure_toggle, self.a_channel_red, self.a_channel_green,
213 self.a_channel_blue, self.a_channel_alpha,
214 ):
215 action.setShortcutContext(Qt.ShortcutContext.ApplicationShortcut)
217 self.a_export_mp4 = self.make_shortcut_action(
218 tr("MP4書き出し"), self.export.mp4
219 )
221 # Tool-panel commands.
222 self.a_select_main = self.make_shortcut_action(
223 tr("描画色:メインを選択"), lambda: self.colors.set_color_mode("main")
224 )
225 self.a_select_sub = self.make_shortcut_action(
226 tr("描画色:サブを選択"), lambda: self.colors.set_color_mode("sub")
227 )
228 self.a_select_transparent = self.make_shortcut_action(
229 tr("描画色:背景色を選択"), self.colors.select_background_color, "E"
230 )
231 self.a_toggle_draw_background = self.make_shortcut_action(
232 tr("描画色と背景色を切り替え"), self.colors.toggle_draw_background_color, "C"
233 )
234 self.a_swap_main_sub = self.make_shortcut_action(
235 tr("メイン色とサブ色を切り替え"), self.line_ops.swap_main_sub, "X"
236 )
237 self.a_choose_background = self.make_shortcut_action(
238 tr("背景色の表示色を変更"), self.colors.choose_background_color
239 )
240 self.a_mainline_repaint = self.make_shortcut_action(
241 "MainLineRepaint", self.line_ops.main_line_repaint
242 )
243 self.a_selection_transform = self.make_shortcut_action(
244 tr("選択範囲:自由変形"), lambda: self.line_ops.start_wire_transform("free"),
245 "Ctrl+T",
246 )
247 self.a_selection_scale = self.make_shortcut_action(
248 tr("選択範囲:拡大縮小"), lambda: self.line_ops.start_wire_transform("scale")
249 )
250 self.a_selection_mesh = self.make_shortcut_action(
251 tr("選択範囲:メッシュ変形"), lambda: self.line_ops.start_wire_transform("mesh")
252 )
253 self.a_selection_clear = self.make_shortcut_action(
254 tr("選択範囲を解除"), self.canvas.clear_selection, "Ctrl+D"
255 )
256 # これまでの Ctrl+Shift+A でも解除できるよう、2つ目のキーとして残す。
257 self.a_selection_clear.setShortcuts([
258 QKeySequence("Ctrl+D"), QKeySequence("Ctrl+Shift+A"),
259 ])
260 self.a_selection_all = self.make_shortcut_action(
261 tr("すべてを選択"), self.canvas.select_all, "Ctrl+A"
262 )
263 self.a_selection_invert = self.make_shortcut_action(
264 tr("選択範囲を反転"), self.canvas.invert_selection, "Ctrl+Shift+I"
265 )
266 self.a_selection_delete = self.make_shortcut_action(
267 tr("選択範囲の中を消去"), self.canvas.delete_selection_contents, "Delete"
268 )
269 # Mac のキーボードの delete キーは Backspace として届く。
270 self.a_selection_delete.setShortcuts([
271 QKeySequence("Delete"), QKeySequence("Backspace"),
272 ])
273 self.a_view_zoom_in = self.make_shortcut_action(
274 tr("表示:拡大"), lambda: self.step_canvas_zoom(1.25), "Ctrl+="
275 )
276 # 「Ctrl++」は配列によって Ctrl+= や Ctrl+Shift+= で届くので両方受ける。
277 self.a_view_zoom_in.setShortcuts([
278 QKeySequence("Ctrl+="), QKeySequence("Ctrl++"),
279 ])
280 self.a_view_zoom_out = self.make_shortcut_action(
281 tr("表示:縮小"), lambda: self.step_canvas_zoom(1 / 1.25), "Ctrl+-"
282 )
283 self.a_view_fit = self.make_shortcut_action(
284 tr("表示:全体を表示"), self.fit_canvas, "Ctrl+0"
285 )
286 self.a_brush_smaller = self.make_shortcut_action(
287 tr("ブラシサイズを小さく"), lambda: self.tools.step_brush_size(-1), "["
288 )
289 self.a_brush_larger = self.make_shortcut_action(
290 tr("ブラシサイズを大きく"), lambda: self.tools.step_brush_size(1), "]"
291 )
292 self.a_transform_rotate_left = self.make_shortcut_action(
293 tr("変形:左へ90°回転"),
294 lambda: self.canvas.rotate_selection_transform(-90.0)
295 )
296 self.a_transform_rotate_right = self.make_shortcut_action(
297 tr("変形:右へ90°回転"),
298 lambda: self.canvas.rotate_selection_transform(90.0)
299 )
300 self.a_transform_mesh_grid = self.make_shortcut_action(
301 tr("メッシュ変形:格子数を変更…"),
302 self.line_ops.choose_transform_mesh_grid
303 )
304 self.a_transform_commit = self.make_shortcut_action(
305 tr("変形を確定"), self.tween.commit_transform_or_tween
306 )
307 self.a_transform_commit.setShortcuts([
308 QKeySequence(Qt.Key.Key_Return),
309 QKeySequence(Qt.Key.Key_Enter),
310 ])
311 self.a_transform_cancel = self.make_shortcut_action(
312 tr("変形をキャンセル"), self.tween.cancel_transform_or_tween
313 )
314 self.a_focus_used_color = self.make_shortcut_action(
315 tr("使用色:この色を選択範囲にする"),
316 self.colors.focus_selected_used_color,
317 )
318 self.a_bucket_include_sub = self.make_shortcut_action(
319 tr("バケツ:含み塗り ON/OFF"),
320 lambda: self.tools.bucket_include_sub.toggle()
321 )
322 self.a_bucket_close_gap = self.make_shortcut_action(
323 tr("バケツ:隙間閉じ ON/OFF"),
324 lambda: self.tools.bucket_close_gap.toggle()
325 )
326 self.a_bucket_all_frames = self.make_shortcut_action(
327 tr("バケツ:串刺し塗り ON/OFF"),
328 lambda: self.tools.bucket_all_frames.toggle()
329 )
330 self.a_dust_all_frames = self.make_shortcut_action(
331 tr("ゴミ取り:すべてのコマ ON/OFF"),
332 lambda: self.tools.dust_all_frames.toggle()
333 )
334 self.a_dust_apply = self.make_shortcut_action(
335 tr("ゴミ取り/塗り抜けを適用"), self.line_ops.remove_dust_fill_surrounding
336 )
337 self.a_selection_all_frames = self.make_shortcut_action(
338 tr("選択変形:すべてのコマ ON/OFF"),
339 self.tools.toggle_selection_all_frames
340 )
342 # Timeline commands.
343 self.a_tl_add_exposure = self.make_shortcut_action(
344 tr("タイムライン:コマ数を1つ増やす"),
345 self.timeline._extend_current_exposure,
346 )
347 self.a_tl_delete_frame = self.make_shortcut_action(
348 tr("タイムライン:コマを削除"),
349 self.timeline_ops.delete_frame,
350 "Shift+E",
351 )
352 self.a_tl_previous = self.make_shortcut_action(
353 tr("タイムライン:前のフレーム"),
354 self.timeline_ops.previous_frame,
355 )
356 self.a_tl_next = self.make_shortcut_action(
357 tr("タイムライン:次のフレーム"),
358 self.timeline_ops.next_frame,
359 )
360 self.a_tl_previous_key = self.make_shortcut_action(
361 tr("タイムライン:前のコマ"),
362 self.timeline_ops.previous_key,
363 "A",
364 )
365 self.a_tl_next_key = self.make_shortcut_action(
366 tr("タイムライン:次のコマ"),
367 self.timeline_ops.next_key,
368 "S",
369 )
370 self.a_tl_play = self.make_shortcut_action(
371 tr("タイムライン:再生/停止"),
372 lambda: self.timeline.play.toggle(),
373 )
374 self.a_tl_paste_time_remap = self.make_shortcut_action(
375 tr("タイムライン:タイムリマップを貼り付け"),
376 self.time_remap.show_paste_dialog,
377 )
378 self.a_tl_copy_time_remap = self.make_shortcut_action(
379 tr("タイムライン:タイムリマップをコピー"),
380 self.time_remap.copy_from_timeline,
381 )
382 self.a_tl_onion = self.make_shortcut_action(
383 tr("タイムライン:オニオンスキン設定"),
384 lambda: self.timeline.onion.toggle(),
385 )
386 self.a_tl_layer_add = self.make_shortcut_action(
387 tr("タイムライン:レイヤー追加"),
388 self.canvas.add_layer,
389 "Ctrl+Shift+N",
390 )
391 self.a_tl_layer_delete = self.make_shortcut_action(
392 tr("タイムライン:レイヤー削除"),
393 self.canvas.delete_layer,
394 )
395 self.a_tl_layer_rename = self.make_shortcut_action(
396 tr("タイムライン:レイヤー名変更"),
397 self.timeline.rename_selected_layer,
398 )
399 self.file_edit_actions = [
400 (tr("新規作成"), self.a_new),
401 (tr("プロジェクトを開く"), self.a_open_project),
402 (tr("カットフォルダーを開く"), self.a_open_cut_folder),
403 (tr("画像を読み込む"), self.a_import_images),
404 (tr("画像フォルダーを読み込む"), self.a_import_folder),
405 (tr("上書き保存"), self.a_save_project),
406 (tr("名前を付けて保存"), self.a_save_project_as),
407 (tr("現在のコマをPNG書き出し"), self.a_save),
408 (tr("現在のコマをTGA書き出し"), self.a_save_tga),
409 (tr("連番PNG+CSV書き出し"), self.a_export_png_seq),
410 (tr("連番TGA+CSV書き出し"), self.a_export_tga_seq),
411 (tr("カットフォルダーへ書き出し"), self.a_export_cut_folder),
412 (tr("影素材を書き出し"), self.a_export_shadow),
413 (tr("MP4書き出し"), self.a_export_mp4),
414 (tr("元に戻す"), self.a_undo),
415 (tr("やり直す"), self.a_redo),
416 (tr("切り取り"), self.a_cut),
417 (tr("コピー"), self.a_copy),
418 (tr("貼り付け"), self.a_paste),
419 (tr("キャンバスサイズ変更"), self.a_resize),
420 (tr("筆圧設定"), self.a_pressure),
421 (tr("メモを開く"), self.a_memo),
422 (tr("環境設定"), self.a_preferences),
423 (tr("背景以外を黒シルエット表示"), self.a_silhouette),
424 (tr("隣接色分けで表示"), self.a_preview_map),
425 (tr("露光量の調整を切り替え"), self.a_exposure_toggle),
426 (tr("赤チャンネルを表示"), self.a_channel_red),
427 (tr("緑チャンネルを表示"), self.a_channel_green),
428 (tr("青チャンネルを表示"), self.a_channel_blue),
429 (tr("アルファチャンネルを表示"), self.a_channel_alpha),
430 (tr("選択色だけ表示"), self.a_isolate_color),
431 (tr("特定色表示を解除"), self.a_clear_color_filter),
432 (tr("ゴミ取り"), self.a_remove_dust),
433 (tr("自動で塗る"), self.a_auto_paint),
434 (tr("今の絵を初期状態として保持"), self.a_keep_initial_state),
435 (tr("線画カットフォルダーを初期状態として読み込む"), self.a_import_initial_state),
436 (tr("CLIPの線画を初期状態として読み込む"), self.a_import_clip_initial_state),
437 (tr("保持した初期状態を消す"), self.a_clear_initial_state),
438 (tr("見本との違いチェック"), self.a_reference_check),
439 ]
440 self.tool_command_actions = [
441 (tr("描画色:メインを選択"), self.a_select_main),
442 (tr("描画色:サブを選択"), self.a_select_sub),
443 (tr("描画色:背景色を選択"), self.a_select_transparent),
444 (tr("描画色と背景色を切り替え"), self.a_toggle_draw_background),
445 (tr("メイン色とサブ色を切り替え"), self.a_swap_main_sub),
446 (tr("背景色の表示色を変更"), self.a_choose_background),
447 ("MainLineRepaint", self.a_mainline_repaint),
448 (tr("選択範囲:自由変形"), self.a_selection_transform),
449 (tr("選択範囲:拡大縮小"), self.a_selection_scale),
450 (tr("選択範囲:メッシュ変形"), self.a_selection_mesh),
451 (tr("すべてを選択"), self.a_selection_all),
452 (tr("選択範囲を解除"), self.a_selection_clear),
453 (tr("選択範囲を反転"), self.a_selection_invert),
454 (tr("選択範囲の中を消去"), self.a_selection_delete),
455 (tr("表示:拡大"), self.a_view_zoom_in),
456 (tr("表示:縮小"), self.a_view_zoom_out),
457 (tr("表示:全体を表示"), self.a_view_fit),
458 (tr("ブラシサイズを小さく"), self.a_brush_smaller),
459 (tr("ブラシサイズを大きく"), self.a_brush_larger),
460 (tr("変形:左へ90°回転"), self.a_transform_rotate_left),
461 (tr("変形:右へ90°回転"), self.a_transform_rotate_right),
462 (tr("メッシュ変形:格子数を変更"), self.a_transform_mesh_grid),
463 (tr("変形を確定"), self.a_transform_commit),
464 (tr("変形をキャンセル"), self.a_transform_cancel),
465 (tr("使用色:この色を選択範囲にする"), self.a_focus_used_color),
466 (tr("バケツ:含み塗り ON/OFF"), self.a_bucket_include_sub),
467 (tr("バケツ:隙間閉じ ON/OFF"), self.a_bucket_close_gap),
468 (tr("バケツ:串刺し塗り ON/OFF"), self.a_bucket_all_frames),
469 (tr("ゴミ取り:すべてのコマ ON/OFF"), self.a_dust_all_frames),
470 (tr("ゴミ取り/塗り抜けを適用"), self.a_dust_apply),
471 (tr("選択変形:すべてのコマ ON/OFF"), self.a_selection_all_frames),
472 ]
473 self.timeline_actions = [
474 (tr("コマ数を1つ増やす"), self.a_tl_add_exposure),
475 (tr("コマを削除"), self.a_tl_delete_frame),
476 (tr("タイムリマップを貼り付け"), self.a_tl_paste_time_remap),
477 (tr("タイムリマップをコピー"), self.a_tl_copy_time_remap),
478 (tr("前のフレーム"), self.a_tl_previous),
479 (tr("前のコマ"), self.a_tl_previous_key),
480 (tr("再生/停止"), self.a_tl_play),
481 (tr("次のコマ"), self.a_tl_next_key),
482 (tr("次のフレーム"), self.a_tl_next),
483 (tr("オニオンスキン設定"), self.a_tl_onion),
484 (tr("レイヤー追加"), self.a_tl_layer_add),
485 (tr("レイヤー削除"), self.a_tl_layer_delete),
486 (tr("レイヤー名変更"), self.a_tl_layer_rename),
487 ]
488 def build_menu(self):
489 f=self.menuBar().addMenu(tr("ファイル"))
490 f.addAction(self.a_new)
491 f.addAction(self.a_open_project)
492 self.recent_menu = f.addMenu(tr("最近使用したファイル"))
493 self.recent_menu.aboutToShow.connect(
494 lambda: self.project.fill_recent_menu(self.recent_menu)
495 )
496 f.addAction(self.a_open_cut_folder)
497 f.addAction(self.a_import_images)
498 f.addAction(self.a_import_folder)
499 f.addAction(self.a_import_images_raw)
500 f.addAction(self.a_import_psd)
501 f.addAction(self.a_import_clip)
502 f.addSeparator()
503 f.addAction(self.a_save_project)
504 f.addAction(self.a_save_project_as)
505 f.addSeparator()
506 f.addAction(self.a_save)
507 f.addAction(self.a_save_tga)
508 f.addAction(self.a_export_cut_folder)
509 f.addAction(self.a_export_shadow)
510 f.addAction(self.a_export_png_seq)
511 f.addAction(self.a_export_tga_seq)
512 f.addAction(self.a_export_xdts)
513 f.addAction(self.a_export_psd)
514 f.addAction(self.a_export_mp4)
515 e=self.menuBar().addMenu(tr("編集"));e.addAction(self.a_undo);e.addAction(self.a_redo);e.addSeparator();e.addAction(self.a_cut);e.addAction(self.a_copy);e.addAction(self.a_paste);e.addSeparator();e.addAction(self.a_silhouette);e.addAction(self.a_preview_map);e.addAction(self.a_isolate_color);e.addAction(self.a_clear_color_filter);e.addAction(self.a_swap_main_sub);e.addAction(self.a_remove_dust);e.addSeparator();e.addAction(self.a_resize);e.addAction(self.a_pressure);e.addSeparator();e.addAction(self.a_memo)
516 e.addSeparator()
517 self.a_color_chart = QAction(tr("カラーチャート…"), self)
518 self.a_color_chart.triggered.connect(
519 lambda: self._set_color_chart_visible(True)
520 )
521 e.addAction(self.a_color_chart)
522 e.addSeparator()
523 e.addAction(self.a_shortcuts)
524 e.addAction(self.a_preferences)
525 # 自動で塗る道具と、その仕上がりの確認。
526 auto_menu=self.menuBar().addMenu(tr("自動塗り"))
527 auto_menu.addAction(self.a_auto_paint)
528 auto_menu.addSeparator()
529 auto_menu.addAction(self.a_keep_initial_state)
530 auto_menu.addAction(self.a_import_initial_state)
531 auto_menu.addAction(self.a_import_clip_initial_state)
532 auto_menu.addAction(self.a_clear_initial_state)
533 auto_menu.addSeparator()
534 auto_menu.addAction(self.a_reference_check)
535 selection_menu=self.menuBar().addMenu(tr("選択範囲"))
536 selection_menu.addAction(self.a_selection_all)
537 selection_menu.addAction(self.a_selection_clear)
538 selection_menu.addAction(self.a_selection_invert)
539 selection_menu.addAction(self.a_selection_delete)
540 selection_menu.addSeparator()
541 selection_menu.addAction(self.a_selection_transform)
542 selection_menu.addAction(self.a_selection_scale)
543 selection_menu.addAction(self.a_selection_mesh)
544 selection_menu.addSeparator()
545 selection_menu.addAction(self.a_transform_rotate_left)
546 selection_menu.addAction(self.a_transform_rotate_right)
547 selection_menu.addAction(self.a_transform_mesh_grid)
548 selection_menu.addSeparator()
549 selection_menu.addAction(self.a_transform_commit)
550 selection_menu.addAction(self.a_transform_cancel)
551 a=self.menuBar().addMenu(tr("アニメーション"))
552 a.addAction(self.a_prev)
553 a.addAction(self.a_next)
554 a.addSeparator()
555 a.addAction(self.a_tl_previous_key)
556 a.addAction(self.a_tl_next_key)
557 a.addSeparator()
558 a.addAction(self.a_tl_paste_time_remap)
559 a.addAction(self.a_tl_copy_time_remap)
560 # 「システムに合わせる」のときは、OSの外観の切り替えに追従する。
561 QGuiApplication.styleHints().colorSchemeChanged.connect(
562 self._on_system_color_scheme_changed
563 )
565 def _on_system_color_scheme_changed(self, *_args):
566 if theme.current_theme() == theme.SYSTEM:
567 self.set_theme(theme.SYSTEM)
569 def show_preferences(self, section=None):
570 dialog = getattr(self, "preferences_dialog", None)
571 if dialog is None:
572 dialog = self.preferences_dialog = PreferencesDialog(self)
573 if section:
574 dialog.show_section(section)
575 dialog.show_and_raise()
577 def _set_color_chart_visible(self, visible):
578 window = getattr(self, "color_chart_window", None)
579 if window is None: 579 ↛ 580line 579 didn't jump to line 580 because the condition on line 579 was never true
580 return
581 if visible: 581 ↛ 584line 581 didn't jump to line 584 because the condition on line 581 was always true
582 window.show_and_raise()
583 else:
584 window.close()
585 def _save_canvas_snapshot_clicked(self):
586 """カメラボタン。Shiftを押していれば保存先を選び直してから保存する。"""
587 shift = bool(
588 QGuiApplication.keyboardModifiers() & Qt.KeyboardModifier.ShiftModifier
589 )
590 return self.export.save_canvas_snapshot(choose_folder=shift)
592 def _apply_canvas_bar_icons(self):
593 for button, name in getattr(self, "_canvas_bar_icons", {}).items():
594 button.setIcon(icons.icon(name))
595 def _refresh_theme_dependent_ui(self):
596 """Re-apply palette-derived styles after a theme/accent change."""
597 bar: Any = self.statusBar()
598 if hasattr(bar, "refresh_palette"): 598 ↛ 600line 598 didn't jump to line 600 because the condition on line 598 was always true
599 bar.refresh_palette()
600 if hasattr(self.tools, "apply_theme"): 600 ↛ 602line 600 didn't jump to line 602 because the condition on line 600 was always true
601 self.tools.apply_theme()
602 if hasattr(self.tool_selector, "apply_theme"): 602 ↛ 604line 602 didn't jump to line 604 because the condition on line 602 was always true
603 self.tool_selector.apply_theme()
604 if hasattr(self.timeline, "apply_theme"): 604 ↛ 606line 604 didn't jump to line 606 because the condition on line 604 was always true
605 self.timeline.apply_theme()
606 self._apply_canvas_bar_icons()
607 if hasattr(self, "update_notice"): 607 ↛ 609line 607 didn't jump to line 609 because the condition on line 607 was always true
608 self.update_notice.refresh_style()
609 if hasattr(self.palette, "apply_theme"): 609 ↛ 611line 609 didn't jump to line 611 because the condition on line 609 was always true
610 self.palette.apply_theme()
611 if hasattr(self, "dock_manager"): 611 ↛ exitline 611 didn't return from function '_refresh_theme_dependent_ui' because the condition on line 611 was always true
612 self._customize_docking_hover()
613 # スタイルシートの付け直しでタブの min/max-height が QSS の値に
614 # 戻るため、固定の高さを付け直す。
615 self._sync_all_area_hamburgers()
616 def build_ui(self):
617 QtAds.CDockManager.setConfigFlag(
618 QtAds.CDockManager.eConfigFlag.AlwaysShowTabs, True
619 )
620 QtAds.CDockManager.setConfigFlag(
621 QtAds.CDockManager.eConfigFlag.DockAreaHasUndockButton, False
622 )
623 QtAds.CDockManager.setConfigFlag(
624 QtAds.CDockManager.eConfigFlag.DockAreaHasTabsMenuButton, False
625 )
626 QtAds.CDockManager.setConfigFlag(
627 QtAds.CDockManager.eConfigFlag.ActiveTabHasCloseButton, False
628 )
629 QtAds.CDockManager.setConfigFlag(
630 QtAds.CDockManager.eConfigFlag.DockAreaHasCloseButton, False
631 )
632 QtAds.CDockManager.setConfigFlag(
633 QtAds.CDockManager.eConfigFlag.OpaqueSplitterResize, True
634 )
635 QtAds.CDockManager.setConfigFlag(
636 QtAds.CDockManager.eConfigFlag.FloatingContainerForceQWidgetTitleBar,
637 True,
638 )
639 QtAds.CDockManager.setConfigFlag(
640 QtAds.CDockManager.eConfigFlag.FloatingContainerForceNativeTitleBar,
641 False,
642 )
643 QtAds.CDockManager.setConfigFlag(
644 QtAds.CDockManager.eConfigFlag.DoubleClickUndocksWidget, True
645 )
646 self.setDockNestingEnabled(True)
647 self.status_bar = StatusBar(self)
648 self.setStatusBar(self.status_bar)
649 center=QWidget()
650 cv=QVBoxLayout(center)
651 cv.setContentsMargins(0,0,0,0)
652 self.canvas.setMinimumSize(160, 40)
653 cv.addWidget(self.canvas,1)
654 bar=QHBoxLayout()
655 self.zoom=QSlider(Qt.Orientation.Horizontal)
656 self.zoom.setRange(5,1600)
657 self.zoom.setValue(100)
658 self.zoom.setFixedWidth(72)
659 self.zoom_label=QLabel("100%")
660 self.rot=QSlider(Qt.Orientation.Horizontal)
661 self.rot.setRange(-180,180)
662 self.rot.setFixedWidth(72)
663 self.rot_label=QLabel("0°")
664 b100=QPushButton()
665 b100.setToolTip(tr("100%表示"))
666 bfit=QPushButton()
667 bfit.setToolTip(tr("全体を表示"))
668 b0=QPushButton()
669 b0.setToolTip(tr("回転を0°に戻す"))
670 self.flip_horizontal_button=QPushButton()
671 self.flip_horizontal_button.setCheckable(True)
672 self.flip_horizontal_button.setToolTip(tr("左右反転プレビュー"))
673 self.flip_horizontal_button.toggled.connect(self.view_preview.set_flip_horizontal)
674 self.flip_vertical_button=QPushButton()
675 self.flip_vertical_button.setCheckable(True)
676 self.flip_vertical_button.setToolTip(tr("上下反転プレビュー"))
677 self.flip_vertical_button.toggled.connect(self.view_preview.set_flip_vertical)
678 # 今の絵の書き出し(クリップボード/フォルダー)。見え方のチェックとは
679 # 役割が違うので、区切りを挟んで比較ボタンの左へ置く。
680 self.copy_canvas_button=QPushButton()
681 self.copy_canvas_button.setToolTip(tr(
682 "今の絵をクリップボードへ書き出す"
683 ))
684 self.copy_canvas_button.clicked.connect(self.export.copy_canvas_to_clipboard)
685 self.save_canvas_button=QPushButton()
686 self.save_canvas_button.setToolTip(tr(
687 "今の絵をフォルダーへ書き出す(年月日時分秒のファイル名)。"
688 "Shift+クリックで保存先を選び直す"
689 ))
690 self.save_canvas_button.clicked.connect(self._save_canvas_snapshot_clicked)
691 self.canvas_export_separator=QFrame()
692 self.canvas_export_separator.setFrameShape(QFrame.Shape.VLine)
693 self.canvas_export_separator.setFrameShadow(QFrame.Shadow.Plain)
694 self.canvas_export_separator.setFixedHeight(16)
695 self.compare_initial_button=QPushButton()
696 self.compare_initial_button.setCheckable(True)
697 self.compare_initial_button.setToolTip(tr(
698 "色塗り前の初期状態と比べる(押すと初期状態を表示、もう一度押すと今の絵に戻る)"
699 ))
700 self.compare_initial_button.toggled.connect(self.initial_state.set_compare)
701 # 見え方のチェック(画面だけを変え、書き出しには影響しない)。
702 self.black_check_button=QPushButton()
703 self.black_check_button.setCheckable(True)
704 self.black_check_button.setToolTip(tr("ブラックチェック(背景以外を黒シルエット表示)"))
705 self.black_check_button.toggled.connect(self.line_ops.toggle_silhouette)
706 self.map_colors_button=QPushButton()
707 self.map_colors_button.setCheckable(True)
708 self.map_colors_button.setToolTip(tr(
709 "隣接色分け:隣り合う部分を見分けやすい仮の色で塗り分けて表示する"
710 "(近い色の塗り漏れが見つかる。元の色が同じ部分は同じ色)。"
711 "アクティブレイヤーだけに掛かる"
712 ))
713 self.map_colors_button.toggled.connect(self.view_preview.set_map_colors)
714 self.channel_combo=ChannelPreviewButton(channel_labels())
715 self.channel_combo.channelChanged.connect(self.view_preview.set_channel)
716 self.exposure_button=QPushButton()
717 self.exposure_button.setCheckable(True)
718 self.exposure_button.setToolTip(tr(
719 "露光量の調整:押すと 0 に戻す。もう一度押すと直前の値に戻す"
720 ))
721 self.exposure_button.clicked.connect(self.view_preview.toggle_exposure)
722 self.exposure_control=ExposureControl(
723 view_preview.EXPOSURE_MIN, view_preview.EXPOSURE_MAX
724 )
725 self.exposure_control.setToolTip(tr("露光量(段)。左右ドラッグ、またはクリックして入力"))
726 self.exposure_spin=self.exposure_control.spin
727 self.exposure_control.valueChanged.connect(self.view_preview.set_exposure)
728 # 表示操作はアイコンボタンにして、キャンバスの下を細い1本のバーにする。
729 self._canvas_bar_icons = {
730 b100: "zoom_actual", bfit: "fit", self.compare_initial_button: "compare",
731 b0: "rotate_reset", self.black_check_button: "black_check",
732 self.flip_horizontal_button: "flip_horizontal",
733 self.flip_vertical_button: "flip_vertical",
734 self.map_colors_button: "map_colors", self.exposure_button: "exposure",
735 self.copy_canvas_button: "clipboard", self.save_canvas_button: "camera",
736 }
737 for button in self._canvas_bar_icons:
738 button.setProperty("iconButton", True)
739 button.setFixedSize(26, 24)
740 button.setIconSize(QSize(16, 16))
741 button.setFocusPolicy(Qt.FocusPolicy.NoFocus)
742 self._apply_canvas_bar_icons()
743 for label in (self.zoom_label, self.rot_label):
744 label.setMinimumWidth(38)
745 bar.setContentsMargins(8, 2, 8, 2)
746 bar.setSpacing(4)
747 zoom_title=QLabel(tr("拡大"))
748 rot_title=QLabel(tr("回転"))
749 for w in (zoom_title,self.zoom,self.zoom_label,b100,bfit):
750 bar.addWidget(w)
751 bar.addSpacing(16)
752 for w in (rot_title,self.rot,self.rot_label,b0,
753 self.flip_horizontal_button,self.flip_vertical_button):
754 bar.addWidget(w)
755 bar.addSpacing(16)
756 bar.addStretch(1)
757 for w in (
758 self.copy_canvas_button, self.save_canvas_button,
759 self.canvas_export_separator,
760 self.compare_initial_button, self.black_check_button,
761 self.map_colors_button, self.channel_combo,
762 self.exposure_button, self.exposure_control,
763 ):
764 bar.addWidget(w)
765 cv.addLayout(bar)
766 self.dock_manager = QtAds.CDockManager(self)
767 # ADS の既定(FollowPalette)はパレットが変わるたびに組み込みの
768 # スタイルシートを読み込み直し、テーマ切り替えの後に
769 # _customize_docking_hover() のスタイルを上書きしてしまう。
770 # 配色はこちらで付けるので、自動の読み込み直しを止める。
771 self.dock_manager.setColorSchemeMode(
772 QtAds.CDockManager.ColorSchemeMode.Light
773 )
774 self._customize_docking_hover()
775 self._setup_split_drop_overlay()
776 self.dock_manager.floatingWidgetCreated.connect(
777 self._configure_floating_window
778 )
779 self.central_dock = QtAds.CDockWidget(self.dock_manager, tr("キャンバス"))
780 self.central_dock.setObjectName("canvasDock")
781 self.central_dock.setWidget(
782 center, QtAds.CDockWidget.eInsertMode.ForceNoScrollArea
783 )
784 self.dock_manager.setCentralWidget(self.central_dock)
785 # setCentralWidget() intentionally strips movable/floatable in ADS;
786 # restore them afterwards because the canvas is detachable here.
787 self.central_dock.setFeatures(
788 QtAds.CDockWidget.DockWidgetFeature.DockWidgetFocusable
789 | QtAds.CDockWidget.DockWidgetFeature.DockWidgetMovable
790 | QtAds.CDockWidget.DockWidgetFeature.DockWidgetFloatable
791 )
792 self.central_dock.topLevelChanged.connect(
793 lambda floating: self._sync_floating_title(
794 self.central_dock, floating
795 )
796 )
798 self.tools.setMinimumWidth(180)
799 self.tools.setMaximumWidth(16777215)
800 self.tools.setMinimumHeight(0)
801 self.palette.setMinimumWidth(220)
802 self.palette.setMaximumWidth(16777215)
803 self.palette.setMinimumHeight(0)
805 self.tools_scroll = QScrollArea()
806 self.tools_scroll.setWidgetResizable(True)
807 self.tools_scroll.setMinimumSize(0, 0)
808 self.tools_scroll.setWidget(self.tools)
810 self.palette_scroll = QScrollArea()
811 self.palette_scroll.setWidgetResizable(True)
812 self.palette_scroll.setMinimumSize(0, 0)
813 self.palette_scroll.setWidget(self.palette)
815 self.history_scroll = QScrollArea()
816 self.history_scroll.setWidgetResizable(True)
817 self.history_scroll.setMinimumSize(0, 0)
818 self.history_scroll.setWidget(self.history_panel)
820 # 描画色(メイン/サブ/背景)はツールバー最下部のスウォッチに加え、
821 # カラーサークルのドック上部(tools.drawing_color_box)にも大きく表示する。
822 self.color_wheel_scroll = QScrollArea()
823 self.color_wheel_scroll.setWidgetResizable(True)
824 self.color_wheel_scroll.setMinimumSize(0, 0)
825 self.color_wheel_scroll.setFrameShape(QScrollArea.Shape.NoFrame)
826 self.color_wheel_scroll.setWidget(self.tools.color_wheel_box)
828 self.color_slider_scroll = QScrollArea()
829 self.color_slider_scroll.setWidgetResizable(True)
830 self.color_slider_scroll.setMinimumSize(0, 0)
831 self.color_slider_scroll.setFrameShape(QScrollArea.Shape.NoFrame)
832 self.color_slider_scroll.setWidget(self.tools.color_slider_box)
834 self.tool_selector_dock=QtAds.CDockWidget(self.dock_manager, tr("ツール"))
835 self.tool_selector_dock.setObjectName("toolSelectorDock")
836 self.tool_selector_dock.setFeatures(
837 QtAds.CDockWidget.DockWidgetFeature.DockWidgetMovable
838 | QtAds.CDockWidget.DockWidgetFeature.DockWidgetFocusable
839 | QtAds.CDockWidget.DockWidgetFeature.DockWidgetFloatable
840 )
841 self.tool_selector_dock.setMinimumSizeHintMode(
842 QtAds.CDockWidget.eMinimumSizeHintMode.MinimumSizeHintFromContentMinimumSize
843 )
844 self.tool_selector_dock.setWidget(
845 self.tool_selector, QtAds.CDockWidget.eInsertMode.ForceNoScrollArea
846 )
847 tool_area = self.dock_manager.addDockWidget(
848 QtAds.LeftDockWidgetArea, self.tool_selector_dock
849 )
851 self.tools_dock=QtAds.CDockWidget(self.dock_manager, tr("ツールプロパティ"))
852 self.tools_dock.setObjectName("toolsDock")
853 self.tools_dock.setWidget(
854 self.tools_scroll, QtAds.CDockWidget.eInsertMode.ForceNoScrollArea
855 )
856 tools_area = self.dock_manager.addDockWidget(
857 QtAds.RightDockWidgetArea, self.tools_dock, tool_area
858 )
860 self.action_panel_scroll = QScrollArea()
861 self.action_panel_scroll.setWidgetResizable(True)
862 self.action_panel_scroll.setMinimumSize(0, 0)
863 self.action_panel_scroll.setWidget(self.action_panel)
864 self.action_panel_dock=QtAds.CDockWidget(self.dock_manager, tr("アクション"))
865 self.action_panel_dock.setObjectName("actionPanelDock")
866 self.action_panel_dock.setWidget(
867 self.action_panel_scroll, QtAds.CDockWidget.eInsertMode.ForceNoScrollArea
868 )
869 self.dock_manager.addDockWidget(
870 QtAds.BottomDockWidgetArea, self.action_panel_dock, tools_area
871 )
873 self.color_wheel_dock=QtAds.CDockWidget(self.dock_manager, tr("カラーサークル"))
874 self.color_wheel_dock.setObjectName("colorWheelDock")
875 self.color_wheel_dock.setWidget(
876 self.color_wheel_scroll, QtAds.CDockWidget.eInsertMode.ForceNoScrollArea
877 )
878 # カラーサークルを右エリアのアンカーにする(旧・描画色ドックの位置)。
879 drawing_area = self.dock_manager.addDockWidget(
880 QtAds.RightDockWidgetArea, self.color_wheel_dock
881 )
882 wheel_area = drawing_area
884 self.color_slider_dock=QtAds.CDockWidget(self.dock_manager, tr("カラースライダー"))
885 self.color_slider_dock.setObjectName("colorSliderDock")
886 self.color_slider_dock.setWidget(
887 self.color_slider_scroll, QtAds.CDockWidget.eInsertMode.ForceNoScrollArea
888 )
889 self.dock_manager.addDockWidget(
890 QtAds.BottomDockWidgetArea, self.color_slider_dock, wheel_area
891 )
893 self.palette_dock=QtAds.CDockWidget(self.dock_manager, tr("使用色"))
894 self.palette_dock.setObjectName("paletteDock")
895 self.palette_dock.setWidget(
896 self.palette_scroll, QtAds.CDockWidget.eInsertMode.ForceNoScrollArea
897 )
898 palette_area = self.dock_manager.addDockWidget(
899 QtAds.BottomDockWidgetArea, self.palette_dock, drawing_area
900 )
901 # タイトルバーの×の左側に「?」ヘルプボタンを置き、
902 # 常時表示していた使い方の説明をそこへ収める。
903 self.palette_help_action = QAction("?", self)
904 self.palette_help_action.setToolTip(tr("使用色パネルの使い方を表示します。"))
905 self.palette_help_action.triggered.connect(self.palette.show_help)
906 self.palette_dock.setTitleBarActions([self.palette_help_action])
908 self.history_dock=QtAds.CDockWidget(self.dock_manager, tr("ヒストリー"))
909 self.history_dock.setObjectName("historyDock")
910 self.history_dock.setWidget(
911 self.history_scroll, QtAds.CDockWidget.eInsertMode.ForceNoScrollArea
912 )
913 # 使用色パネルと同じ場所にタブとして重ねる。
914 self.dock_manager.addDockWidget(
915 QtAds.CenterDockWidgetArea, self.history_dock, palette_area
916 )
917 self.history_help_action = QAction("?", self)
918 self.history_help_action.setToolTip(tr("ヒストリーパネルの使い方を表示します。"))
919 self.history_help_action.triggered.connect(self.history_panel.show_help)
920 self.history_dock.setTitleBarActions([self.history_help_action])
921 # 使用色をヒストリーより前のタブとして、起動時の前面にする。
922 palette_area.setCurrentDockWidget(self.palette_dock)
924 # カラーチャートはたまに使う道具なので、パネルではなく「編集」メニュー
925 # から開く別ウィンドウにする(パネルの列には幅が足りず中身が切れていた)。
926 self.color_chart_window = ToolWindow(
927 self, tr("カラーチャート"), "color_chart", self.color_chart,
928 default_size=(520, 460),
929 )
931 self.subview_dock=QtAds.CDockWidget(self.dock_manager, tr("サブビュー"))
932 self.subview_dock.setObjectName("subviewDock")
933 self.subview_dock.setWidget(
934 self.subview, QtAds.CDockWidget.eInsertMode.ForceNoScrollArea
935 )
936 self.dock_manager.addDockWidget(
937 QtAds.RightDockWidgetArea, self.subview_dock
938 )
940 self.timeline_dock=QtAds.CDockWidget(self.dock_manager, tr("タイムライン"))
941 self.timeline_dock.setObjectName("timelineDock")
942 self.timeline.setMaximumHeight(16777215)
943 self.timeline_dock.setWidget(
944 self.timeline, QtAds.CDockWidget.eInsertMode.ForceNoScrollArea
945 )
946 self.timeline_dock.setMinimumHeight(70)
947 self.timeline_dock.setMaximumHeight(16777215)
948 self.dock_manager.addDockWidget(
949 QtAds.BottomDockWidgetArea, self.timeline_dock
950 )
951 # 各パネルのタブ左端に付けるハンバーガーメニューの内容。
952 # 未登録のパネルは閉じる/フロートの共通項目だけになる。
953 dock_menu_builders = {
954 self.action_panel_dock: self._build_action_panel_menu,
955 self.color_wheel_dock: self._build_color_wheel_menu,
956 self.color_slider_dock: self._build_color_slider_menu,
957 }
958 for dock in (
959 self.tool_selector_dock,
960 self.tools_dock,
961 self.action_panel_dock,
962 self.color_wheel_dock,
963 self.color_slider_dock,
964 self.palette_dock,
965 self.history_dock,
966 self.subview_dock,
967 self.timeline_dock,
968 ):
969 dock.topLevelChanged.connect(
970 lambda floating, current=dock:
971 self._sync_floating_title(current, floating)
972 )
973 self._add_dock_hamburger(dock, dock_menu_builders.get(dock))
974 self._sync_floating_title(dock, False)
975 self.dock_manager.dockAreasAdded.connect(
976 lambda *_args: QTimer.singleShot(
977 0, self._sync_all_area_hamburgers
978 )
979 )
980 self.dock_manager.dockWidgetAdded.connect(
981 lambda *_args: QTimer.singleShot(
982 0, self._sync_all_area_hamburgers
983 )
984 )
985 self.workspace.build_menu()
986 self._finalize_startup_dock_ui()
987 for dock in (
988 self.tool_selector_dock,
989 self.tools_dock,
990 self.action_panel_dock,
991 self.color_wheel_dock,
992 self.color_slider_dock,
993 self.palette_dock,
994 self.history_dock,
995 self.subview_dock,
996 self.timeline_dock,
997 ):
998 self._sync_floating_title(dock, dock.isFloating())
999 QTimer.singleShot(
1000 0,
1001 lambda: self._resize_tool_selector_area(
1002 self.tool_selector.width_for_columns(1)
1003 ),
1004 )
1006 panel_menu=self.menuBar().addMenu(tr("パネル"))
1007 view_menu=panel_menu
1008 view_menu.addAction(self.tool_selector_dock.toggleViewAction())
1009 view_menu.addAction(self.tools_dock.toggleViewAction())
1010 view_menu.addAction(self.action_panel_dock.toggleViewAction())
1011 view_menu.addAction(self.color_wheel_dock.toggleViewAction())
1012 view_menu.addAction(self.color_slider_dock.toggleViewAction())
1013 view_menu.addAction(self.palette_dock.toggleViewAction())
1014 view_menu.addAction(self.history_dock.toggleViewAction())
1015 view_menu.addAction(self.subview_dock.toggleViewAction())
1016 view_menu.addAction(self.timeline_dock.toggleViewAction())
1018 help_menu=self.menuBar().addMenu(tr("ヘルプ"))
1019 a_check_update=QAction(tr("更新を確認…"), self)
1020 a_check_update.triggered.connect(self.check_for_updates_interactive)
1021 help_menu.addAction(a_check_update)
1022 help_menu.addSeparator()
1023 a_about=QAction(tr("バージョン情報…"), self)
1024 a_about.triggered.connect(self.show_about_dialog)
1025 help_menu.addAction(a_about)
1027 self.zoom.valueChanged.connect(self.set_zoom)
1028 b100.clicked.connect(self.show_canvas_at_100_percent)
1029 bfit.clicked.connect(self.fit_canvas)
1030 self.rot.valueChanged.connect(self.set_rot)
1031 b0.clicked.connect(lambda:self.rot.setValue(0))
1034 def _resize_tool_selector_area(self, content_width):
1035 dock = self.tool_selector_dock
1036 frame_width = max(0, dock.width() - self.tool_selector.width())
1037 target_width = int(content_width) + frame_width
1038 child = dock
1039 parent = dock.parentWidget()
1040 while parent is not None: 1040 ↛ exitline 1040 didn't return from function '_resize_tool_selector_area' because the condition on line 1040 was always true
1041 if isinstance(parent, QSplitter) and parent.orientation() == Qt.Orientation.Horizontal:
1042 index = parent.indexOf(child)
1043 sizes = parent.sizes()
1044 if 0 <= index < len(sizes): 1044 ↛ 1052line 1044 didn't jump to line 1052 because the condition on line 1044 was always true
1045 delta = sizes[index] - target_width
1046 sizes[index] = target_width
1047 recipients = [i for i in range(len(sizes)) if i != index]
1048 if recipients: 1048 ↛ 1051line 1048 didn't jump to line 1051 because the condition on line 1048 was always true
1049 recipient = max(recipients, key=lambda i: sizes[i])
1050 sizes[recipient] = max(1, sizes[recipient] + delta)
1051 parent.setSizes(sizes)
1052 return
1053 child = parent
1054 parent = parent.parentWidget()
1056 def connect_signals(self):
1057 """Wire every widget signal to its handler.
1059 Named ``connect_signals`` rather than ``connect``: on a PySide6 object,
1060 an override living on a non-``QObject`` base is shadowed by the built-in
1061 ``QObject.connect`` at attribute-lookup time, so the plain name would
1062 silently resolve to Qt's method.
1063 """
1064 self.tool_selector.toolChanged.connect(self.tools.select_tool)
1065 self.tool_selector.snapWidthRequested.connect(
1066 self._snap_tool_selector_width
1067 )
1068 self.tools.toolChanged.connect(self.tool_selector.set_active_tool)
1069 self.tools.toolChanged.connect(self.canvas.set_tool)
1070 self.tools.brush_size_spinbox.valueChanged.connect(self.canvas.set_pen_size)
1071 self.tools.brush_stabilizer.valueChanged.connect(
1072 self.canvas.set_brush_stabilizer
1073 )
1074 self.tools.brush_size_spinbox.pressureRequested.connect(self.pressure)
1075 self.tools.fill_opacity.valueChanged.connect(
1076 lambda value: setattr(self.canvas, "fill_opacity", value / 100)
1077 )
1078 self.tools.colorModeChanged.connect(self.colors.set_color_mode)
1079 self.tools.colorChanged.connect(self.colors.set_color_value)
1080 self.subview.colorPicked.connect(self.colors.apply_sampled_color)
1081 self.color_chart.colorPicked.connect(self.colors.apply_sampled_color)
1082 self.color_chart.chartChanged.connect(self.color_chart_ops.on_edited)
1083 self.color_chart.captureRequested.connect(self.color_chart_ops.capture)
1084 self.color_chart.applyRequested.connect(self.color_chart_ops.apply)
1085 self.color_chart.saveRequested.connect(self.color_chart_ops.save_pmag)
1086 self.color_chart.loadRequested.connect(self.color_chart_ops.load_pmag)
1087 self.subview.set_color_provider(
1088 lambda: (
1089 self.canvas.sub_color
1090 if self.canvas.color_mode == "sub"
1091 else self.canvas.main_color
1092 )
1093 )
1094 self.subview.colorPicked.connect(
1095 lambda color: self.status(
1096 tr("サブビューから {color} を取得しました").format(
1097 color=color.name().upper()
1098 ),
1099 "success",
1100 2500,
1101 )
1102 )
1103 # Keep the tool-bar drawing-colour swatch in sync with the panel.
1104 self.tool_selector.colorModeRequested.connect(self.colors.set_color_mode)
1105 self.tool_selector.backgroundColorRequested.connect(
1106 self.colors.choose_background_color
1107 )
1108 self.tool_selector.colorEditRequested.connect(self.colors.choose_color)
1109 self.tools.colorModeChanged.connect(
1110 lambda _m=None: self.colors._sync_tool_selector_swatch()
1111 )
1112 self.tools.colorChanged.connect(
1113 lambda *_a: self.colors._sync_tool_selector_swatch()
1114 )
1115 self.tools.main_btn.colorPicked.connect(
1116 lambda color: self.colors.apply_sampled_color_to_mode("main", color)
1117 )
1118 self.tools.sub_btn.colorPicked.connect(
1119 lambda color: self.colors.apply_sampled_color_to_mode("sub", color)
1120 )
1121 self.tools.meshCommitRequested.connect(self.canvas.commit_mesh)
1122 self.tools.meshCancelRequested.connect(self.canvas.cancel_mesh)
1123 self.tools.selectionTransformRequested.connect(
1124 lambda: self.line_ops.start_wire_transform("free")
1125 )
1126 self.tools.selectionScaleRequested.connect(
1127 lambda: self.line_ops.start_wire_transform("scale")
1128 )
1129 self.tools.selectionMeshRequested.connect(
1130 lambda: self.line_ops.start_wire_transform("mesh")
1131 )
1132 self.tools.selectionClearRequested.connect(self.canvas.clear_selection)
1133 self.tools.selectionRotateRequested.connect(
1134 self.canvas.rotate_selection_transform
1135 )
1136 self.tools.transformMeshGridChanged.connect(
1137 self.canvas.set_transform_mesh_grid
1138 )
1139 self.tools.transform_quality.toggled.connect(
1140 self.canvas.set_transform_quality
1141 )
1142 self.tools.transform_line_width.valueChanged.connect(
1143 self.canvas.set_transform_line_threshold
1144 )
1145 self.tools.transform_line_width.sliderPressed.connect(
1146 self.canvas.begin_transform_line_adjustment
1147 )
1148 self.tools.transform_line_width.sliderReleased.connect(
1149 self.canvas.finish_transform_line_adjustment
1150 )
1151 self.canvas.set_transform_quality(
1152 self.tools.transform_quality.isChecked()
1153 )
1154 self.canvas.set_transform_line_threshold(
1155 self.tools.transform_line_width.value()
1156 )
1157 self.tools.selectionCommitRequested.connect(
1158 self.tween.commit_transform_or_tween
1159 )
1160 self.tools.selectionCancelRequested.connect(
1161 self.tween.cancel_transform_or_tween
1162 )
1163 self.tools.flipLayerRequested.connect(self.canvas.flip_active_layer)
1164 self.tools.swapMainSubRequested.connect(self.line_ops.swap_main_sub)
1165 self.tools.resetMainSubRequested.connect(self.line_ops.reset_main_sub)
1166 self.tools.removeDustRequested.connect(self.line_ops.remove_dust_fill_surrounding)
1167 self.tools.backgroundColorRequested.connect(self.colors.choose_background_color)
1168 self.tools.colorEditRequested.connect(self.colors.choose_color)
1170 self.timeline.frameSelected.connect(self.timeline_ops.select_exposure)
1171 self.timeline.addFrameRequested.connect(
1172 self.canvas.add_frame
1173 )
1174 self.timeline.extendExposureRequested.connect(
1175 lambda row, key, exposure:
1176 self.timeline_ops.resize_exposure(
1177 row,
1178 key,
1179 key + max(1, exposure),
1180 "right",
1181 )
1182 )
1183 self.timeline.deleteFrameRequested.connect(
1184 self.timeline_ops.delete_frame
1185 )
1186 self.timeline.first.clicked.connect(lambda: self.timeline_ops.jump_boundary(False))
1187 self.timeline.last.clicked.connect(lambda: self.timeline_ops.jump_boundary(True))
1188 self.timeline.table.ruler.rangeResized.connect(self.timeline_ops.resize_range)
1189 self.timeline.previousRequested.connect(self.timeline_ops.previous_frame)
1190 self.timeline.nextRequested.connect(self.timeline_ops.next_frame)
1191 self.timeline.previousKeyRequested.connect(self.timeline_ops.previous_key)
1192 self.timeline.nextKeyRequested.connect(self.timeline_ops.next_key)
1193 self.timeline.durationChanged.connect(self.canvas.set_duration)
1194 self.timeline.cellMoveRequested.connect(self.timeline_ops.move_cell)
1195 self.timeline.cellCopyRequested.connect(self.timeline_ops.copy_cell)
1196 self.timeline.sequenceRecallRequested.connect(self.timeline_ops.recall_sequence_number)
1197 self.timeline.multiCellMoveRequested.connect(
1198 self.timeline_ops.move_selection
1199 )
1200 self.timeline.timelineModeChanged.connect(
1201 self.timeline_ops.set_mode
1202 )
1203 self.timeline.normalizeNumbersRequested.connect(
1204 self.timeline_ops.normalize_numbers
1205 )
1206 self.timeline.blankFrameRequested.connect(
1207 self.timeline_ops.create_blank_key
1208 )
1209 # セルの右クリックメニュー(Issue #48)。
1210 self.timeline.newAnimationCellRequested.connect(
1211 self.timeline_ops.new_animation_cell
1212 )
1213 self.timeline.clearCellRequested.connect(self.timeline_ops.clear_cell)
1214 self.timeline.cutCellRequested.connect(
1215 self.timeline_ops.cut_cell_to_clipboard
1216 )
1217 self.timeline.copyCellToClipboardRequested.connect(
1218 self.timeline_ops.copy_cell_to_clipboard
1219 )
1220 self.timeline.pasteCellRequested.connect(
1221 self.timeline_ops.paste_cell_from_clipboard
1222 )
1223 self.timeline.mergeClipRequested.connect(self.timeline_ops.merge_clip)
1224 self.timeline.splitClipRequested.connect(self.timeline_ops.split_clip)
1225 self.timeline.insertFrameAtRequested.connect(
1226 self.timeline_ops.insert_frame_at
1227 )
1228 self.timeline.deleteFrameAtRequested.connect(
1229 self.timeline_ops.delete_frame_at
1230 )
1231 self.timeline.playbackRangeEdgeRequested.connect(
1232 self.timeline_ops.resize_range
1233 )
1234 self.timeline.timeRemapCopyRequested.connect(
1235 self.time_remap.copy_from_timeline
1236 )
1237 self.timeline.exposureResizeRequested.connect(self.timeline_ops.resize_exposure)
1238 self.timeline.tweenRequested.connect(
1239 lambda row, column: self.tween.enable(row, column, "free")
1240 )
1241 self.timeline.tweenMeshRequested.connect(
1242 lambda row, column: self.tween.enable(row, column, "mesh")
1243 )
1244 self.timeline.tweenCancelRequested.connect(
1245 self.tween.cancel_transform_or_tween
1246 )
1247 self.timeline.tweenEditRequested.connect(self.tween.edit)
1248 self.timeline.tweenReleaseRequested.connect(self.tween.release)
1249 self.timeline.onionPopupToggled.connect(
1250 self.onion.toggle_settings_popup
1251 )
1252 self.timeline.onionChanged.connect(self.onion.set_enabled)
1253 self.timeline.timeRemapPasteRequested.connect(
1254 self.time_remap.show_paste_dialog
1255 )
1256 self.timeline.timeRemapFileDropped.connect(
1257 self.time_remap.open_dropped
1258 )
1259 self.timeline.playRequested.connect(self.play)
1261 self.canvas.changed.connect(self.refresh_ui)
1262 self.canvas.selectionChanged.connect(self.refresh_selection)
1263 self.canvas.selectionCleared.connect(
1264 self.palette._clear_used_color_selection
1265 )
1266 self.canvas.cellChanged.connect(self._on_canvas_cell_changed)
1267 self.canvas.colorUsed.connect(self.palette.add_color)
1269 self.timeline.layerSelected.connect(self.layers.on_selected)
1270 self.timeline.layerVisibilityChanged.connect(self.layers.set_visibility_row)
1271 self.timeline.layerOpacityChanged.connect(self.layers.set_opacity_row)
1272 self.timeline.layer_opacity_slider.sliderPressed.connect(
1273 self.canvas.push_doc_undo
1274 )
1275 self.timeline.layerNameChanged.connect(self.layers.rename_row)
1276 self.timeline.layerMoveRequested.connect(self.layers.move_row)
1277 self.timeline.addLayerRequested.connect(self.layers.add_fast)
1278 self.timeline.deleteLayerRequested.connect(self.canvas.delete_layer)
1279 self.timeline.duplicateLayersRequested.connect(self.layers.duplicate_rows)
1280 self.timeline.mergeLayersRequested.connect(self.layers.merge_rows)
1281 self.timeline.deleteLayersRequested.connect(self.layers.delete_rows)
1282 self.timeline.toggleDraftLayersRequested.connect(
1283 self.layers.set_draft_rows
1284 )
1286 self.canvas.imagesDropped.connect(self.import_dropped_images)
1287 self.canvas.projectDropped.connect(self.project.open_dropped)
1288 self.canvas.timeRemapDropped.connect(self.time_remap.open_dropped)
1289 self.canvas.clipAnimationDropped.connect(self.importer.clip_animation)
1290 self.canvas.colorSampled.connect(self.colors.apply_sampled_color)
1291 self.canvas.status_message.connect(
1292 lambda message: self.statusBar().showMessage(message, 2500)
1293 )
1294 self.canvas.viewChanged.connect(self.sync_canvas_view_controls)
1295 self.canvas.onionInteractionChanged.connect(
1296 self.onion.sync_browser_from_canvas
1297 )
1298 self.canvas.onionInteractionFinished.connect(
1299 self.onion.finish_browser_interaction
1300 )
1302 self.palette.isolateColorClicked.connect(self.used_color.apply_palette_isolate_color)
1303 self.palette.mainColorRequested.connect(
1304 lambda color: self.colors.apply_sampled_color_to_mode("main", color)
1305 )
1306 self.palette.sourceScreenColorPicked.connect(self.colors.apply_sampled_color)
1307 self.palette.applyReplacementRequested.connect(
1308 self.used_color.apply_palette_replacements
1309 )
1310 self.palette.saveReplacementSetRequested.connect(
1311 self.used_color.save_replacement_set
1312 )
1313 self.palette.loadReplacementSetRequested.connect(
1314 self.used_color.load_replacement_set
1315 )
1316 self.palette.previewGroupsChanged.connect(self.colors.set_preview_color_groups)
1317 self.palette.mergeColorsRequested.connect(self.used_color.apply_palette_merge)
1318 self.palette.deleteColorsRequested.connect(
1319 self.used_color.apply_palette_delete
1320 )
1321 self.palette.adjustLineThicknessRequested.connect(
1322 self.colors.adjust_parent_line_thickness
1323 )
1324 self.palette.adjustHsvRequested.connect(
1325 self.used_color.adjust_used_color_hsv
1326 )
1327 self.palette.focusColorRequested.connect(self.colors.focus_used_color)
1328 self.palette.clearIsolateRequested.connect(
1329 self.colors.clear_selected_color_filter
1330 )
1331 self.palette.maskColorsChanged.connect(self.colors.set_mask_colors)
1332 self.palette.selectedColorsChanged.connect(self.colors.set_selected_used_colors)
1333 self.palette.visibleColorsChanged.connect(self.colors.set_visible_colors)
1334 self.palette.historyStatePush.connect(self.used_color.push_palette_history)
1335 self.history_panel.jumpRequested.connect(self.used_color.jump_history)
1336 self.history_panel.branchSwitchRequested.connect(
1337 self.used_color.switch_history_branch
1338 )
1339 # 編集のたびにヒストリー一覧を更新する。
1340 self.canvas.selectionChanged.connect(self.used_color.refresh_history_panel)
1341 self.canvas.changed.connect(self.used_color.refresh_history_panel)
1342 self.canvas.cellChanged.connect(
1343 lambda *_args: self.used_color.refresh_history_panel()
1344 )
1347 def show_about_dialog(self):
1348 """Show version, copyright and the license notices.
1350 Crediting the LGPL-licensed Qt/PySide6 and Qt-Advanced-Docking-System
1351 components is a distribution requirement (LGPLv3 4a/4c), not
1352 decoration -- keep this reachable from the menu. See
1353 THIRD_PARTY_LICENSES.md for the full compliance notes.
1354 """
1355 repo_url = f"https://github.com/{GITHUB_REPO}"
1356 QMessageBox.about(
1357 self,
1358 tr("{app} について").format(app=APP_NAME),
1359 tr("""<h3>{app}</h3>
1360<p>Copyright © 2026 PaintMaskAnimator contributors</p>
1361<p>本ソフトウェアは <b>Apache License 2.0</b> のもとで配布されています。
1362商用・非商用を問わず、自由に利用・改変・再配布できます。</p>
1363<p>本ソフトウェアは「現状有姿」で提供され、明示黙示を問わず<b>いかなる保証もありません</b>。
1364詳細はライセンス全文を参照してください。</p>
1365<p>ライセンス全文: <a href="https://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a><br>
1366ソースコード: <a href="{repo}">{repo}</a></p>
1367<p><b>サードパーティコンポーネント</b><br>
1368Qt for Python (PySide6) — LGPLv3<br>
1369Qt Advanced Docking System — LGPL-2.1<br>
1370NumPy — BSD-3-Clause / Pillow — MIT-CMU / psd-tools — MIT</p>
1371<p>これらの LGPL ライブラリは差し替え可能な形で同梱されています。ライセンス全文と
1372対応ソースの入手先は、配布物内の <tt>THIRD_PARTY_LICENSES.md</tt> を参照してください。</p>""").format(
1373 app=APP_DISPLAY_NAME, repo=repo_url
1374 ),
1375 )