I'm making simple game - there're 8 ovals, they should be clickable and moveable. After click on an oval, the oval is following cursor. The target is to get the oval into rectangle, if you release mouse button in rectangle, the oval disappears. If you release mouse button outside rectangle, the oval should appear on it's initial position. I made this program and it works, but only for one oval. I need it works for all ovals. There's my code, any idea what to change, please?
import tkinter, random
class Desktop:
array = [(50,50,70,70),(100,50,120,70),(150,50,170,70),(150,100,170,120),
(150,150,170,170),(100,150,120,170),(50,150,70,170),(50,100,70,120)]
def __init__(self):
self.canvas = tkinter.Canvas(width=400,height=400)
self.canvas.pack()
self.canvas.create_rectangle(100,250,300,350)
for i in range(len(self.array)):
self.__dict__[f'oval{i}'] = self.canvas.create_oval(self.array[i], fill='brown',tags='id')
self.canvas.tag_bind('id','<B1-Motion>',self.move)
self.canvas.tag_bind('id','<ButtonRelease-1>',self.release)
def move(self, event):
self.canvas.coords(self.oval0,event.x-10,event.y-10,event.x+10,event.y+10)
def release(self, event):
if event.x>100 and event.x<300 and event.y>250 and event.y<350:
self.canvas.delete(self.oval0)
else:
self.canvas.coords(self.oval0,self.array[0])
d = Desktop()
I bind three methods
<ButtonPress-1> to get ID of clicked item and assign to self.selected
<B1-Motion> to move item using self.selected
<ButtonRelease-1> to release or delete moved item using self.selected
Code
import tkinter as tk
import random
class Desktop:
array = [(50,50,70,70),(100,50,120,70),(150,50,170,70),(150,100,170,120),
(150,150,170,170),(100,150,120,170),(50,150,70,170),(50,100,70,120)]
def __init__(self, master):
self.canvas = tk.Canvas(master, width=400, height=400)
self.canvas.pack()
self.canvas.create_rectangle(100, 250, 300, 350)
# to keep all IDs and its start position
self.ovals = {}
for item in self.array:
# create oval and get its ID
item_id = self.canvas.create_oval(item, fill='brown', tags='id')
# remember ID and its start position
self.ovals[item_id] = item
self.canvas.tag_bind('id', '<ButtonPress-1>', self.start_move)
self.canvas.tag_bind('id', '<B1-Motion>', self.move)
self.canvas.tag_bind('id', '<ButtonRelease-1>', self.stop_move)
# to remember selected item
self.selected = None
def start_move(self, event):
# find all clicked items
self.selected = self.canvas.find_overlapping(event.x, event.y, event.x, event.y)
# get first selected item
self.selected = self.selected[0]
def move(self, event):
# move selected item
self.canvas.coords(self.selected, event.x-10, event.y-10, event.x+10,event.y+10)
def stop_move(self, event):
# delete or release selected item
if 100 < event.x < 300 and 250 < event.y < 350:
self.canvas.delete(self.selected)
del self.ovals[self.selected]
else:
self.canvas.coords(self.selected, self.ovals[self.selected])
# clear it so you can use it to check if you are draging item
self.selected = None
root = tk.Tk()
d = Desktop(root)
root.mainloop()
EDIT: using event.widget.find_withtag("current")[0] I can get first selected item, and I can skip <ButtonPress-1>.
import tkinter as tk
import random
class Desktop:
array = [(50,50,70,70),(100,50,120,70),(150,50,170,70),(150,100,170,120),
(150,150,170,170),(100,150,120,170),(50,150,70,170),(50,100,70,120)]
def __init__(self, master):
self.canvas = tk.Canvas(master, width=400, height=400)
self.canvas.pack()
self.canvas.create_rectangle(100, 250, 300, 350)
# to keep all IDs and its start position
self.ovals = {}
for item in self.array:
# create oval and get its ID
item_id = self.canvas.create_oval(item, fill='brown', tags='id')
# remember ID and its start position
self.ovals[item_id] = item
self.canvas.tag_bind('id', '<B1-Motion>', self.move)
self.canvas.tag_bind('id', '<ButtonRelease-1>', self.stop_move)
def move(self, event):
# get selected item
selected = event.widget.find_withtag("current")[0]
# move selected item
self.canvas.coords(selected, event.x-10, event.y-10, event.x+10,event.y+10)
def stop_move(self, event):
# get selected item
selected = event.widget.find_withtag("current")[0]
# delete or release selected item
if 100 < event.x < 300 and 250 < event.y < 350:
self.canvas.delete(selected)
del self.ovals[selected]
else:
self.canvas.coords(selected, self.ovals[selected])
root = tk.Tk()
d = Desktop(root)
root.mainloop()
EDIT: added del self.ovals[selected]
Related
I am trying to make a sidebar for a tkinter Text widget that displays a red dot next to the line that the mouse hovers over. Currently it is configured to go to the selected line but would be better if it tracked the mouse. I know how to track the mouses y position with root.winfo_pointery() but don't know how the get the corresponding line for that. I also don't want it to display anything if it is out of the text widget's y area or if there is no line. How do I cross compare the y value of the pointer to text lines?
Current code:
from tkinter import *
class BreakpointBar(Canvas):
def __init__(self, *args, **kwargs):
#Initializes the canvas
Canvas.__init__(self, *args, **kwargs, highlightthickness=0)
self.textwidget = None
self.ovals = []
def attach(self, text_widget):
#Attaches the canvas to the text widget
self.textwidget = text_widget
def redraw(self, *args):
#Redraws the canvas
"""redraw line numbers"""
# try:
self.delete("all")
self.unbind_all("<Enter>")
self.unbind_all("<Leave>")
self.ovals = []
index = self.textwidget.index("insert")
index_linenum = str(index).split(".")[0]
i = self.textwidget.index("#0,0")
print(self.winfo_pointerx())
print(self.winfo_pointery())
while True :
dline= self.textwidget.dlineinfo(i)
if dline is None: break
y = dline[1]
linenum = str(i).split(".")[0]
if linenum == index_linenum:
oval = self.create_oval(5, y, 15, y+10, fill="red", outline="red")
self.tag_bind(oval, "<Enter>", lambda event: self.on_enter(event, oval))
self.tag_bind(oval, "<Leave>", lambda event: self.on_exit(event, oval))
self.tag_bind(oval, "<Button-1>", lambda event: self.on_press(event, oval))
self.ovals.append(oval)
i = self.textwidget.index("%s+1line" % i)
# except:
# pass
def on_enter(self, event, oval):
self.itemconfig(oval, fill="dark red", outline="dark red")
def on_exit(self, event, oval):
self.itemconfig(oval, fill="red", outline="red")
def on_press(self, event, oval):
index_linenum = int(str(self.textwidget.index("insert")).split(".")[0])
self.textwidget.insert("{}.end".format(index_linenum), "\nbreakpoint\n")
self.textwidget.mark_set("insert", "{}.0".format(index_linenum+2))
self.textwidget.see("insert")
root = Tk()
frame = Frame(root)
frame.pack(expand=True, fill=BOTH)
text=Text(frame)
text.pack(side=RIGHT, fill=BOTH, expand=True)
bb = BreakpointBar(frame, width=20)
bb.attach(text)
bb.pack(side=LEFT, fill=Y)
root.bind("<Button-1>", bb.redraw)
root.bind("<KeyRelease-Return>", bb.redraw)
root.mainloop()
You can get the index for character nearest to the mouse via an x,y coordinate using the format #x,y.
For example, if you have a function bound to the <Motion> event, you can get the index under the cursor by doing something like this:
def track_mouse(event):
index = event.widget.index(f"#{event.x},{event.y}")
...
In the above example, index will be in the canonical form of a string in the format line.character.
It's important to note that you must give coordinates that are relative to the upper-left corner of the text widget itself, not of the window or display as a whole.
I am making a game in which you dodge falling object by using the tkinter library. In my code, I am trying to make an object fall by binding a canvas.move() function with pressing the down arrow key, then using pyautogui to hold down the key. Here is my code:
from tkinter import *
from random import randint
import pyautogui
class Window(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.master = master
self.initWindow()
def initWindow(self):
self.master.title('Dodger')
self.pack(fill=BOTH, expand=1)
self.master.geometry('600x800')
self.master.config(bg='black')
menu = Menu(self.master)
self.master.config(menu=menu)
def clientExit():
exit()
file = Menu(menu)
file.add_command(label='Exit', command=clientExit)
file.add_command(label='Start', command=self.game)
menu.add_cascade(label='File', menu=file)
def game(self):
canvas = Canvas(self.master, width='600', height='800', borderwidth='0', highlightthickness='0')
canvas.pack()
canvas.create_rectangle(0, 0, 600, 800, fill='black', outline='black')
character = canvas.create_rectangle(270, 730, 330, 760, fill='magenta', outline='cyan', width='2')
def left(event):
cord = canvas.coords(character)
if not cord[0] <= 5:
canvas.move(character, -10, 0)
def right(event):
cord = canvas.coords(character)
if not cord[2] >= 595:
canvas.move(character, 10, 0)
self.master.bind('<Left>', left)
self.master.bind('<Right>', right)
class variables:
sizeMin = 10
sizeMax = 80
y = 10
minX = 5
maxX = 545
def createShape():
size = randint(variables.sizeMin, variables.sizeMax)
x = randint(variables.minX, variables.maxX)
topLeft = [x, variables.y]
bottomRight = [x + size, variables.y + size]
shape = canvas.create_rectangle(topLeft[0], topLeft[1], bottomRight[0], bottomRight[1],
fill='red', outline='red')
return shape
def moveShape(event):
cord = canvas.coords(x)
if cord[1] != 800:
canvas.move(x, 0, 10)
x = createShape()
self.master.bind('<Down>', moveShape)
pyautogui.keyDown('down')
root = Tk()
app = Window(root)
app.mainloop()
As you can see, at the bottom of the class, I binded the down arrow key and moving a shape down. However, the pyautogui does not work; the object does not move down unless I manually press the down arrow key. Am I forgetting something or is pyautogui not compatible with bind()? I know there are more efficient ways to move the object down, however with all the methods I have tried, none show the actual movement of the object heading down the screen; they just show the object being re-created in another position. Please let me know how I can fix this.
I wouldn't bother with it. Maybe it misses the _all argument. Try to simply bind canvas.move() function to a canvas.bind_all()
I am trying to wrap my head around parallel animations.
In the following code, clicking on a square will cause a small animation.
But declaring 2 boxes (or more) makes things more difficult: The animation called last will run and cause the other to pause and resume only after it is complete.
How to change my code so that all animation calls can run independently and in parallel?
#!python3
import tkinter as tk
import time
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
# create a canvas
self.canvas = tk.Canvas(width=400, height=400)
self.canvas.pack()
# create a couple of movable objects
self._create_token(100, 100, "green")
self._create_token(200, 100, "black")
def _create_token(self, x, y, color):
self.canvas.create_rectangle(x-25, y-25, x+25, y+25, outline=color, fill=color, tags=color)
self.canvas.tag_bind(color, "<ButtonPress-1>", self.on_token_press)
def on_token_press(self,event):
Rx = self.canvas.find_closest(event.x, event.y)
x = 0
y = 5
for i in range(25):
time.sleep(0.025)
self.canvas.move(Rx, x, y)
self.canvas.update()
for i in range(25):
time.sleep(0.025)
self.canvas.move(Rx, x, -y)
self.canvas.update()
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack()
root.mainloop()
You should not use blocking tasks in a GUI, they live inside an event loop that allows them to verify events such as keyboard, mouse, etc, if you use those tasks the GUI will probably freeze. what you should do is use after() for periodic tasks, in the following solution I have proposed to create a class that manages the animation in a simple way.
#!python3
import tkinter as tk
import time
class AbstractAnimation:
def __init__(self, canvas, id_item, duration, _from = 0, _to = 1):
self.canvas = canvas
self.id_item = id_item
self._progress = 0
self._from = _from
self._to = _to
self.t = max(10, int(duration/(self._to -self._from)))
def start(self):
self.canvas.after(self.t, self.on_timeout)
def on_timeout(self):
if self._from <= self._progress < self._to:
self.interpolated(self._from, self._to, self._progress)
self._progress += 1
self.canvas.after(self.t, self.on_timeout)
def interpolated(self, _from, _to, _progress):
pass
class Animation(AbstractAnimation):
def interpolated(self, _from, _to, _progress):
x, y = 0, 5
if _progress < 25:
self.canvas.move(self.id_item, x, y)
else:
self.canvas.move(self.id_item, x, -y)
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
# create a canvas
self.canvas = tk.Canvas(width=400, height=400)
self.canvas.pack()
# create a couple of movable objects
self._create_token(100, 100, "green")
self._create_token(200, 100, "black")
def _create_token(self, x, y, color):
self.canvas.create_rectangle(x-25, y-25, x+25, y+25, outline=color, fill=color, tags=color)
self.canvas.tag_bind(color, "<ButtonPress-1>", self.on_token_press)
def on_token_press(self,event):
Rx = self.canvas.find_closest(event.x, event.y)
animation = Animation(self.canvas, Rx, 1250, 0, 50)
animation.start()
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack()
root.mainloop()
As someone mention is the comments and #eyllanesc also mentions in his answer, you generally shouldn't call time.sleep() in a tkinter program because doing so temporarily halts its mainloop() which essentially halts the running GUI for the duration. Instead you should use the universal after() method.
However, you don't really need to use to get delays to animation. Instead it can be used to periodically run an arbitrary function within the mainloop(), which provides the leverage to animate things if desired.
In the code below, this is done by first defining a Token class to encapsulate the values associated with one, and then creating a list of them named _self._tokens, and finally using after() to schedule moving all the items in it that are currently active. The function to be called by after() in this case is the Example._update_tokens() method.
Here's code showing how to implement this approach:
import tkinter as tk
UPDATE_RATE = 10 # Updates-per-second.
UPDATE_DELAY = 1000//UPDATE_RATE # msec delay between updates.
class Token:
WIDTH, HEIGHT, INCR = 25, 25, 1
def __init__(self, canvas, x, y, color, max_value, dx, dy):
self.canvas, self.x, self.y = canvas, x, y
self.color, self.max_value, self.dx, self.dy = color, max_value, dx, dy
self.value, self.moving, self.saved_direction = 0, 0, 1
self.id = self.canvas.create_rectangle(x-self.WIDTH, y-self.HEIGHT,
x+self.WIDTH, y+self.HEIGHT,
outline=color, fill=color)
self.canvas.tag_bind(self.id, "<ButtonPress-1>", self._toggle)
def _toggle(self, _event):
""" Start movement of object if it's paused otherwise reverse its
direction.
"""
if self.moving:
self.moving = -self.moving # Reverse movement.
else: # Start it moving.
self.moving = self.saved_direction
def start(self):
self.moving = self.saved_direction
def pause(self):
if self.moving:
self.saved_direction = self.moving
self.moving = 0
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.canvas = tk.Canvas(width=400, height=400)
self.canvas.pack()
# Create list of movable objects.
self._tokens = []
self._tokens.append(Token(self.canvas, 100, 100, "green", 25, 0, 5))
self._tokens.append(Token(self.canvas, 200, 100, "black", 25, 0, 5))
tk.Button(self, text='Go', command=self._start_paused_tokens).pack(side=tk.LEFT)
tk.Button(self, text='Pause', command=self._pause_tokens).pack(side=tk.LEFT)
# Start the updating of active objects in _tokens list.
self.after(UPDATE_DELAY, self._update_tokens)
def _start_paused_tokens(self):
""" Start any paused Tokens. """
for token in self._tokens:
if token.moving == 0:
token.start()
def _pause_tokens(self):
""" Stop any moving Tokens. """
for token in self._tokens:
if token.moving != 0:
token.pause()
def _update_tokens(self):
""" Update any objects in Tokens lst that aren't paused. """
for token in self._tokens:
if token.moving > 0:
if token.value < token.max_value:
token.value += token.INCR
token.canvas.move(token.id, token.dx, token.dy)
else:
token.value = token.max_value
token.moving = -token.moving # Reverse moving.
token.canvas.move(token.id, token.dx, -token.dy)
elif token.moving < 0:
if token.value > 0:
token.value -= token.INCR
token.canvas.move(token.id, token.dx, -token.dy)
else:
token.value = 0
token.moving = -token.moving # Reverse moving.
token.canvas.move(token.id, token.dx, token.dy)
self.after(UPDATE_DELAY, self._update_tokens) # Continue doing updates.
def on_token_press(self, event):
closest_token = self.canvas.find_closest(event.x, event.y)
dx, dy = 0, 5
for i in range(25):
time.sleep(0.025)
self.canvas.move(closest_token, dx, dy)
self.canvas.update()
for i in range(25):
time.sleep(0.025)
self.canvas.move(closest_token, dx, -dy)
self.canvas.update()
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack()
root.mainloop()
So I have been playing with tkinter to try add a gui to a lift simulator project I have written for university. It is not really needed, but I would like to add it.
Here is the code that I currently have.
import tkinter as tk
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
master.title("Test") #Controls the window title.
self.pack()
self.createWidgets()
def createWidgets(self):
floors = [i for i in range(41)]
buttons = []
xPos = 0
yPos = 0
for floor in floors:
if(yPos == 5):
xPos = xPos + 1
yPos = 0
if(xPos == 8):
yPos = 2
self.button = tk.Button(self, width=3, text=floor,
command = lambda f=floor: self.pressed(f))
self.button.grid(row=xPos, column =yPos)
yPos = yPos +1
self.QUIT = tk.Button(self, text="QUIT", fg="red",
command=root.destroy).grid(row = xPos, column = yPos)
def pressed(self, index):
print("number pressed", index)
self.button.configure(bg = "red")
root = tk.Tk()
app = Application(master=root)
app.mainloop()
This is all fine and dandy other than when the button is pressed it prints out the correct number, but it changes the background of the last button (number 40) to red, not the one pressed.
If you could let me know what needs correcting that would be great.
Thanks
self.button can only ever reference a single button, and it will always be whatever was assigned to it last. A simple solution is to store the button references in a dict, using floor as the key. Since you're passing that to the callback, you then have everything you need to reconfigure the button:
def createWidgets(self):
...
self.buttons = {}
for floor in floors:
...
self.buttons[floor] = tk.Button(...)
...
def pressed(self, index):
...
self.buttons[index].configure(bg="red")
Can anyone point me to where I can find info on making a listbox with the ability to drag and drop items for re-arranging? I've found some related to Perl, but I know nothing of that language and I'm pretty new to tkinter, so it was pretty confusing. I know how to generate listboxes, but I'm not sure how to re-order it through drag and drop.
Here is the code from Recipe 11.4:
import Tkinter
class DragDropListbox(Tkinter.Listbox):
""" A Tkinter listbox with drag'n'drop reordering of entries. """
def __init__(self, master, **kw):
kw['selectmode'] = Tkinter.SINGLE
Tkinter.Listbox.__init__(self, master, kw)
self.bind('<Button-1>', self.setCurrent)
self.bind('<B1-Motion>', self.shiftSelection)
self.curIndex = None
def setCurrent(self, event):
self.curIndex = self.nearest(event.y)
def shiftSelection(self, event):
i = self.nearest(event.y)
if i < self.curIndex:
x = self.get(i)
self.delete(i)
self.insert(i+1, x)
self.curIndex = i
elif i > self.curIndex:
x = self.get(i)
self.delete(i)
self.insert(i-1, x)
self.curIndex = i
Here's a modified recipe if you're dealing with MULTIPLE as the selectmode (as opposed to SINGLE).
Changes made:
When dragging over an already selected item, it would deselect it which was a bad user-experience.
When clicking an item that was selected, it would become unselected from the click. So I added a self.curState bit that kept track of whether the clicked-on item's state was initially selected or not. When you drag it around, it doesn't lose its state.
I also bound two events to the Button-1 event using add='+' but that might avoidable by simply keeping it all under setCurrent.
I prefer activestyle equals 'none'.
Made this Listbox tk.MULTIPLE instead of tk.SINGLE.
Here is the code:
class Drag_and_Drop_Listbox(tk.Listbox):
""" A tk listbox with drag'n'drop reordering of entries. """
def __init__(self, master, **kw):
kw['selectmode'] = tk.MULTIPLE
kw['activestyle'] = 'none'
tk.Listbox.__init__(self, master, kw)
self.bind('<Button-1>', self.getState, add='+')
self.bind('<Button-1>', self.setCurrent, add='+')
self.bind('<B1-Motion>', self.shiftSelection)
self.curIndex = None
self.curState = None
def setCurrent(self, event):
''' gets the current index of the clicked item in the listbox '''
self.curIndex = self.nearest(event.y)
def getState(self, event):
''' checks if the clicked item in listbox is selected '''
i = self.nearest(event.y)
self.curState = self.selection_includes(i)
def shiftSelection(self, event):
''' shifts item up or down in listbox '''
i = self.nearest(event.y)
if self.curState == 1:
self.selection_set(self.curIndex)
else:
self.selection_clear(self.curIndex)
if i < self.curIndex:
# Moves up
x = self.get(i)
selected = self.selection_includes(i)
self.delete(i)
self.insert(i+1, x)
if selected:
self.selection_set(i+1)
self.curIndex = i
elif i > self.curIndex:
# Moves down
x = self.get(i)
selected = self.selection_includes(i)
self.delete(i)
self.insert(i-1, x)
if selected:
self.selection_set(i-1)
self.curIndex = i
Example demo:
root = tk.Tk()
listbox = Drag_and_Drop_Listbox(root)
for i,name in enumerate(['name'+str(i) for i in range(10)]):
listbox.insert(tk.END, name)
if i % 2 == 0:
listbox.selection_set(i)
listbox.pack(fill=tk.BOTH, expand=True)
root.mainloop()
The following class is a Listbox with EXTENDED selection mode that enables dragging around multiple selected items.
Default selecting mechanisms are preserved (by dragging and clicking, including holding down Ctrl or Shift), with the exception of dragging an already selected item without holding Ctrl.
To drag the selection, drag one of the selected items below the last selected item or above the first selected item.
To scroll the listbox while dragging selection, use the mousewheel or move the cursor near or beyond the top or bottom of the listbox. => This could be improved: since it's bound to the B1‑Motion event, extra movement of the mouse is needed to continue the scroll. Feels buggy in longer listboxes.
If the selection is discontinuous, dragging will make it continuous by moving the unselected items up or down, respectively.
The above means that to drag just one item, it needs to be selected first, then clicked again and dragged.
import tkinter as tk;
class ReorderableListbox(tk.Listbox):
""" A Tkinter listbox with drag & drop reordering of lines """
def __init__(self, master, **kw):
kw['selectmode'] = tk.EXTENDED
tk.Listbox.__init__(self, master, kw)
self.bind('<Button-1>', self.setCurrent)
self.bind('<Control-1>', self.toggleSelection)
self.bind('<B1-Motion>', self.shiftSelection)
self.bind('<Leave>', self.onLeave)
self.bind('<Enter>', self.onEnter)
self.selectionClicked = False
self.left = False
self.unlockShifting()
self.ctrlClicked = False
def orderChangedEventHandler(self):
pass
def onLeave(self, event):
# prevents changing selection when dragging
# already selected items beyond the edge of the listbox
if self.selectionClicked:
self.left = True
return 'break'
def onEnter(self, event):
#TODO
self.left = False
def setCurrent(self, event):
self.ctrlClicked = False
i = self.nearest(event.y)
self.selectionClicked = self.selection_includes(i)
if (self.selectionClicked):
return 'break'
def toggleSelection(self, event):
self.ctrlClicked = True
def moveElement(self, source, target):
if not self.ctrlClicked:
element = self.get(source)
self.delete(source)
self.insert(target, element)
def unlockShifting(self):
self.shifting = False
def lockShifting(self):
# prevent moving processes from disturbing each other
# and prevent scrolling too fast
# when dragged to the top/bottom of visible area
self.shifting = True
def shiftSelection(self, event):
if self.ctrlClicked:
return
selection = self.curselection()
if not self.selectionClicked or len(selection) == 0:
return
selectionRange = range(min(selection), max(selection))
currentIndex = self.nearest(event.y)
if self.shifting:
return 'break'
lineHeight = 15
bottomY = self.winfo_height()
if event.y >= bottomY - lineHeight:
self.lockShifting()
self.see(self.nearest(bottomY - lineHeight) + 1)
self.master.after(500, self.unlockShifting)
if event.y <= lineHeight:
self.lockShifting()
self.see(self.nearest(lineHeight) - 1)
self.master.after(500, self.unlockShifting)
if currentIndex < min(selection):
self.lockShifting()
notInSelectionIndex = 0
for i in selectionRange[::-1]:
if not self.selection_includes(i):
self.moveElement(i, max(selection)-notInSelectionIndex)
notInSelectionIndex += 1
currentIndex = min(selection)-1
self.moveElement(currentIndex, currentIndex + len(selection))
self.orderChangedEventHandler()
elif currentIndex > max(selection):
self.lockShifting()
notInSelectionIndex = 0
for i in selectionRange:
if not self.selection_includes(i):
self.moveElement(i, min(selection)+notInSelectionIndex)
notInSelectionIndex += 1
currentIndex = max(selection)+1
self.moveElement(currentIndex, currentIndex - len(selection))
self.orderChangedEventHandler()
self.unlockShifting()
return 'break'