windows Tkinter:鼠标拖动一个没有边框的窗口,例如。覆盖直接(1)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4055267/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 15:30:20  来源:igfitidea点击:

Tkinter: Mouse drag a window without borders, eg. overridedirect(1)

pythonwindowsuser-interfacewindowtkinter

提问by Malcolm

Any suggestions on how one might create event bindings that would allow a user to mouse drag a window without borders, eg. a window created with overridedirect(1)?

关于如何创建允许用户用鼠标拖动无边框窗口的事件绑定的任何建议,例如。用overridedirect(1)?创建的窗口

Use case: We would like to create a floating toolbar/palette window (without borders) that our users can drag around on their desktop.

用例:我们想创建一个浮动工具栏/调色板窗口(无边框),我们的用户可以在桌面上拖动它。

Here's where I'm at in my thinking (pseudo code):

这是我的想法(伪代码):

  1. window.bind( '<Button-1>', onMouseDown )to capture the initial position of the mouse.

  2. window.bind( '<Motion-1>', onMouseMove )to track position of mouse once it starts to move.

  3. Calculate how much mouse has moved and calculate newX, newYpositions.

  4. Use window.geometry( '+%d+%d' % ( newX, newY ) )to move window.

  1. window.bind( '<Button-1>', onMouseDown )捕捉鼠标的初始位置。

  2. window.bind( '<Motion-1>', onMouseMove )一旦鼠标开始移动,就可以跟踪鼠标的位置。

  3. 计算鼠标移动了多少并计算newXnewY位置。

  4. 使用window.geometry( '+%d+%d' % ( newX, newY ) )到移动窗口。

Does Tkinter expose enough functionality to allow me to implement the task at hand? Or are there easier/higher-level ways to achieve what I want to do?

Tkinter 是否公开了足够的功能来让我实现手头的任务?或者是否有更简单/更高级别的方法来实现我想做的事情?

回答by Bryan Oakley

Yes, Tkinter exposes enough functionality to do this, and no, there are no easier/higher-level ways to achive what you want to do. You pretty much have the right idea.

是的,Tkinter 公开了足够的功能来做到这一点,不,没有更简单/更高级的方法来实现你想做的事情。你的想法几乎是正确的。

Here's one example, though it's not the only way:

这是一个例子,虽然它不是唯一的方法:

import tkinter as tk

class App(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.floater = FloatingWindow(self)

class FloatingWindow(tk.Toplevel):
    def __init__(self, *args, **kwargs):
        tk.Toplevel.__init__(self, *args, **kwargs)
        self.overrideredirect(True)

        self.label = tk.Label(self, text="Click on the grip to move")
        self.grip = tk.Label(self, bitmap="gray25")
        self.grip.pack(side="left", fill="y")
        self.label.pack(side="right", fill="both", expand=True)

        self.grip.bind("<ButtonPress-1>", self.start_move)
        self.grip.bind("<ButtonRelease-1>", self.stop_move)
        self.grip.bind("<B1-Motion>", self.do_move)

    def start_move(self, event):
        self.x = event.x
        self.y = event.y

    def stop_move(self, event):
        self.x = None
        self.y = None

    def do_move(self, event):
        deltax = event.x - self.x
        deltay = event.y - self.y
        x = self.winfo_x() + deltax
        y = self.winfo_y() + deltay
        self.geometry(f"+{x}+{y}")

app=App()
app.mainloop()

回答by David58

Here is my solution:

这是我的解决方案:

from tkinter import *
from webbrowser import *


lastClickX = 0
lastClickY = 0


def SaveLastClickPos(event):
    global lastClickX, lastClickY
    lastClickX = event.x
    lastClickY = event.y


def Dragging(event):
    x, y = event.x - lastClickX + window.winfo_x(), event.y - lastClickY + window.winfo_y()
    window.geometry("+%s+%s" % (x , y))


window = Tk()
window.overrideredirect(True)
window.attributes('-topmost', True)
window.geometry("400x400+500+300")
window.bind('<Button-1>', SaveLastClickPos)
window.bind('<B1-Motion>', Dragging)
window.mainloop()

回答by sjana

Try this, and it surely works;

试试这个,它肯定有效;

  1. Create an event function to move window:

    def movewindow(event): root.geometry('+{0}+{1}'.format(event.x_root, event.y_root))

  2. Bind window:

    root.bind('', movewindow)

  1. 创建一个事件函数来移动窗口:

    def movewindow(event): root.geometry('+{0}+{1}'.format(event.x_root, event.y_root))

  2. 绑定窗口:

    root.bind('', movewindow)

Now you can touch the the window and drag

现在您可以触摸窗口并拖动

回答by SkarmoutsosV

This code is the same as Bryan's solution but it does not use overridedirect.

此代码与 Bryan 的解决方案相同,但不使用 overridedirect。

It was tested with: python 3.7, Debian GNU/Linux 10 (buster), Gnome 3.30

它已经过测试:python 3.7、Debian GNU/Linux 10 (buster)、Gnome 3.30

import tkinter as tk


class App(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.floater = FloatingWindow(self)


class FloatingWindow(tk.Toplevel):
    def __init__(self, *args, **kwargs):
        tk.Toplevel.__init__(self, *args, **kwargs)

        #self.overrideredirect(True)
        self.resizable(0, 0)  # Window not resizable
        self.wm_attributes('-type', 'splash')  # Hide title bar (Linux)

        self.label = tk.Label(self, text="Click on the grip to move")
        self.grip = tk.Label(self, bitmap="gray25")
        self.grip.pack(side="left", fill="y")
        self.label.pack(side="right", fill="both", expand=True)

        self.grip.bind("<ButtonPress-1>", self.StartMove)
        self.grip.bind("<ButtonRelease-1>", self.StopMove)
        self.grip.bind("<B1-Motion>", self.OnMotion)

    def StartMove(self, event):
        self.x = event.x
        self.y = event.y

    def StopMove(self, event):
        self.x = None
        self.y = None

    def OnMotion(self, event):
        deltax = event.x - self.x
        deltay = event.y - self.y
        x = self.winfo_x() + deltax
        y = self.winfo_y() + deltay
        self.geometry("+%s+%s" % (x, y))


app = App()
app.mainloop()

回答by bruce

The idea of Lo?c Faure-Lacroix is useful, the following is my own simple code snippets on Python3.7.3, hope it will help:

Lo?c Faure-Lacroix的想法很有用,以下是我自己在Python3.7.3上的简单代码片段,希望对大家有帮助:

from tkinter import *


def move_window(event):
    root.geometry(f'+{event.x_root}+{event.y_root}')


root = Tk()
root.bind("<B1-Motion>", move_window)
root.mainloop()

But the position of the mouse is always in the upper left corner of the window. How can I keep it unchanged? Looking forward to a better answer!

但是鼠标的位置总是在窗口的左上角。我怎样才能保持它不变?期待更好的答案!



Thanks to Bryan Oakley, because at the beginning I couldn't run your code on my computer, I didn't pay attention to it. Just now after the modification, it was very good to run, and the above situation would not happen (the mouse is always in the upper left corner), The updated code recently as follows:

感谢Bryan Oakley,因为一开始我无法在我的电脑上运行你的代码,我没有注意它。刚才修改后,运行很好,不会出现上述情况(鼠标一直在左上角),最近更新的代码如下:

def widget_drag_free_bind(widget):
    """Bind any widget or Tk master object with free drag"""
    if isinstance(widget, Tk):
        master = widget  # root window
    else:
        master = widget.master

    x, y = 0, 0
    def mouse_motion(event):
        global x, y
        # Positive offset represent the mouse is moving to the lower right corner, negative moving to the upper left corner
        offset_x, offset_y = event.x - x, event.y - y  
        new_x = master.winfo_x() + offset_x
        new_y = master.winfo_y() + offset_y
        new_geometry = f"+{new_x}+{new_y}"
        master.geometry(new_geometry)

    def mouse_press(event):
        global x, y
        count = time.time()
        x, y = event.x, event.y

    widget.bind("<B1-Motion>", mouse_motion)  # Hold the left mouse button and drag events
    widget.bind("<Button-1>", mouse_press)  # The left mouse button press event, long calculate by only once