I want to implement a custom redo fuction in GUI app using Tkinter on Linux (Wayland).
This is my current code:
"""Plotting canvas."""
from PIL import Image
from PIL.ImageTk import PhotoImage
from tkinter import NW
from tkinter import Canvas
from tkinter import Event
from tkinter import Tk
from diffusion_scribbles.functions import overlay
from diffusion_scribbles.gui.colors import BLUE, RED
from diffusion_scribbles.selections import DiffusionSelection, Selection
from diffusion_scribbles.types import Color, Dataset, Vector2D
__all__ = ['PlotCanvas']
class PlotCanvas(Canvas):
"""Image canvas."""
def __init__(self, root: Tk, width: int, height: int, **kwargs):
super().__init__(root, width=width, height=height, **kwargs)
self.width = width
self.height = height
self.selection_method: Selection = DiffusionSelection(width, height)
self.bind("<Button-1>", self.start_draw)
self.bind("<B1-Motion>", self.draw)
self.bind("<ButtonRelease-1>", self.stop_draw)
root.bind('<Control-z>', self.undo)
root.bind('<Control-Shift-KeyPress-z>', self.redo)
self._dataset: Dataset | None = None
self._data_points: Image.Image | None = None
self._scribble: Image.Image | None = None
self._image: Image.Image | None = None
self._photo_image: PhotoImage | None = None
self.primary_color: Color = RED
self.secondary_color: Color = BLUE
@property
def dataset(self) -> Dataset | None:
"""Returns the dataset."""
return self._dataset
@dataset.setter
def dataset(self, dataset: Dataset) -> None:
"""Sets the dataset."""
self._dataset = dataset
self._scribble = None
self.image = self._data_points = dataset.draw(
(self.width, self.height)
)
@property
def data_points(self) -> Image.Image:
"""Returns the data points image."""
return self._data_points
@data_points.setter
def data_points(self, data_points: Image.Image) -> None:
"""Sets the data points image."""
self._data_points = data_points
self.image = overlay(data_points, self.scribble)
@property
def scribble(self) -> Image.Image:
"""Returns the scribble image."""
return self._scribble
@scribble.setter
def scribble(self, scribble: Image.Image) -> None:
"""Sets the scribble image."""
self._scribble = scribble
self.image = overlay(self.data_points, scribble)
@property
def image(self) -> Image.Image:
"""Returns the currently displayed image."""
return self._image
@image.setter
def image(self, image: Image.Image) -> None:
"""Sets the currently displayed image."""
self._image = image
self.photo_image = PhotoImage(self._image)
@property
def photo_image(self) -> PhotoImage:
"""Returns the current photo image."""
return self._photo_image
@photo_image.setter
def photo_image(self, photo_image: PhotoImage) -> None:
"""Sets the current photo image."""
self._photo_image = photo_image
self.create_image(0, 0, anchor=NW, image=self._photo_image)
def start_draw(self, event: Event) -> None:
"""Start drawing a line."""
if self.dataset:
self.selection_method.start(
Vector2D(event.x, event.y),
self.primary_color,
self.secondary_color
)
def draw(self, event: Event) -> None:
"""Draw lines."""
if not self.dataset:
return
self.scribble = self.selection_method.move(Vector2D(event.x, event.y))
def stop_draw(self, _: Event) -> None:
"""Stop drawing a line."""
if not self.dataset:
return
self.selection_method.stop()
self.data_points = self.selection_method.colorize(self.dataset)
def undo(self, _: Event) -> None:
"""Undo last action."""
self.scribble = self.selection_method.undo()
self.data_points = self.selection_method.colorize(self.dataset)
def redo(self, _: Event) -> None:
"""Undo last action."""
print('Redo.', flush=True)
While undo works just fine, redo()
is never executed when I press the appropriate key combination of Ctrl Shift Z
. I also tried other variants such as '<Control-Shift-z>'
, '<Shift-Control-KeyPress-z>'
and '<Control-Shift-KeyPress-z>'
to no avail.
How can I bind the key combination Ctrl Shift Z for a custom redo action on Linux under Wayland?
Minimal reproducible example
from functools import partial
from tkinter import Tk
class Window(Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.bind('<Control-z>', partial(print, 'Undo'))
self.bind('<Control-Shift-KeyPress-z>', partial(print, 'Redo'))
if __name__ == '__main__':
Window().mainloop()
CodePudding user response:
Code works for me if I use upper case Z
instead of Shift-z
(with lower case z
)
import tkinter as tk
def test1(event):
print('test1:', event)
def test2(event):
print('test2:', event)
root = tk.Tk()
root.bind('<Control-z>', test1) # Control Z
root.bind('<Control-Z>', test2) # Control Shift Z
root.mainloop()
If I use '<Control-Shift-Z>'
then it also works because there is upper case Z
.