Tkinter: Strange behavior using place() and changing cursors

M

Mudcat

I was trying to design a widget that I could drag and drop anywhere in
a frame and then resize by pulling at the edges with the mouse. I have
fiddled with several different approaches and came across this behavior
when using the combination of place() and configure(cursor = ...) This
problem doesn't occur if you remove either one of these elements.

It's a very basic design of a button wrapped in a sizer frame and is
supposed to work like any window that can be resized. The mouse will
change into a resize cursor when it hits the sizer frame (button's
edge).

However when the cursor hits the right edge it gets in some kind of
loop where it keeps entering and leaving the frame. You can also cause
this to happen by entering the widget from the bottom and slowly moving
down. However it doesn't happen if you enter from the left or the top.

Here's the code, boiled down to the basic components that seem to
affect it in some way. Is there something I'm supposed to do in order
to prevent this from happening?

Thanks,
Marc

from Tkinter import *

class Gui:
def __init__(self, master):
master.geometry("200x100")
btn = Widget(master)

class Widget:
def __init__(self, master):
self.master = master
self.buildFrame()
self.buildWidget()

def buildFrame(self):
self.f = Frame(self.master, height=32, width=32, relief=RIDGE,
borderwidth=2)
self.f.place(relx=.5,rely=.5)
self.f.bind( '<Enter>', self.enterFrame )
self.f.bind( '<Leave>', self.leaveFrame )

def buildWidget(self):
self.b = Button(self.f, text="Sure!")
self.b.pack(fill=BOTH, expand=1)

def enterFrame(self, event):
self.f.configure(cursor = 'sb_h_double_arrow')

def leaveFrame(self, event):
self.f.configure(cursor = '' )

root = Tk()
ent = Gui(root)
root.mainloop()
 
W

Wojciech =?iso-8859-2?Q?Mu=B3a?=

Mudcat said:

You have to set cursor once, Tk change it automatically:
def buildFrame(self):
self.f = Frame(self.master, height=32, width=32, relief=RIDGE,
borderwidth=2)
self.f.place(relx=.5,rely=.5)
#self.f.bind( '<Enter>', self.enterFrame )
#self.f.bind( '<Leave>', self.leaveFrame )
self.f.configure(cursor = 'sb_h_double_arrow')
 
M

Mudcat

Wojciech said:
Mudcat said:

You have to set cursor once, Tk change it automatically:
def buildFrame(self):
self.f = Frame(self.master, height=32, width=32, relief=RIDGE,
borderwidth=2)
self.f.place(relx=.5,rely=.5)
#self.f.bind( '<Enter>', self.enterFrame )
#self.f.bind( '<Leave>', self.leaveFrame )
self.f.configure(cursor = 'sb_h_double_arrow')


The problem is I need the ability to change it dynamically. I don't
want the cursor to be the double_arrow the whole time the mouse hovers
inside that frame. It's supposed to be a resize arrow when the mouse is
on the frame border, and regular cursor once it passes to the inner
part of the frame.

Unless there is a better way to do this, I have written code to
determine if the mouse is on the edge or not. There doesn't seem to be
a 'mouseover' type event to determine if the mouse is currently on the
frame border itself. As a result, I calculate the position of the mouse
and set the cursor appropriately.

I have also determined that this is not a problem if the button is not
packed inside the frame. So somehow the interaction of the internal
button is causing this problem.
 
W

Wojciech =?iso-8859-2?Q?Mu=B3a?=

Mudcat said:
I have also determined that this is not a problem if the button is not
packed inside the frame. So somehow the interaction of the internal
button is causing this problem.

Problem is really strange, and seems to be a Tk issue, not Tkinter.
I've observed that if method configure is called and **any** parameter
of frame is changed, then frame "forgets" all its children, gets natural
size and then pack children again! This just(?) causes flickering, and
do not depend on geometry manager --- all place, pack and grid work in
this way. But only place causes the problem --- I guess it do not block
events, or generate some events.

Workaround:

class Widget:
def __init__(self, master):
self.master = master
self.buildFrame()
self.buildWidget()
self.x = 0
self.y = 0

# [snip]

def enterFrame(self, event):
if self.x != event.x or self.y != event.y:
self.f.configure(cursor = 'sb_h_double_arrow')
self.x, self.y = event.x, event.y

def leaveFrame(self, event):
if self.x != event.x or self.y != event.y:
self.f.configure(cursor = '')
self.x, self.y = event.x, event.y

w.
 
J

John McMonagle

Mudcat said:
Is there something I'm supposed to do in order
to prevent this from happening?

Yes. Instead of configuring the cursor on the frame, do it on the master:

self.master.configure(cursor='sb_h_double_arrow')
 
E

Eric Brunel

The problem is I need the ability to change it dynamically. I don't
want the cursor to be the double_arrow the whole time the mouse hovers
inside that frame. It's supposed to be a resize arrow when the mouse is
on the frame border, and regular cursor once it passes to the inner
part of the frame.

Why don't you create a Frame for the inside with no border, and another
Frame for the border? This way, you can have a cursor setting for each of
them, i.e one for the inside and one for the border.

HTH
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,014
Latest member
BiancaFix3

Latest Threads

Top