Drag window when using overrideredirect - python

I know how to remove a border from a Tkinter window using overrideredirect, but whenever I do that the window becomes unresponsive. I can't move it using alt and dragging, or any other method.
I want to make an application that looks like one of those "riced" applications that are just a bare window, and obviously I can't get very far if it just sits unresponsive in the upper-left corner. So, how do I do this?

To make the window draggable, put bindings for <Button-1> (mouse clicks) and <B1-Motion> (mouse movements) on the window.
All you need to do is store the x and y values of a mouse down event and then during mouse motion events, you position the window based on the current pointer x and y, delta the original event x and y.
The handler for the mouse click binding stores the original event x and y.
The handler for the mouse movement binding calls the TopLevel method geometry() to reposition the window, based on current mouse position and the offset you have stored from the most recent mouse click. You supply a geometry string to the geometry method.
Here is a very minimal example which does not take into account the edges of the screen:
import tkinter
class Win(tkinter.Tk):
def __init__(self,master=None):
tkinter.Tk.__init__(self,master)
self.overrideredirect(True)
self._offsetx = 0
self._offsety = 0
self.bind('<Button-1>',self.clickwin)
self.bind('<B1-Motion>',self.dragwin)
def dragwin(self,event):
x = self.winfo_pointerx() - self._offsetx
y = self.winfo_pointery() - self._offsety
self.geometry('+{x}+{y}'.format(x=x,y=y))
def clickwin(self,event):
self._offsetx = event.x
self._offsety = event.y
win = Win()
win.mainloop()
EDIT by TheLizzard:
The code above works but doesn't behave correctly when there is more than one widget so this is the fixed code:
import tkinter as tk
class Win(tk.Tk):
def __init__(self):
super().__init__()
super().overrideredirect(True)
self._offsetx = 0
self._offsety = 0
super().bind("<Button-1>" ,self.clickwin)
super().bind("<B1-Motion>", self.dragwin)
def dragwin(self,event):
x = super().winfo_pointerx() - self._offsetx
y = super().winfo_pointery() - self._offsety
super().geometry(f"+{x}+{y}")
def clickwin(self,event):
self._offsetx = super().winfo_pointerx() - super().winfo_rootx()
self._offsety = super().winfo_pointery() - super().winfo_rooty()
root = Win()
label_1 = tk.Label(root, text="Label 1")
label_1.pack(side="left")
label_2 = tk.Label(root, text="Label 2")
label_2.pack(side="left")
root.mainloop()

Thanks to #dusty's answer, it had a jumping problem, and I solved it by saving the window location.
import tkinter
class Win(tkinter.Tk):
def __init__(self,master=None):
tkinter.Tk.__init__(self,master)
self.overrideredirect(True)
self._offsetx = 0
self._offsety = 0
self._window_x = 500
self._window_y = 100
self._window_w = 500
self._window_h = 500
self.geometry('{w}x{h}+{x}+{y}'.format(w=self._window_w,h=self._window_h,x=self._window_x,y=self._window_y))
self.bind('<Button-1>',self.clickwin)
self.bind('<B1-Motion>',self.dragwin)
def dragwin(self,event):
delta_x = self.winfo_pointerx() - self._offsetx
delta_y = self.winfo_pointery() - self._offsety
x = self._window_x + delta_x
y = self._window_y + delta_y
self.geometry("+{x}+{y}".format(x=x, y=y))
self._offsetx = self.winfo_pointerx()
self._offsety = self.winfo_pointery()
self._window_x = x
self._window_y = y
def clickwin(self,event):
self._offsetx = self.winfo_pointerx()
self._offsety = self.winfo_pointery()
win = Win()
win.mainloop()
self._window_x and self._window_y are the primary position of the window.
self._window_h and self._window_w are the height and width of the window.

This solution is works for me:
from tkinter import *
import mouse
global x, y
def standard_bind():
root.bind('<B1-Motion>', lambda e: event(e, Mode=True))
def event(widget, Mode=False):
global x, y
if Mode:
x = widget.x
y = widget.y
root.bind('<B1-Motion>', lambda e: event(e))
root.geometry('+%d+%d' % (mouse.get_position()[0]-x, mouse.get_position()[1]-y))
root = Tk()
root.overrideredirect(True)
root.bind('<B1-Motion>', lambda e: event(e, Mode=True))
root.bind('<ButtonRelease-1>', lambda e: standard_bind())
root.geometry('%dx%d+%d+%d' % (600, 60, 50, 50))
mainloop()

Here a bit more sophisticated method which assumes that you don't want to just click any where on the tkinter app to move it, but rather clicking on the title bar to move the app around while retaining the familiar "X" to close the app.
Works for python3.0 and later
Since tkinter does not (by default) allow you to directly achieve this, we must:
Remove the tkinter frame's title bar
Create our own title bar and recreate the "x" for closing the app
bind the event for clicking, such that the app moves when dragged
from tkinter import *
root = Tk()
root.title('The Name of Your Application')
root.geometry("500x300")
# remove title bar
root.overrideredirect(True)
def move_app(e):
root.geometry(f'+{e.x_root}+{e.y_root}')
def quitter(e):
root.quit()
#root.destroy()
# Create Fake Title Bar
title_bar = Frame(root, bg="darkgreen", relief="raised", bd=0)
title_bar.pack(expand=1, fill=X)
# Bind the titlebar
title_bar.bind("<B1-Motion>", move_app)
# Create title text
title_label = Label(title_bar, text=" My Awesome App!!", bg="darkgreen", fg="white")
title_label.pack(side=LEFT, pady=4)
# Create close button on titlebar
close_label = Label(title_bar, text=" X ", bg="darkgreen", fg="white", relief="sunken", bd=0)
close_label.pack(side=RIGHT, pady=4)
close_label.bind("<Button-1>", quitter)
my_button = Button(root, text="CLOSE!", font=("Helvetica, 32"), command=root.quit)
my_button.pack(pady=100)
root.mainloop()

Related

How to drag a widget and click on it at the same time in tkinter ??? In Python

Does anyone know an event for saying "when the element is grabbed" by grab I mean not just pressed or clicked but when we try to move an element from one position to another (it's more like drag the element)
For example I want to do two things at the same time on a widget (a Label) when I click on it, it display other labels (2 others) and when I grab it => I can move it
The initial line to lunch an event when the mouse is clicked is :
widget.bind('<Button-1>', function)
How I can click and drag a widget at the same time ??? There's a event to do that ?
The full code you don't necessarily have to read it :
import tkinter as tk
from tkinter import *
from tkinter import ttk
root = Tk()
root.geometry('800x600')
root.resizable(False,False)
frame = tk.Frame(root)
frame.pack(expand=1,fill=BOTH)
label = tk.Label(frame,text='˃LABEL',fg='green')
label.grid(column=0,row=0)
label2 = Label(frame,text='˃LABEL2',fg='green')
label3 = Label(frame,text='˃LABEL3',fg='green')
def change(event):
label['text'] = '˅LABEL'
label2.grid(column=0,row=1)
label3.grid(column=0,row=2)
widget = event.widget
widget._drag_start_x = event.x
widget._drag_start_y = event.y
if label['text'] == '˅LABEL':
label.bind('<Button-1>', hide)
def hide(event):
label['text'] = '˃LABEL'
label2.grid_forget()
label3.grid_forget()
if label['text'] == '˃LABEL':
label.bind('<Button-1>',change)
label.bind('<Button-1>',change)
def make_draggable(widget):
widget.bind("<Button-1>", change)
widget.bind("<B1-Motion>", on_drag_motion)
#def on_drag_start(event):
def on_drag_motion(event):
widget = event.widget
x = widget.winfo_x() - widget._drag_start_x + event.x
y = widget.winfo_y() - widget._drag_start_y + event.y
widget.place(x=x, y=y)
make_draggable(label)
root.mainloop()

How to make Overredirect window work like(eg.Show in Taskbar,Add Minimize Button) normal window in Tkinter Python

my window image I have made a round corner window in tkinter after some research.
from tkinter import *
from tkinter.ttk import Style,Frame as fp
root = Tk()
root.geometry("600x400+250+250")
root.overrideredirect(True)
root.wm_attributes("-transparentcolor", "#d307e9")
root.wm_attributes("-topmost", True)
#This is made with ttkframe from image and the image is in base64 encoded fromat
data='''
iVBORw0KGgoAAAANSUhEUgAAAEAAAAAvCAYAAABTy8x
RAAACxElEQVRoge2aS2/aQBSFs+NRCEkVhUJfolISNW
nSllcIeF91lT6WqdT8gK6y6+/Exobwsne2MWA8+9NFk
1SEAY3Nyx66OCsk5Ptp7twz996takjHJmtr3R+wbv0H
sLA/i+iQEibk/R6UtIX6Cwv1V/359dyCkrIg7/UgxQ1
Uwz4BIEYN1F9aaL+3oZYdaAJZjSoEaslB63gIJWnNDc
Q1AGnHROeDvbqAGYA0j4YQo8ZyAYgRHe1THwVOAdHI9
JcDQEqYUC9WeMznUCc7ghhZIIDargmtsv7A3KhbcJgh
zAQgxQ1oZYKb4m9cFa5xVfh5p2v8eCQ//f6reINOdsR
0QU4HENbRLf499pf5b8jlcoHRp/xnaAJB83DoHcBtpv
9wpIIKQBMIagnTPQAxaozlfZABdLIj9wAamcHYpXKZ/
7r2oLwC0AQCaXu6R6ACeFzygg6g9Xb6XTABQIwZE2Ul
yCmgCQRqmbADaLzucwdAEwjEGD0NJgDQ7C4PAJRnFhs
AmuXlAcA0TzAOIKxTbS8PANpnNgOACL8AugW6HxgDIE
Z06uOCBwBqydlwAFNKISOAYBshTSDQKnMBCP4J2HgAc
6YABwBYLkG+yyALgI03Qjxb4SMWKxzS0T7j9DGUYnwM
8focllifw2Kc1hAJthFy1RCh3QNBB9A6dtESq4ZoTdF
gp4C0Pb01Tm+LRzhqi3/00BavhvgZjEheBiP3pqhbCP
ho7GAwM/jZAEL/hqNf8t/XHpRbAJ3cvMPRO9V2TGjlS
XvsZ3XzI1QXMR5/OAkxA+p5MBYk2qe2q70h9h2hsI7m
4dC3yxJqmUBJ0+3uYgDcl8iYgdaJ7RsQ6oWD2zcDz9t
i3vcEwzrkZA/tdzbUkrM6IBUC9dxB82CA2u7sErdcAB
QgUtyAvNeDnOxBSVlQ0gtQyoK830PtqQnxiY8WJXnRx
gP4AyU7Jb13S9JyAAAAAElFTkSuQmCC
'''
FrameImage = PhotoImage("border", data=data,master=root)
T_style=Style()
T_style.element_create("RoundedFrame",
"image", FrameImage,
border=20, sticky="nsew")
T_style.layout("RoundedFrame",
[("RoundedFrame", {"sticky": "nsew"})])
t = fp(root,style="RoundedFrame", padding=10)
t.pack(fill='both',expand=True)
f=Frame(t,width=581,height=25,bg='red')
f.place(x=-1,y=0)
def loop1(evt):
global xp,yp
xp=evt.x
yp=evt.y
def loop2(evt):
global xp,yp
xp=None
yp=None
def loop(evt):
deltax=evt.x-xp
deltay=evt.y-yp
x=root.winfo_x()+deltax
y=root.winfo_y()+deltay
root.geometry(f"+{x}+{y}")
f.bind('<B1-Motion>',loop)
f.bind('<ButtonPress-1>',loop1)
f.bind('<ButtonRelease-1>',loop2)
def minm():
root.overrideredirect(False)
root.iconify()
root.update_idletasks()
b=Button(t,text='minimize',command=minm).pack()
def good(event):
root.overrideredirect(True)
root.bind("<Map>",good)
root.mainloop()
But I cant minimize it through button,and this does not even appear in the windows taskbar while running.but after doing some research on stackoverflow website i successfully managed this window moveable.I want it to work like normal window.
Any help will be appreciated.
Thank You!
I want this type in the taskbar of window
You can use a Toplevel window (not visible in the screen viewable area) to pretend root window in the taskbar. Then based on the Map or Unmap event of this invisible Toplevel window to show or hide the root window.
Below is a modified program based on yours:
from tkinter import *
from tkinter.ttk import Style, Frame as fp
root = Tk()
def toggle(event):
if event.type == EventType.Map:
root.deiconify()
else:
root.withdraw()
# create the "invisible" toplevel
top = Toplevel(root)
top.geometry('0x0+10000+10000') # make it not visible
top.protocol('WM_DELETE_WINDOW', root.destroy) # close root window if toplevel is closed
top.bind("<Map>", toggle)
top.bind("<Unmap>", toggle)
root.geometry("600x400+250+250")
root.overrideredirect(True)
root.wm_attributes("-transparentcolor", "#d307e9")
root.wm_attributes("-topmost", True)
#This is made with ttkframe from image and the image is in base64 encoded fromat
data='''
iVBORw0KGgoAAAANSUhEUgAAAEAAAAAvCAYAAABTy8x
RAAACxElEQVRoge2aS2/aQBSFs+NRCEkVhUJfolISNW
nSllcIeF91lT6WqdT8gK6y6+/Exobwsne2MWA8+9NFk
1SEAY3Nyx66OCsk5Ptp7twz996takjHJmtr3R+wbv0H
sLA/i+iQEibk/R6UtIX6Cwv1V/359dyCkrIg7/UgxQ1
Uwz4BIEYN1F9aaL+3oZYdaAJZjSoEaslB63gIJWnNDc
Q1AGnHROeDvbqAGYA0j4YQo8ZyAYgRHe1THwVOAdHI9
JcDQEqYUC9WeMznUCc7ghhZIIDargmtsv7A3KhbcJgh
zAQgxQ1oZYKb4m9cFa5xVfh5p2v8eCQ//f6reINOdsR
0QU4HENbRLf499pf5b8jlcoHRp/xnaAJB83DoHcBtpv
9wpIIKQBMIagnTPQAxaozlfZABdLIj9wAamcHYpXKZ/
7r2oLwC0AQCaXu6R6ACeFzygg6g9Xb6XTABQIwZE2Ul
yCmgCQRqmbADaLzucwdAEwjEGD0NJgDQ7C4PAJRnFhs
AmuXlAcA0TzAOIKxTbS8PANpnNgOACL8AugW6HxgDIE
Z06uOCBwBqydlwAFNKISOAYBshTSDQKnMBCP4J2HgAc
6YABwBYLkG+yyALgI03Qjxb4SMWKxzS0T7j9DGUYnwM
8focllifw2Kc1hAJthFy1RCh3QNBB9A6dtESq4ZoTdF
gp4C0Pb01Tm+LRzhqi3/00BavhvgZjEheBiP3pqhbCP
ho7GAwM/jZAEL/hqNf8t/XHpRbAJ3cvMPRO9V2TGjlS
XvsZ3XzI1QXMR5/OAkxA+p5MBYk2qe2q70h9h2hsI7m
4dC3yxJqmUBJ0+3uYgDcl8iYgdaJ7RsQ6oWD2zcDz9t
i3vcEwzrkZA/tdzbUkrM6IBUC9dxB82CA2u7sErdcAB
QgUtyAvNeDnOxBSVlQ0gtQyoK830PtqQnxiY8WJXnRx
gP4AyU7Jb13S9JyAAAAAElFTkSuQmCC
'''
FrameImage = PhotoImage("border", data=data, master=root)
T_style = Style()
T_style.element_create("RoundedFrame", "image", FrameImage,
border=20, sticky="nsew")
T_style.layout("RoundedFrame",
[("RoundedFrame", {"sticky": "nsew"})])
t = fp(root, style="RoundedFrame", padding=10)
t.pack(fill='both', expand=True)
f = Frame(t, width=581, height=25, bg='red')
f.place(x=-1, y=0)
def on_mouse_press(evt):
global xp, yp
xp = evt.x
yp = evt.y
def on_mouse_drag(evt):
deltax = evt.x - xp
deltay = evt.y - yp
x = root.winfo_x() + deltax
y = root.winfo_y() + deltay
root.geometry(f"+{x}+{y}")
f.bind('<B1-Motion>', on_mouse_drag)
f.bind('<ButtonPress-1>', on_mouse_press)
# simulate close button in normal title bar
Button(t, text='X', width=1, command=root.destroy).pack(side=RIGHT, anchor='n')
# simulate minimize button in normal title bar
Button(t, text='_', width=1, command=top.iconify).pack(side=RIGHT, anchor='n')
root.mainloop()

How to move a rectangle to the mouse position with Tkinter/Python?

I'm using Tkinter/Python's Canva class' coord() method to move a rectangle. What should I pass as paramters in order to make it work?
from tkinter import *
root = Tk()
def key(event):
print ("pressed", repr(event.char))
def callback(event):
position = (event.x,event.y)
event.widget.coords(item, position)
canvas= Canvas(root, width=100, height=100)
canvas.bind("<Key>", key)
canvas.bind("<Button-1>", callback)
item = canvas.create_rectangle(10,10,5,5)
canvas.pack()
move widget using mouse
from tkinter import *
import pyautogui
def on_move(event):
component=event.widget
locx, locy = component.winfo_x(), component.winfo_y()
w , h =master.winfo_width(),master.winfo_height()
mx ,my =component.winfo_width(),component.winfo_height()
xpos=(locx+event.x)-(15)
ypos=(locy+event.y)-int(my/2)
if xpos>=0 and ypos>=0 and w-abs(xpos)>=0 and h-abs(ypos)>=0 and xpos<=w-5 and ypos<=h-5:
component.place(x=xpos,y=ypos)
master = Tk()
master.geometry("%dx%d+0+0" % (500,500))
msg = Label(master, text = "Click & Move")
msg.config(bg='lightgreen', font=('times', 24, 'italic'))
msg.bind('<B1-Motion>',on_move)
msg.place(x=10,y=20)
mainloop()
This seems your first post. Welcome to SO :D
Updated answer: After some research and testing, it seems that you just need to pass the coordenates without the tuple. Storing x and y in a tuple is a problem, but also not providing the values for x2 and y2.
def callback(event):
event.widget.coords(item, event.x + 5, event.y + 5, event.x, event.y)
You can learn more here
Original wrong answer:
You can't move items on tk. Maybe try to clean the canvas and create the item at the new coordinates.
canvas.delete("all")
canvas.create_rectangle(event.x + 5,event.y + 5, position)

place objects in canvas where clicked using binding python

I want to make an object appear in the tkinter window where the user clicks.
I have this code:
from tkinter import *
class Storage:
def __init__(self):
x = None
circle = None
w=None
class Game:
def Start():
#make object appear where clicked
root = Tk()
w = Canvas(root, width=200, height=100)
w.pack()
w.bind("<Button-1>", Start)
start = Button(text="Start!", command=Game.Start)
start.pack()
root.mainloop()
Any help would be appreciated thanks.
first of all you should put the canvas object on the frame, then bind the canvas to the event requred
from tkinter import *
class Storage:
def __init__(self):
x = None
circle = None
w=None
class Game:
def Start(event):
print("clicked at", event.x, event.y)
x = event.x
y = event.y
w.create_rectangle(x, y, 100, 100, fill="blue")
root = Tk()
frame = Frame(root, width=200, height=200)
frame.pack()
w = Canvas(frame, width=200, height=100)
w.pack()
w.bind("<Button-1>", Game.Start)
root.mainloop()

A differen "Drag and Drop" images in Tkinter (Python 3.x)

I'm trying to make it possible for my program's user to drag an image from a widget and drop it in another without removing the image from its first position.
I thought on using a TopLevel for this. Upon a click+drag event, a TopLevel, containing the same image, would pop up right above the clicked image on the event.x and event.y position. Then it would change its position along with the mouse and only upon ButtonRelease-1 the TopLevel would be destroyed.
If the Button Release event was triggered on the master x and y coordinates corresponding to where is my other widget (in the case, a canvas), then it would trigger a canvas.create_image() using the event.x and event.y.
The problems I'm facing are:
Only 1 TopLevel should appear at once, but I had no success limiting it. Several windows overlap as I click and drag the mouse.
I can't make Toplevel.geometry use the bound event's x and y positions.
How to make the Toplevel display the same image as the user clicked, without magic numbers and variables?
Here's my code so far:
class animalImgList():
def __init__(self, a):
#Constructor
initX = 75
initY = 40
animalList = ['pig2.jpg', 'pig3.jpg', 'pig4.jpg']
for a in animalList:
vars(self)[a+'img'] = PIL.Image.open(a)
vars(self)[a+'tkimg'] = PIL.ImageTk.PhotoImage(vars(self)[a+'img'])
vars(self)[a+'labelName'] = Label(anmlCanvas, image=vars(self)[a+'tkimg'])
vars(self)[a+'canvasImg'] = anmlCanvas.create_image(initX, initY,image=(vars(self)[a+'tkimg']))
initY = initY + 70
anmlImgList = []
anmlImgList.append(vars(self)[a+'canvasImg'])
imgTags = anmlCanvas.addtag_all("img")
anmlCanvas.tag_bind("img", "<Button-1>", self.createImg)
def createImg(self, event):
newImg = Toplevel(root)
newImg.geometry("50x40"+"+"+ x+"+"+y)
newImgMsg = Message(newImg, text="This is supposed to be an image")
newImgMsg.pack()
newImg.update_idletasks()
newImg.overrideredirect(True)
createImgOpen = True
if createImgOpen == True:
pass
To drag an image between two canvases without removing the image from the first canvas.
The idea is the following:
When the user clicks on the canvas can1 (click1 function):
get the item the user clicked on with can1.find_closest
get its image with can1.itemcget
create a toplevel containing the image
bind mouse motion to drag along the toplevel: for that you need to use the event.x_root and event.y_root to change the toplevel's geometry.
When the user releases the left mouse button (release function):
unbind mouse motion
if the toplevel is inside the canvas can2, create the image in can2 at the mouse position
destroy the toplevel
This way, there can be only one toplevel because each time the button is released, the toplevel is destroyed.
Here is the code:
import tkinter as tk
class DragToplevel(tk.Toplevel):
def __init__(self, master, image, x, y):
tk.Toplevel.__init__(self, master)
self.overrideredirect(True)
self.geometry('+%i+%i' % (x, y))
self.image = image
self.label = tk.Label(self, image=image, bg='red')
self.label.pack()
def move(self, x, y):
self.geometry('+%i+%i' % (x, y))
root = tk.Tk()
can1 = tk.Canvas(root, width=300, height=300, bg='white')
can2 = tk.Canvas(root, width=300, height=300, bg='white')
can1.pack(side='left')
can2.pack(side='right')
root.geometry('800x800')
im = tk.PhotoImage('tux', master=root, file='/home/juliette/Images/tux_mini.png')
drag_id = ''
dragged = None
can1.create_image(100, 200, image=im)
def click1(event):
global drag_id, dragged
items = can1.find_closest(event.x, event.y)
if items:
image = can1.itemcget(items[0], 'image')
dragged = DragToplevel(root, image, event.x_root, event.y_root)
drag_id = root.bind('<Motion>', lambda e: dragged.move(e.x_root, e.y_root))
def release(event):
global drag_id, dragged
root.unbind('<Motion>', drag_id)
drag_id = ""
xr, yr = event.x_root, event.y_root
x2, y2 = can2.winfo_rootx(), can2.winfo_rooty()
w2, h2 = can2.winfo_width(), can2.winfo_height()
if dragged and xr >= x2 and xr < x2 + w2 and yr >= y2 and yr < y2 + h2:
can2.create_image(xr - x2, yr - y2, image=dragged.image, anchor='nw')
if dragged:
dragged.destroy()
dragged = None
can1.bind('<ButtonPress-1>', click1)
root.bind('<ButtonRelease-1>', release)
root.mainloop()

Categories