I'd like to make a drag-n-drop function for a widget. The code is this:
from tkinter import *
root = Tk()
root.config(background = "red", width = 500, height = 500)
root.title("root")
def frameDrag(event):
frame.place(x = event.x , y = event.y)
frame = Frame(root, width = 60, height = 30)
frame.place(x=0, y=0)
frame.bind("<B1-Motion>", frameDrag)
root.mainloop()
Basically, it should place the widget to the location you move your mouse to. Instead, the widget jumps around all over the window.
Any ideas how to fix this?
It is jumping all over the place because you are telling it to as shown by:
def frameDrag(event):
print event.x, event.y
frame.place(x = event.x , y = event.y)
Better to use a canvas widget and better to use B1-Click and B1-Release events and compute the delta. Look for the widget demo that comes along with Tkinter.
Related
I am trying to add a menu bar on a Canvas widget. I am currently testing it using some demo code I found online, before I implement it in an application that I am writing. Currently the code shows the window, but the Menu bar appears at the bottom of the page, instead of the top.
Also more of a side note: Is there a way that I can use a function in a seperate python file to draw a shape without having it create an entire new window?
my code:
import tkinter
from tkinter import *
from tkinter import messagebox
def option():
print("Options")
top = Tk()
mb = Menubutton(top, text = "condiments", relief = RAISED)
C = Canvas(top, bg = "blue", height = 250, width = 250)
C.grid()
mb.grid()
mb.menu = Menu(mb, tearoff = 0)
mb["menu"] = mb.menu
mb.menu.add_command(label = "mayo", command = option)
mb.menu.add_command(label = "ketchup", command = option)
coord = 10,50.240, 210
coord1 = 10,50,20,60
arc = C.create_arc(coord, start = 0, extent = 150, fill = "red")
line = C.create_line(coord, fill = "white")
oval = C.create_oval(coord1, fill = "black")
top.mainloop()
By default, grid will automatically increase the row and column unless you specify otherwise. You could also simply reorder the code so that the defaults match up with your expectation.
The Zen of Python says that "explicit is better than implicit". If you explicitly define the row and column, the code will be easier to understand and you can put the menubar wherever you want.
mb.grid(row=0, column=0)
C.grid(row=1, column=0)
I'm learning Tkinter at the moment. From my book, I get the following code for producing a simple vertical scrollbar:
from tkinter import * # Import tkinter
class ScrollText:
def __init__(self):
window = Tk() # Create a window
window.title("Scroll Text Demo") # Set title
frame1 = Frame(window)
frame1.pack()
scrollbar = Scrollbar(frame1)
scrollbar.pack(side = RIGHT, fill = Y)
text = Text(frame1, width = 40, height = 10, wrap = WORD,
yscrollcommand = scrollbar.set)
text.pack()
scrollbar.config(command = text.yview)
window.mainloop() # Create an event loop
ScrollText() # Create GUI
which produces the following nice output:
enter image description here
However, when I then try to change this code in the obvious way to get a horizontal scrollbar, it's producing a weird output. Here's the code I'm using
from tkinter import * # Import tkinter
class ScrollText:
def __init__(self):
window = Tk() # Create a window
window.title("Scroll Text Demo") # Set title
frame1 = Frame(window)
frame1.pack()
scrollbar = Scrollbar(frame1)
scrollbar.pack(side = BOTTOM, fill = X)
text = Text(frame1, width = 40, height = 10, wrap = WORD,
xscrollcommand = scrollbar.set)
text.pack()
scrollbar.config(command = text.xview)
window.mainloop() # Create an event loop
ScrollText() # Create GUI
and here's what I get when I run this:
enter image description here
You're assigning horizontal scrolling, xscrollcommand, to a vertical scrollbar. You need to modify Scrollbar's orient option to 'horizontal' which is by default 'vertical'.
Try replacing:
scrollbar = Scrollbar(frame1)
with:
scrollbar = Scrollbar(frame1, orient='horizontal')
I have the following piece of code that takes an image within a canvas and then whenever I click the paint function draws a dot over it.
Everything is working fine except that the paint function is not working as expected.
Desirable output
Click event draws a dot. No need to drag the on click event
Actual output
I have to drag the on mouse click event to see a drawing on the canvas.
I guess there might be a slight problem with the paint function. However, I haven't been able to know what it is exactly.
from tkinter import *
from PIL import Image, ImageTk
class Main(object):
def __init__(self):
self.canvas = None
def main(self):
master = Tk()
# Right side of the screen / image holder
right_frame = Frame(master, width=500, height=500, cursor="dot")
right_frame.pack(side=LEFT)
# Retrieve image
image = Image.open("./image/demo.JPG")
image = image.resize((800, 700), Image.ANTIALIAS)
photo = ImageTk.PhotoImage(image)
# Create canvas
self.canvas = Canvas(right_frame, width=800, height=700)
self.canvas.create_image(0, 0, image=photo, anchor="nw")
self.canvas.pack()
self.canvas.bind("<B1-Motion>", self.paint)
mainloop()
def paint(self, event):
python_green = "#476042"
x1, y1 = (event.x - 1), (event.y - 1)
x2, y2 = (event.x + 1), (event.y + 1)
self.canvas.create_oval(x1, y1, x2, y2, fill=python_green, outline=python_green, width=10)
if __name__ == "__main__":
Main().main()
Fix:
Added these two methods:
def on_button_press(self, event):
self.x = event.x
self.y = event.y
def on_button_release(self, event):
python_green = "#476042"
x0,y0 = (self.x, self.y)
x1,y1 = (event.x, event.y)
changed canvas to this:
self.canvas.bind("<ButtonPress-1>", self.on_button_press)
self.canvas.bind("<ButtonRelease-1>", self.on_button_release)
When you only click and don't move the mouse, B1-Motion isn't triggering.
To bind to mouse press (as well as mouse moving), you can add self.canvas.bind("<ButtonPress-1>", self.paint) before the mainloop.
Trying to scroll a graphic on the screen while keeping the text in the same place. The text shows where the mouse is located. I've thought about the idea of scrolling the text in the opposite direction of the screen scroll but I'm not sure if there is an easier way of doing it and if I have to scroll the text the opposite way I'm not sure how to set the initial text pointer so I can come back and recall/reset it. I'm only wanting it to show the position on not as will be doing other things in the near future.
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.keys = dict.fromkeys(('Left', 'Right', 'Up', 'Down'))
self.canvas = tk.Canvas(self, background="bisque", width=700, height=700)
self.canvas.pack(fill="both", expand=True)
self.canvas.configure(scrollregion=(-1000, -1000, 1000, 1000))
self.looper() # start the looping
def keypress(self,event):
if event.keysym in self.keys:
# event type 2 is key down, type 3 is key up
self.keys[event.keysym] = event.type == '2'
def looper(self):
if self.keys['Up']:
self.canvas.yview_scroll(-2,'units')
if self.keys['Down']:
self.canvas.yview_scroll(2,'units')
if self.keys['Left']:
self.canvas.xview_scroll(-2,'units')
if self.keys['Right']:
self.canvas.xview_scroll(2,'units')
self.after(5, self.looper) # set the refresh rate here ... ie 20 milliseconds. Smaller number means faster scrolling
def on_press(self, event):
self.last_x = event.x
self.last_y = event.y
self.startx, self.starty = self.canvas.canvasx(event.x),self.canvas.canvasy(event.y)
def on_motion(self, event):
self.canvas.delete("sx")
self.startx, self.starty = self.canvas.canvasx(event.x),self.canvas.canvasy(event.y)
px = round(-(((1000-self.startx) * .00015) + 69.3),5)
py = round((45.05-((1000+self.starty) * .00015)),5)
self.canvas.create_text(400,-400, text = str(px), fill = "black", tags = "sx")
self.canvas.create_text(475,-400, text = str(py), fill = "black", tags = "sx")
def button_motion(self,event):
delta_x = event.x - self.last_x
delta_y = event.y - self.last_y
self.last_x = event.x
self.last_y = event.y
self.canvas.xview_scroll(-delta_x, "units")
self.canvas.yview_scroll(-delta_y, "units")
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
The simplest solution is to use a widget that is not embedded in the canvas for the text so that it won't scroll when the canvas scrolls. Create a Label with it's parent being the canvas, and then use place to superimpose the label on top of the canvas.
Ok guys.
I am trying to generate 10 balls of random color in Tkinter canvas when I click the generate button.
Program works, and random color choice works for the ball, but I only get one ball generated at a time.
Every time I click the button it randomly moves the ball around, but all I want is 10 balls in 10 random positions at a time. I am using Python 3.4 on a Linux box.
This is a code I've got:
from tkinter import *
import random # to generate random balls
colors = ["red", "blue", "purple", "green", "violet", "black"]
class RandomBalls:
"""
Boilerplate code for window in Tkinter
window = Tk()
window.title("Random title")
window.mainloop()
"""
def __init__(self):
"""
Initialize the window and add two frames, one with button, and another one with
canvas
:return:
"""
window = Tk()
window.title("Random balls")
# A canvas frame
frame1 = Frame(window)
frame1.pack()
self.canvas = Canvas(frame1, width = 200, height = 300, bg = "white")
self.canvas.pack()
# A button frame
frame2 = Frame(window)
frame2.pack()
displayBtn = Button(frame2, text = "Display", command = self.display)
displayBtn.pack()
window.mainloop()
def display(self):
for i in range(0, 10):
self.canvas.delete("circle") # delete references to the old circle
self.x1 = random.randrange(150)
self.y1 = random.randrange(200)
self.x2 = self.x1 + 5
self.y2 = self.y1 + 5
self.coords = self.x1, self.y1, self.x2, self.y2
self.canvas.create_oval(self.coords, fill = random.choice(colors), tags = "circle")
self.canvas.update()
RandomBalls()
Every time through your loop you are deleting everything you created before, including what you created the previous iteration. Move the delete statement outside of the loop:
def display(self):
self.canvas.delete("circle")
for i in range(0, 10):
...