Сделайте виджеты / рамки внутри холста tkinter изменяемого размера - PullRequest
1 голос
/ 05 января 2012

У меня есть несколько текстовых виджетов, плавающих в прокручиваемом виджете холста.Я хочу позволить пользователям изменять их размер, перетаскивая их края и / или углы, возможно перемещая их, если они перетаскивают верхние левые края или угол.Я открыт для того, чтобы превращать их в фреймы с текстовыми виджетами внутри, так как я, вероятно, все равно это сделаю.

Я полагаю, что смогу обработать события вручную, если смогу заставить их отображать ручки изменения размера.Нужно ли мне ловить указатели мыши и щелчки на границе?

Добавление привязки события "" ничего не дает, как и следовало ожидать.ttk.Sizegrip предположительно работает только на окнах верхнего уровня.Существует множество ресурсов для предотвращения изменения размеров, но очень мало для их облегчения, и все они, похоже, предназначены для окон верхнего уровня.

Ответы [ 2 ]

2 голосов
/ 09 января 2012

В итоге я сделал рамку вокруг виджета с жирной рамкой, перехватывал события мыши и сам обрабатывал всю грязную логику изменения размера.

Мне нужно было записать как начальное местоположение местоположения щелчка, так и позицию каждого последующего перемещения мыши, и использовать их для изменения размеров сверху / слева и снизу / справа соответственно.

Редактировать: Вот удобная (относительно) инкапсулированная реализация.

from Tkinter import *

class ResizableCanvasFrame(Frame):
    '''
    Class that handles creating resizable frames on a canvas.
    Don't pack it.

    Set save_callback to whatever you want to happen when the mouse
    lets up on the border. You can catch <Configure> too, but at least
    in my case I didn't want to save the new position on every mouse move.
    '''
    def __init__(self, master, x, y, w, h, *args, **kwargs):
        # master should be a Canvas
        self.frame_thickness = 5
        Frame.__init__(
            self,
            master,
            *args,
            borderwidth = self.frame_thickness,
            cursor = 'fleur',
            **kwargs
        )
        self.canvas = master
        self.resize_state = None
        self.bind('<Button-1>', self.mousedown)
        self.bind('<B1-Motion>', self.mousemove)
        self.bind('<ButtonRelease-1>', self.mouseup)
        self.bind('<Destroy>', self.delete_item)
        # add self to canvas
        self.itemid = self.canvas.create_window(
            x,
            y,
            window=self,
            anchor="nw",
            width=w,
            height=h
        )
        self.save_callback = None

    def canvas_coords(self):
        return map(int, self.canvas.coords(self.itemid))

    def move(self, dx, dy):
        # strictly, this is out of the range of RCF,
        # but it helps with the law of demeter
        self.canvas.move(self.itemid, dx, dy)

    def mousedown(self, event):
        window_width = self.winfo_width()
        window_height = self.winfo_height()
        self.resize_state = {
            'start_coords': (event.x, event.y),
            'last_coords': (event.x, event.y),
            'left_edge': (0 <= event.x < self.frame_thickness),
            'right_edge': (window_width - self.frame_thickness <= event.x < window_width),
            'top_edge': (0 <= event.y < self.frame_thickness),
            'bottom_edge': (window_height - self.frame_thickness <= event.y < window_height),
        }            

    def mousemove(self, event):
        if self.resize_state:
            resize = self.resize_state # debug var
            event_x = event.x
            event_y = event.y
            # distance of cursor from original position of window
            delta = map(int, (event.x - self.resize_state['start_coords'][0],
                              event.y - self.resize_state['start_coords'][1]))
            # load current pos, size
            new_x, new_y = self.canvas_coords()
            new_width = int(self.canvas.itemcget(self.itemid, 'width'))
            new_height = int(self.canvas.itemcget(self.itemid, 'height'))
            # handle x resize/move
            if self.resize_state['left_edge']:
                # must move pos and resize
                new_x += delta[0]
                new_width -= delta[0]
            elif self.resize_state['right_edge']:
                new_width += (event.x - self.resize_state['last_coords'][0])
            # handle y resize/move
            if self.resize_state['top_edge']:
                new_y += delta[1]
                new_height -= delta[1]
            elif self.resize_state['bottom_edge']:
                new_height += (event.y - self.resize_state['last_coords'][1])
            # save new settings in item, not card yet
            self.resize_state['last_coords'] = (event.x, event.y)
            self.canvas.coords(self.itemid, new_x, new_y)
            self.canvas.itemconfig(self.itemid, width=new_width, height=new_height)

    def mouseup(self, event):
        if self.resize_state:
            self.resize_state = None
            if self.save_callback:
                self.save_callback()

    def delete_item(self, event):
        self.canvas.delete(self.itemid)
0 голосов
/ 19 августа 2012

Вы можете использовать виджет PanedWindow или комбинацию нескольких из них внутри своего холста.Они предназначены для этого.* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

* * * * * * * * * * * * * * * * * * * *1004* * * * * * * * * * * * * *} * * * * * * * *1004* * * * * * * * * * * * * * * * * * * * * *} * * * * *1004* * * * * * * * * * *1004* * * * * * * * * * * *1004* * * * * * * * * *1004* * * * * * * * * * * * * * * * * * * * * * * * * * 100 *

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...