画布绝对和相对坐标,delta像素检索

时间:2016-11-15 22:20:07

标签: python python-3.x tkinter tkinter-canvas

在这个测试脚本中,我绘制了一个正方形,我可以使用鼠标滚轮放大。 如果我右键单击一个单元格,我得到正确的单元格坐标(不是x和y,而是列和行):这正是我期望它在后台写入控制台的。 相反,如果我通过按下鼠标左键并将其拖动到其他位置来移动画布,则坐标不再正确。

我从哪里获得delta x和delta y(或偏移量)以返回正确的信息?

供参考: 1)get_pos()是执行检查并生成结果的方法。 2)以下代码已经在运行Python 3.5.2的Ubuntu 16.10(带有最新更新)上进行了测试。

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
    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 = 1

    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

    zoom = self.zoom
    if self.drawn() and 1 != zoom:
        if self.zoom < 1:
            self.zoom = zoom = 1
            print('no zooming below 1.')
        else:
            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:', self.zoom, '  col, row:', 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 = 9

    self.zoom += 1
    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 -= 1
    if self.zoom < 1:
        print("Can't go below one.")
        self.zoom = 1
        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)

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

    scale = zoom * 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 'canvas' not 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()

1 个答案:

答案 0 :(得分:0)

看一下previously answered question,我学会了如何找到我想要的delta x和delta y。

所以,解决方案是:

    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)

        # the following line stores deltax and deltay into x0, y0 
        x0, y0 = int(self.canvasx(0)), int(self.canvasy(0))

        # then it is trivial to compute the solution
        xa, ya = x1 - x0, y1 - y0
        xb, yb = x2 - x0, y2 - y0

        if not (xa <= x <= xb and ya <= y <= yb):
            print('Here we are out of bounds.')
            return None, None

        scale = zoom * cell_dim

        col = (x - xa) // scale
        row = (y - ya) // scale

        return col, row