如何在tkinter中重置画布视图?

2024-07-05 09:46:32 发布

您现在位置:Python中文网/ 问答频道 /正文

当我在画布上画一个矩形时,我知道它的坐标,绝对坐标(canvasx(),canvasy())和相对坐标(我传递的那些)。 然后,我可以使用canvas scan_mark()和scan_dragto()方法移动画布。 如何返回原始位置(在一个或多个scan_mark()/scan_dragto()调用之前)以重新居中用户可能丢失位置的矩形? 是否有重置视图命令/方法?我如何跟踪发生的变化?在


Tags: 方法用户命令视图scan画布重置canvas
2条回答

正如前面的评论@jasonharper,他的建议奏效了。对于其他可能有同样问题的人,我附上了一个工作示例,尽管它没有被清理得那么好,值得骄傲,它可能会让你看到放大和缩小,视图拖动和重置如何工作。在

import tkinter as tk
import tkinter.ttk as ttk


class GriddedMazeCanvas(tk.Canvas):

    def almost_centered(self, cols, rows):

        width = int(self['width'])
        height = int(self['height'])
        cell_dim = self.settings['cell_dim']
        rows = rows % height
        cols = cols % width

        w = cols * cell_dim
        h = rows * cell_dim

        if self.zoom < 0:
            raise ValueError('zoom is negative:', self.zoom)

        zoom = self.zoom // 2 + 1
        if self.drawn() and 1 != zoom:
            w *= zoom
            h *= zoom

        h_shift = (width - w) // 2
        v_shift = (height - h) // 2

        return [h_shift, v_shift,
                h_shift + w, v_shift + h]

    def __init__(self, *args, **kwargs):
        if 'settings' not in kwargs:
            raise ValueError("'settings' not passed.")
        settings = kwargs['settings']
        del kwargs['settings']

        super().__init__(*args, **kwargs)

        self.config(highlightthickness=0)

        self.settings = settings
        self.bind_events()

    def draw_maze(self, cols, rows):

        self.cols = cols
        self.rows = rows

        if self.not_drawn():
            self.cells = {}
            self.cell_dim = self.settings['cell_dim']
            self.border_thickness = self.settings['border_thickness']
            self.zoom = 0

        self.delete(tk.ALL)

        maze, coords = self._draw_maze(cols, rows, fix=False)
        lines = self._draw_grid(coords)

        return maze, lines

    def _draw_maze(self, cols, rows, fix=True):
        data = self.settings

        to_max = data['to_max']
        border_thickness = data['border_thickness']
        poligon_color = data['poligon_color']
        poligon_border_color = data['poligon_border_color']

        coords = self.almost_centered(cols, rows)

        if fix:
            # Fix for the disappearing NW borders
            if to_max == cols:
                coords[0] += 1
            if to_max == rows:
                coords[1] += 1

        maze = self.create_rectangle(*coords,
                                     fill=poligon_color,
                                     outline=poligon_border_color,
                                     width=border_thickness,
                                     tag='maze')
        return maze, coords

    def _draw_grid(self, coords):
        data = self.settings
        poligon_border_color = data['poligon_border_color']
        cell_dim = data['cell_dim']

        if coords is None:
            if self.not_drawn():
                raise ValueError('The maze is still uninitialized.')
            x1, y1, x2, y2 = self.almost_centered(self.cols, self.rows)
        else:
            x1, y1, x2, y2 = coords

        if self.drawn() and 0 != self.zoom:
            if self.zoom < 0:
                self.zoom = 0
                print('no zooming at negative values.')
            else:
                zoom = self.zoom // 2 + 1
                cell_dim *= zoom

        lines = []

        for i, x in enumerate(range(x1, x2, cell_dim)):
            line = self.create_line(x, y1, x, y2,
                                    fill=poligon_border_color,
                                    tags=('grid', 'grid_hl_{}'.format(i)))
            lines.append(line)

        for i, y in enumerate(range(y1, y2, cell_dim)):
            line = self.create_line(x1, y, x2, y,
                                    fill=poligon_border_color,
                                    tags=('grid', 'grid_vl_{}'.format(i)))
            lines.append(line)

        return lines

    def drawn(self):
        return hasattr(self, 'cells')

    def not_drawn(self):
        return not self.drawn()

    def bind_events(self):

        self.bind('<Button-4>', self.onZoomIn)
        self.bind('<Button-5>', self.onZoomOut)

        self.bind('<ButtonPress-1>', self.onScrollStart)
        self.bind('<B1-Motion>', self.onScrollMove)
        self.tag_bind('maze', '<ButtonPress-3>', self.onMouseRight)

    def onScrollStart(self, event):
        print(event.x, event.y, self.canvasx(event.x), self.canvasy(event.y))
        self.scan_mark(event.x, event.y)

    def onMouseRight(self, event):
        col, row = self.get_pos(event)
        print('zoom, col, row:', self.zoom, col, row)

    def onScrollMove(self, event):
        delta = event.x, event.y
        self.scan_dragto(*delta, gain=1)

    def onZoomIn(self, event):
        if self.not_drawn():
            return

        max_zoom = 16

        self.zoom += 2
        if self.zoom > max_zoom:
            print("Can't go beyond", max_zoom)
            self.zoom = max_zoom
            return

        print('Zooming in.', event.num, event.x, event.y, self.zoom)
        self.draw_maze(self.cols, self.rows)

    def onZoomOut(self, event):
        if self.not_drawn():
            return

        self.zoom -= 2
        if self.zoom < 0:
            print("Can't go below zero.")
            self.zoom = 0
            return

        print('Zooming out.', event.num, event.x, event.y, self.zoom)
        self.draw_maze(self.cols, self.rows)

    def get_pos(self, event):
        x, y = event.x, event.y
        cols, rows = self.cols, self.rows
        cell_dim, zoom = self.cell_dim, self.zoom
        x1, y1, x2, y2 = self.almost_centered(cols, rows)

        if not (x1 <= x <= x2 and y1 <= y <= y2):
            print('Here we are out of bounds.')
            return None, None

        scale = (zoom // 2 + 1) * cell_dim

        col = (x - x1) // scale
        row = (y - y1) // scale

        return col, row


class CanvasButton(ttk.Button):

    def freeze_origin(self):
        if not hasattr(self, 'origin'):
            canvas = self.canvas
            self.origin = canvas.xview()[0], canvas.yview()[0]

    def reset(self):
        canvas = self.canvas
        x, y = self.origin
        canvas.yview_moveto(x)
        canvas.xview_moveto(y)

    def __init__(self, *args, **kwargs):
        if not 'canvas' in kwargs:
            raise ValueError("'canvas' not passed.")
        canvas = kwargs['canvas']
        del kwargs['canvas']

        super().__init__(*args, **kwargs)
        self.config(command=self.reset)

        self.canvas = canvas


root = tk.Tk()

settings = {'cell_dim': 3,
            'to_max': 200,
            'border_thickness': 1,
            'poligon_color': '#F7F37E',
            'poligon_border_color': '#AC5D33'}

frame = ttk.Frame(root)
canvas = GriddedMazeCanvas(frame,
                           settings=settings,
                           width=640,
                           height=480)
button = CanvasButton(frame, text='Reset', canvas=canvas)
button.freeze_origin()

canvas.draw_maze(20, 10)

canvas.grid(row=0, column=0, sticky=tk.NSEW)
button.grid(row=1, column=0, sticky=tk.EW)
frame.rowconfigure(0, weight=1)
frame.grid()

root.mainloop()

在最新的Ubuntu16.10上用Python3.4进行了测试 (事件:鼠标左键、鼠标右键、鼠标滚轮、按键) 高温

我看到的Tkinter文档似乎没有提到这一点,但是可以调用底层的Tk Canvas xview/yview方法来获取当前的滚动位置(实际上,作为两个元素的元组,第二个元素对您没有任何用处)。因此,以下方法应该有效(未经测试):

保存位置:

origX = yourcanvas.xview()[0]
origY = yourcanvas.yview()[0]

恢复位置:

^{pr2}$

相关问题 更多 >