You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何消除Tkinter中overrideredirect窗口拖动调整时的闪烁?

Tkinter overrideredirect窗口拖动调整大小闪烁问题及解决方案

问题描述

我有一个overrideredirect窗口,需要通过鼠标拖动调整其大小。当前示例代码可正常运行,但拖动sw、w、nw、n和ne手柄时会出现闪烁(快速拖动时尤为明显),推测是同时调整窗口位置与尺寸导致的。我尝试使用update_idletasks平滑过渡,但无效果。

import tkinter as tk


class Box(tk.Tk):
    def __init__(self):
        super().__init__()
        self.drag_point_x = None
        self.drag_point_y = None
        self.drag_margin = 15
        self.width = 400
        self.height = 200
        self.x = 300
        self.y = 300

        self.geometry(f"{self.width}x{self.height}+{self.x}+{self.y}")
        self.overrideredirect(1)

        self.bind("<B1-Motion>", self.on_dragging)
        self.bind("<Button-1>", self.on_click)
        self.bind("<Motion>", self.on_motion)

    def on_dragging(self, event):
        mouse_x = self.winfo_pointerx()
        mouse_y = self.winfo_pointery()
        delta_width = self.x - mouse_x
        delta_height = self.y - mouse_y

        if self.drag_point == 'e':
            self.width = mouse_x - self.x
        elif self.drag_point == 'se':
            self.width = mouse_x - self.x
            self.height = mouse_y - self.y
        elif self.drag_point == 's':
            self.height = mouse_y - self.y
        elif self.drag_point == 'sw':
            self.height = mouse_y - self.y
            self.width += delta_width
            self.x = mouse_x
        elif self.drag_point == 'w':
            self.width += delta_width
            self.x = mouse_x
        elif self.drag_point == 'nw':
            self.width += delta_width
            self.height += delta_height
            self.x = mouse_x
            self.y = mouse_y
        elif self.drag_point == 'n':
            self.height += delta_height
            self.y = mouse_y
        elif self.drag_point == 'ne':
            self.width = mouse_x - self.x
            self.height += delta_height
            self.y = mouse_y
        self.geometry(f"{self.width}x{self.height}+{self.x}+{self.y}")

    def on_click(self, event):
        self.drag_point_x = event.x
        self.drag_point_y = event.y

    def on_motion(self, event):
        if event.x < self.drag_margin:
            if event.y < self.drag_margin:
                self.config(cursor="size_nw_se")
                self.drag_point = "nw"
            elif event.y > self.height - self.drag_margin:
                self.config(cursor="size_ne_sw")
                self.drag_point = "sw"
            else:
                self.config(cursor="size_we")
                self.drag_point = "w"
        elif event.x > self.width - self.drag_margin:
            if event.y < self.drag_margin:
                self.config(cursor="size_ne_sw")
                self.drag_point = "ne"
            elif event.y > self.height - self.drag_margin:
                self.config(cursor="size_nw_se")
                self.drag_point = "se"
            else:
                self.config(cursor="size_we")
                self.drag_point = "e"
        elif event.y < self.drag_margin:
            self.config(cursor="size_ns")
            self.drag_point = "n"
        elif event.y > self.height - self.drag_margin:
            self.config(cursor="size_ns")
            self.drag_point = "s"
        else:
            self.config(cursor="")
            self.drag_point = None


if __name__ == "__main__":
    box = Box()
    box.mainloop()

更新:带多显示器支持的解决方案代码

以下是基于OneMadGypsy的解决方案编写的代码,新增了多显示器支持:

import tkinter as tk
from PIL import ImageGrab
from screeninfo import get_monitors


class Overlay(tk.Toplevel):
    def __init__(self, monitor, overlays):
        tk.Toplevel.__init__(self)
        self.b_x = 0
        self.b_y = 0
        self.e_x = 0
        self.e_y = 0
        self.drag = False
        self.overlays = overlays
        self.monitor = monitor

        self.configure(cursor='cross')
        self.attributes('-alpha', 0.3)
        self.attributes('-topmost', True)

        self.overrideredirect(True)
        self.geometry(f"{self.monitor.width}x{self.monitor.height}+{self.monitor.x}+{self.monitor.y}")

        self.canvas = tk.Canvas(self, bg='dark gray')
        self.canvas.pack(fill='both', expand=True)
        self.canvas.bind('<B1-Motion>', self.on_drag)
        self.canvas.bind('<ButtonPress-1>', self.on_press)
        self.canvas.bind('<ButtonRelease-1>', self.on_release)
        rect = dict(
            outline='#0052d6',
            fill='white',
            tags='snip_rect',
            width=2,
        )
        self.canvas.create_rectangle(0, 0, 0, 0, **rect)

    def on_press(self, event):
        self.b_x = event.x
        self.b_y = event.y

    def on_drag(self, event):
        self.drag = True
        self.e_x = event.x
        self.e_y = event.y
        self.canvas.coords('snip_rect', self.b_x, self.b_y, self.e_x, self.e_y)

    def on_release(self, event):
        self.destroy()
        for window in self.overlays:
            if window:
                window.destroy()
        self.overlays.clear()

        if not self.drag:
            return

        adjusted_b_x = self.b_x + self.monitor.x
        adjusted_b_y = self.b_y + self.monitor.y
        adjusted_e_x = self.e_x + self.monitor.x
        adjusted_e_y = self.e_y + self.monitor.y

        image = ImageGrab.grab(bbox=(min(adjusted_b_x, adjusted_e_x),
                                     min(adjusted_b_y, adjusted_e_y),
                                     max(adjusted_b_x, adjusted_e_x),
                                     max(adjusted_b_y, adjusted_e_y)), all_screens=True)
        image.show()



class Snipper:
    def __init__(self):
        self.image = None
        self.overlays = []

    def snip(self):
        monitors = get_monitors()

        for monitor in monitors:
            overlay = Overlay(monitor, self.overlays)
            self.overlays.append(overlay)


if __name__ == '__main__':
    class App(tk.Tk):
        def __init__(self,** kwargs):
            tk.Tk.__init__(self, **kwargs)
            self.image = None
            self.windows = []
            self.snipper = Snipper()
            tk.Button(self, text='snip', width=50, command=self.snipper.snip).pack()

    app = App()
    app.mainloop()

内容的提问来源于stack exchange,提问作者Jason Fyle

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 10:44:54