我有一个溢出直接窗口,我需要通过鼠标拖动来调整其大小。
该示例正在运行,但是当我用 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()
既然你已经明确了你真正想要做什么,我可以建议你使用这个方法吗?简短的解释是:当按下“截图”按钮时,整个屏幕都会被接管,您可以在要捕获屏幕截图的地方拖出一个方块,然后单击“保存”来保存它。它没有与您尝试实现的控件相同的控件,但它运行非常流畅,并实现了仅捕获屏幕的一部分的最终目标。您可以按照您喜欢的方式使用此代码。
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()