能否在tkinter中同时调整位置和宽度时调整overflowdirect窗口的大小而不闪烁?

问题描述 投票:0回答:1

我有一个溢出直接窗口,我需要通过鼠标拖动来调整其大小。

该示例正在运行,但是当我用 sw、w、nw、n 和 ne 边缘/角拖动时,它会导致闪烁(快速拖动时最明显),很可能是因为在这种情况下我调整宽度/高度和同时定位。

我尝试更新idle_tasks,看看是否可以平滑过渡,但事实并非如此。

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()

python user-interface tkinter winapi window
1个回答
0
投票

既然你已经明确了你真正想要做什么,我可以建议你使用这个方法吗?简短的解释是:当按下“截图”按钮时,整个屏幕都会被接管,您可以在要捕获屏幕截图的地方拖出一个方块,然后单击“保存”来保存它。它没有与您尝试实现的控件相同的控件,但它运行非常流畅,并实现了仅捕获屏幕的一部分的最终目标。您可以按照您喜欢的方式使用此代码。

import cv2
import numpy as np
import tkinter as tk
from os import mkdir, path, getcwd
from time import strftime
from PIL import ImageGrab

class ScrollFrame(tk.Frame):
    def __init__(self, master, row=0, column=0, scrollspeed=.02, **kwargs):        
        tk.Frame.__init__(self, master, **kwargs)
        self.grid(row=row, column=column, sticky='nswe')

        self.scrollspeed = scrollspeed

        self.canvas = tk.Canvas(self, highlightthickness=0)
        self.canvas.grid(column=0, row=0, sticky='nswe')

        self.v_scroll = tk.Scrollbar(self, orient='vertical', command=self.canvas.yview)
        self.v_scroll.grid(row=0, column=1, sticky='ns')

        self.canvas.configure(yscrollcommand=self.v_scroll.set)
        self.canvas.bind_all('<MouseWheel>', self.on_mousewheel)

        self.frame = tk.Frame(self.canvas, height=0)
        self.frame.grid_columnconfigure(0, weight=1)
        self.frame.bind('<Configure>', lambda e:self.canvas.configure(scrollregion=self.canvas.bbox("all")))
        self.canvas.create_window((0,0), window=self.frame, anchor="nw")

        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(0, weight=1)

    def on_mousewheel(self, event):
        self.canvas.yview_moveto(self.v_scroll.get()[0]+((-event.delta/abs(event.delta))*self.scrollspeed))                       
        
        
#a slightly more interactive button
class Button(tk.Button):
    def __init__(self, master, text, command, row, column, **kwargs):
        self.default = {**dict(
            foreground       = 'gray60',
            background       = 'gray10',
            activebackground = 'gray60',
            activeforeground = 'gray10',
            font             = 'Helvetica 28 bold',
            padx             = 10,
            pady             = 10,
            border           = 2,
        ), **kwargs}
        tk.Button.__init__(self, master, text=text, command=command, **self.default)
        self.grid(row=row, column=column, sticky='nswe')
        
        self.hover = self.default.copy()
        self.hover['background'] = 'steel blue'
        self.hover['foreground'] = 'black'
        
        self.bind("<Enter>", self.on_enter)
        self.bind("<Leave>", self.on_leave)
        
    def on_enter(self, event):
        if self['state'] != 'disabled':
            self.configure(**self.hover)
        
    def on_leave(self, event):
        self.configure(**self.default)


class ImageItem(tk.Frame):    
    def __init__(self, master, text, image, **kwargs):
        tk.Frame.__init__(self, master, **kwargs)
        self.grid_columnconfigure(0, weight=1)
        def_label = dict(
            height=1, 
            anchor='nw', 
            foreground='blue', 
            font='calibri 14'
        )        
        self.file_lbl = Label(self, text=text, **def_label)
        self.file_lbl.grid(row=0, column=0, sticky='nswe')
        self.file_lbl.bind('<1>', self.display)
        
        self.image = image
        
        self.save_btn = Button(self, 'Save', self.save, 0, 1)
        
    def display(self):    
        #display image
        cv2.imshow('Captured Image Preview', cv2.cvtColor(np.array(self.image), cv2.COLOR_BGR2RGB))

        
    def save(self):
        cv2.destroyAllWindows()                   #destroy image preview
        
        #save image to disk and reset reference
        if self.image:
            self.image.save(f'snips/snip_{strftime("%a_%b_%d_%Y_%I_%M_%S")}.png')
            self.image = None
        
        
class ScreenSnip(tk.Toplevel):
    def __init__(self, master):
        tk.Toplevel.__init__(self, master)
        #config toplevel
        self.configure(cursor='cross')
        self.attributes('-fullscreen', True)
        self.attributes('-alpha', 0.2)
        
        #init vars
        self.b_x = 0
        self.b_y = 0
        self.e_x = 0
        self.e_y = 0
        self.drag= False
        
        #create and config canvas
        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):
        #store initial click coordinates
        self.b_x = event.x
        self.b_y = event.y

    def on_drag(self, event):
        #gather drag coordinates and tag current rect in canvas
        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()
        
        #if the user didn't drag
        if not self.drag:
            self.b_x -= 400
            self.b_y -= 300
            self.e_x = self.b_x + 800
            self.e_y = self.b_y + 600
        
        #get image based on drag coordinates
        self.master.image = ImageGrab.grab(bbox=(min(self.b_x, self.e_x), min(self.b_y, self.e_y),
                                                 max(self.b_x, self.e_x), max(self.b_y, self.e_y)))
                                                 
        cv2.imshow('Captured Image Preview', cv2.cvtColor(np.array(self.master.image), cv2.COLOR_BGR2RGB))

class App(tk.Tk):
    def __init__(self, **kwargs):
        tk.Tk.__init__(self, **kwargs)
        #for storing a snip reference
        self.image = None
        
        #create directory to store snips, if it doesn't exist
        dir = path.join(getcwd(), 'snips/')
        if not path.isdir(dir):
            mkdir(dir)
        
        #snip button
        Button(self, 'Snip', self.snip, 0, 0)
        
        #self.scrollframe = ScrollFrame(self, 1, 0)
        
        #save button
        self.save_btn = Button(self, 'Save', self.save, 0, 1)
        self.save_btn.configure(state='disabled')
        
    def snip(self):
        self.save_btn.configure(state='normal')   #enable save button
        ScreenSnip(self)                          #start snipper
        
        
    def save(self):
        cv2.destroyAllWindows()                   #destroy image preview
        
        #save image to disk and reset reference
        if self.image:
            self.image.save(f'snips/snip_{strftime("%a_%b_%d_%Y_%I_%M_%S")}.png')
            self.image = None
      
if __name__ == '__main__':
    app = App()
    app.title("Snip-N-Save")
    app.resizable(width=False, height=False)
    app.mainloop()
© www.soinside.com 2019 - 2024. All rights reserved.