place objects in canvas where clicked using binding python - 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()

Related

Tkinter: resizable text box with scrollable

When the following code is run, a text box appear with scrollable. However, I need to make the textbox resizable with the cursor. How can I it?
import tkinter as tk
import sys
class Redirect():
def __init__(self, widget, autoscroll=True):
self.widget = widget
self.autoscroll = autoscroll
self.output = widget
def write(self, text):
self.widget.insert('end', text)
if self.autoscroll:
self.widget.see("end")
self.output.update_idletasks()
root = tk.Tk()
root.geometry("1200x620+1+1")
frame = tk.Frame(root)
frame.place(x=650, y=310, anchor="c", width=850, height=400 )
text=tk.Text(frame,width=25, height=8, wrap='word')
text.pack(side='left', fill='both', expand=True )
scrollbar = tk.Scrollbar(frame)
scrollbar.pack(side='right', fill='y')
text['yscrollcommand'] = scrollbar.set
scrollbar['command'] = text.yview
old_stdout = sys.stdout
sys.stdout = Redirect(text)
root.mainloop()
sys.stdout = old_stdout
Tkinter doesn't directly support this. However, it includes all of the basic building blocks to accomplish this.
All you need to do is add a resize grip that the user can click on, and then bindings to resize the widget when you click and drag that grip.
Here's a simple example. It uses the ttk Sizegrip widget. That widget has built-in bindings to resize the window, but we can override them with custom bindings to resize the widget instead. This example requires that you use place, since that's what your original example uses.
import tkinter as tk
from tkinter import ttk
class ResizableText(tk.Frame):
def __init__(self, parent, **kwargs):
super().__init__(parent)
self.text = tk.Text(self, **kwargs)
self.scrollbar = tk.Scrollbar(self, command=self.text.yview)
self.scrollbar.pack(side="right", fill="y")
self.text.pack(side="left", fill="both", expand=True)
sizegrip = ttk.Sizegrip(self.text)
sizegrip.place(relx=1.0, rely=1.0, anchor="se")
sizegrip.configure(cursor="sizing")
sizegrip.bind("<1>", self._resize_start)
sizegrip.bind("<B1-Motion>", self._resize)
def _resize_start(self, event):
self._x = event.x
self._y = event.y
return "break"
def _resize(self, event):
delta_x = event.x - self._x
delta_y = event.y - self._y
self.place_configure(
width = self.winfo_width() + delta_x,
height = self.winfo_height() + delta_y
)
return "break"
root = tk.Tk()
root.geometry("1200x620+1+1")
rt = ResizableText(root, width=25, height=8, wrap="word")
rt.place(x=650, y=310, anchor="c", width=850, height=400 )
root.mainloop()

How to add a Pmw tooltip to a rectangle object in a tkinter canvas?

When the user hovers the mouse over the pink rectangle I want the program to display a tooltip in the same way that it would if you were to hover your mouse over a button. However, this doesn't work because I get the error >AttributeError: 'Bar' object has no attribute 'tk'. How do I bind a tooltip to a canvas rectangle in tkinter?
from tkinter import *
import Pmw
root = Tk()
Pmw.initialise(root)
canvas = Canvas()
canvas.config(width=800, height=700, bg='white')
class Bar:
def __init__(self, x, y):
self.x = x
self.y = y
self.bar = canvas.create_rectangle(x, y, x + 200, y + 200, fill='pink')
bar = Bar(200, 200)
# create balloon object and bind it to the widget
balloon = Pmw.Balloon(bar)
balloon.bind(bar, "Text for the tool tip")
lbl = balloon.component("label")
lbl.config(background="black", foreground="white")
# Pmw.Color.changecolor(lbl, background="black", foreground="white")
canvas.pack()
root.mainloop()
Use balloon.tagbind(Canvas/Text, tag, "tooltip text").
Minimal example:
import Pmw
from tkinter import *
root = Tk()
Pmw.initialise(root)
canvas = Canvas(root, width=800, height=700, bg="white")
canvas.pack()
bar1 = canvas.create_rectangle(50, 50, 100, 100, fill="pink")
bar2 = canvas.create_rectangle(300, 300, 500, 500, fill="red")
balloon = Pmw.Balloon()
balloon.tagbind(canvas, bar1, "first tooltip")
balloon.tagbind(canvas, bar2, "second tooltip")
root.mainloop()

How to call Turtle window from a GUI

I'm trying to open a Turtle window from a GUI to select a position on the image with a mouse click. The x and y coordinates are then returned as an input to the GUI.
Here is a minimal working example:
from tkinter import *
from tkinter import font as tkFont
import turtle
xclick = 0
yclick = 0
def callback3():
getcoordinates()
def getcoordinates():
screen = turtle.Screen()
screen.setup(400, 400)
screen.bgpic("stunden.gif")
screen.onscreenclick(modifyglobalvariables)
def modifyglobalvariables(rawx,rawy):
global xclick
global yclick
xclick = int(rawx//1)
yclick = int(rawy//1)
print(xclick)
print(yclick)
turtle.bye()
root = Tk()
helv30 = tkFont.Font(family='Helvetica', size=30)
button1 = Button(root, text = "1", width=3, font=helv30, borderwidth=0, command=callback3)
button1.grid(row=0, column=0, padx=5, pady=0)
root.mainloop()
Then the error image "pyimage2" doesn't exist shows up. I found out, that it has something to do with two instances of Tk, as there is the root and the turtle window and that I should solve it with Toplevel(). However, after hours of research and try and error I still could not come up with the right solution to make my code work. Any help is greatly appreciated.
Here's how to implement it directly in tkinter — so no turtle module required — as I suggested earlier in a (now-deleted) comment:
from tkinter import *
from tkinter import font as tkFont
CLOCK_IMAGE_PATH = 'clock.png'
xclick, yclick = 0, 0
def callback3():
window = Toplevel()
window.title("Stunden")
img = PhotoImage(file=CLOCK_IMAGE_PATH)
img_width, img_height = img.width(), img.height()
window.minsize(width=img_width, height=img_height)
canvas = Canvas(window, width=img_width, height=img_height)
canvas.pack()
canvas.image = img # Keep reference to avoid image being garbage collected.
canvas.create_image(0, 0, anchor='nw', image=img)
canvas.bind("<Button-1>", get_coordinates) # Set up event-handler callback.
def get_coordinates(event):
global xclick, yclick
xclick = int(event.x // 1)
yclick = int(event.y // 1)
print(f'xclick={xclick}, yclick={yclick}')
root = Tk()
helv30 = tkFont.Font(family='Helvetica', size=30)
button1 = Button(root, text="1", width=3, font=helv30, borderwidth=0, command=callback3)
button1.grid(row=0, column=0, padx=5, pady=0)
root.mainloop()

How to bind the Mouse wheel to a tkinter canvas object, object using tag_bind?

I was wondering if theres anyway that I could bind the mousewheel event to a canvas rectangle for example.
When I want to do that with other events I use the tag_bind method from the Canvas class, however it seems that that method doesnt work with the mousewheel event.
import tkinter as tk
class Zoom_rectangle:
def __init__(self, canvas, p0, w, h):
self.canvas = canvas
self.p0 = p0
self.w = w
self.h = h
id = canvas.create_rectangle(p0[0], p0[1], p0[0]+w, p0[1]+h,
fill="light blue")
canvas.tag_bind(id, "<ButtonRelease>", self.fun)
def fun(self, event):
print(event)
class Gui:
def __init__(self, master):
w = 500
h = 500
canvas = tk.Canvas(master, width=w, height=h)
canvas.pack()
Zoom_rectangle(canvas, (50,50), 400, 100)
Zoom_rectangle(canvas, (50,200), 400, 100)
root = tk.Tk()
gui = Gui(root)
root.mainloop()
I would like to find a way to do the same but with the MouseWheel event instead of the ButtonRelease event.

Drag window when using overrideredirect

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

Categories