Mouse click results in unexpected behavior using wxPython & wx.Panel - python-2.7

I am writing an application in which the following steps are intended to be executed:
1 - user clicks in blue area (a wx.Panel) and a white circle appears;
2 - user clicks Select Start button;
3 - user clicks in the white circle and it changes to green (by drawing a green circle over the white one)
The problem is that when step 3 is executed the green circle does not appear where the mouse is clicked. For example, clicking in the blue area registers a point at (223, 486). After clicking the Select Start button and then clicking in the white circle a point registers at (211, 464), and a green circle appears outside the white circle. I don’t understand why this is happening and would appreciate any help in resolving the problem.
Using python 2.7 with wxPython 3.0.3.0 on MacOs
import wx
class Test(wx.Frame):
def __init__(self, parent, title):
super(Test, self).__init__(parent, title = title, size = (820,900))
self.startSelected = False
self.radius = 10
self.panel = wx.Panel(self, size=(800,800))
self.panel.SetBackgroundColour(wx.Colour(0,0,200))
self.panel.Bind(wx.EVT_LEFT_DOWN, self.onMouseDown)
self.gbs = wx.GridBagSizer(0,0)
self.startBtn = wx.Button(self, wx.ID_ANY, label = 'Select Start')
self.startBtn.Bind(wx.EVT_BUTTON, self.selectStart)
self.gbs.Add(self.panel, span = (1,4), pos=(1,1),flag=wx.EXPAND | wx.ALL)
self.gbs.Add(self.startBtn, pos=(2,1))
self.SetSizer(self.gbs)
def onMouseDown(self, e):
pt = e.GetPosition()
print pt.x, pt.y
if e.LeftDown():
if self.startSelected:
color = wx.Colour(0,255,0)
self.paint(pt, color)
else:
color = wx.Colour(255,255,255)
self.paint(pt, color)
def paint(self, pt, color):
dc = wx.ClientDC(self)
b = wx.Brush(color)
dc.SetBrush(b)
dc.DrawCircle(pt.x, pt.y, self.radius)
def selectStart(self, e):
self.startSelected = True
if __name__ == '__main__':
app = wx.App()
Test(None, 'Test')
app.MainLoop()

The wx.ClientDC is relative to the object passed to it. As coded in the paint method, dc = wx.ClientDC(self), self is the wx.Frame. Since drawing is done on the wx.Panel, dc = wx.ClientDC(self.panel) should be used. This results in the expected behavior.

Related

Hiding popup frame with witdraw() results in unfitted window size

I'm creating a popup window in tkinter and want to hide it until it is created, labeled and centered on the screen to prevent the window to briefly flash empty at the wrong position.
But if i implemente these the resulting window acts if its contents were never distributed by pack() and the size of the window is set to a default 200 x 200 px.
I also tried after() and update_idletasks() since i guessed the issue in the sequence of drawing but with no success.
To hide it i use the withdraw() and deiconify() functions as stated in many other Posts including this one:
Tkinter - Preload window?
My resulting code looks like this:
Frame creation
class NotificationPopup(Tk.Toplevel):
def __init__(self, root, text, title):
Tk.Toplevel.__init__(self, root)
# Hide window until created
self.withdraw()
# Slaved to parent. Shown over parent window
self.transient(root)
# Stops interaction with parent until child is solved
self.grab_set()
self.label = Tk.Label(self, text=text, justify=Tk.LEFT, padx=10)
self.label.pack()
self.button = Tk.Button(self, text='Ok', command=self.destroy)
self.button.pack()
self.button.focus_set()
self.resizable(False, False)
self.relief = Tk.GROOVE
# Bind return to the button to close the window
self.button.bind('<Return>', (lambda event: self.destroy()))
Toolbox.center_toplevel(self)
# Show window
self.deiconify()
Center method
# Center a window based on screen dimensions and window size
def center_toplevel(toplevel):
toplevel.update_idletasks()
# Toplevel window dimensions
w = toplevel.winfo_width()
h = toplevel.winfo_height()
# get screen width and height
ws = toplevel.winfo_screenwidth() # width of screen
hs = toplevel.winfo_screenheight() # height of screen
# calculate x and y coordinates for the Tk root window
x = (ws / 2) - (w / 2)
y = (hs / 2) - (h / 2)
# Set dimension of window and placement on screen
toplevel.geometry('%dx%d+%d+%d' % (w, h, x, y))
Running the code without withdraw() on my application Shows the width and heigt at 450 and 400 px while including withdraw() shrinks it to 200 x 200 px and don't refit it to it's content.

How to keep a Tkinter widget on top of the others

I have some code that moves images left and right but I do not want them to appear on top of the right border, which I draw as a rectangle.
What are the options in Tkinter to keep a widget (in my example a rectangle) on top of some other widgets (in my code a tile, which is an image)?
I am drawing the rectangle and the image on one canvas.
I can image that using two canvas could do the trick, but are there any other options/settings?
Thanks
import Tkinter as tk # for Python2
import PIL.Image, PIL.ImageTk
win = tk.Tk()
#Create a canvas
canvas = tk.Canvas(win, height = 500, width = 500)
#Create a rectangle on the right of the canvas
rect = canvas.create_rectangle(250, 0, 500, 250, width = 2, fill = "red")
#Create an image
SPRITE = PIL.Image.open("sprite.png")
tilePIL = SPRITE.resize((100, 100))
tilePI = PIL.ImageTk.PhotoImage(tilePIL)
tile = canvas.create_image(100, 100, image = tilePI, tags = "a tag")
#Place the canvas
canvas.grid(row = 1, column = 0, rowspan = 5)
#Move the tile to the right.
#The tile will go on top of red rectangle. How to keep the rectangle on top of the tile?
canvas.coords(tile, (300, 100))
canvas.mainloop()
Use tag_raise() method:
canvas.tag_raise(tile)

Draggable point in matplotlib - How to retrieve point coordinates

I would like to be able to move a point (matplotlib.patches.Ellipse) along a vertical line. To do so, I'm calling the Dragable rectangle class (http://matplotlib.org/1.3.1/users/event_handling.html) inside a PySide QtGui.QWidget.
The dragging of my ellipse works fine, but I'm stuck in how to retrieve the final y-coordinate of the ellipse when I release the mouse button (I want to have a QLabel next to the plot that contains this y-coordinate). From on_release I have its final position:
def on_release(self, event):
if DraggablePoint.lock is not self:
return
self.point.newYcoordinate = self.point.center[1]
self.press = None
DraggablePoint.lock = None
# draw everything but the selected point and store the pixel buffer
canvas = self.point.figure.canvas
axes = self.point.axes
self.point.set_animated(True)
canvas.draw()
self.background = canvas.copy_from_bbox(self.point.axes.bbox)
# now redraw just the point
axes.draw_artist(self.point)
# and blit just the redrawn area
canvas.blit(axes.bbox)
but how can I connect this to the text of my QLabel (and obtain that this label is updated each time the ellipse is moved)?
self.FlexibleValue = patches.Ellipse(xy=(0.5, 0.5), width=0.2, height=0.2, edgecolor='r', facecolor='r', lw=2)
self.axesResp.add_patch(self.FlexibleValue)
self.dragValue = DraggablePoint(self.FlexibleValue)
self.dragValue.connect()
by adding a signal as tcaswell suggested:
def on_release(self, event):
...
self.signal.updatedSignal.emit(str(self.point.newYcoordinate))
and then retrieving the y-coordinate:
self.dragValue.signal.updatedSignal.connect(self.updatedValue)
with:
def updatedValue(self, newValue):
self.labelValue.setText(newValue)
and:
class UpdatedSignal(QtCore.QObject):
updatedSignal = QtCore.Signal(str)

Aligning Label in Frame Tkinter

I am new to Python and even newer to Tkinter.
I am currently practicing how to use Frames and Labels and
the problem I am encountering is, when I put Labels on a frame with some buttons next to each label,
the alignment is not good to look at.
Here is the code:
from Tkinter import *
class GUI():
def __init__(self):
self.namelist = ["Mark","Anna","Jason","Lenna","Leo","Zucharich","Robinson","AReallyLongNameThatMightExist"]
self.canvas = Canvas(width=1200,height=700)
self.canvas.pack(expand=YES,fill=BOTH)
def Friends(self):
controlframe = Frame(self.canvas)
controlframe.place(x=600,y=300)
#Frame for showing names of friends
for x in self.namelist:
frame = Frame(controlframe)
frame.pack()
Name = Label(frame,text="%s "%x).pack(side=LEFT)
chatButton = Button(frame,text="Chat").pack(side=LEFT)
delButton = Button(frame,text="Delete").pack(side=LEFT)
setcloseButton = Button(frame,text="Set Close").pack(side=LEFT)
setgroupButton = Button(frame,text="Set Group").pack(side=LEFT)
mainloop()
GUI = GUI()
GUI.Friends()
What should I do so that the alignment of the Label(=name) and the button is equal to the other ones so that they will form a shape of a rectangle and not some zigzag?
It is almost always better in Tk to use the grid geometry manager. It is much more flexible once you come to understand how it works. Converting your example to use grid solves your problem as shown below but you should experiment with it a bit. Try removing the 'sticky="W"' from the label for instance and see that the centering of the widgets within the row or column can be controlled. To get your frame responding to resizes sensibly you should investigate the columnconfigure and rowconfigure options for the grid geometry management as well.
from Tkinter import *
class GUI():
def __init__(self):
self.namelist = ["Mark","Anna","Jason","Lenna",
"Leo","Zucharich","Robinson",
"AReallyLongNameThatMightExist"]
self.canvas = Canvas(width=1200,height=700)
self.canvas.pack(expand=YES,fill=BOTH)
def Friends(self):
frame = Frame(self.canvas)
frame.place(x=600,y=300)
#Frame for showing names of friends
row = 0
for x in self.namelist:
label = Label(frame,text="%s "%x)
chatButton = Button(frame,text="Chat")
delButton = Button(frame,text="Delete")
setcloseButton = Button(frame,text="Set Close")
setgroupButton = Button(frame,text="Set Group")
label.grid(row=row, column=0, sticky="W")
chatButton.grid(row=row, column=1)
delButton.grid(row=row, column=2)
setcloseButton.grid(row=row, column=3)
setgroupButton.grid(row=row, column=4)
row = row + 1
mainloop()
GUI = GUI()
GUI.Friends()

Keep a Frame in an other window Frame

My programm create a Frame with three panels in an horizontal boxsizer. A menu with "new window" item for create a seconde Frame. I give the seconde panel as parent of the seconde window. I wante the seconde Frame stays in the seconde panel area of my first frame.
if user move one of the two windows, the seconde stays in the panel screen area.
Do you know a way or something for that?
I tried a little something, but using is not very aesthetic.
and
import wx
class MainWindow(wx.Frame):
def __init__(self,parent,id):
wx.Frame.__init__(self,parent,id,'Python Test App',size=(600,400))
#Widgets
panel_gch = wx.Panel(self,-1,size = (150,-1))
panel_gch.SetBackgroundColour('white')
self.panel=wx.Panel(self,-1,size=(300,400))
self.panel.SetBackgroundColour((200,230,200))
panel_drt = wx.Panel(self,-1,size = (150,-1))
panel_drt.SetBackgroundColour('white')
box = wx.BoxSizer(wx.HORIZONTAL)
self.SetSizer(box)
#Add
box.Add(panel_gch,0,wx.EXPAND)
box.Add(self.panel,1,wx.EXPAND)
box.Add(panel_drt,0,wx.EXPAND)
#Menu
status=self.CreateStatusBar()
menubar=wx.MenuBar()
file_menu=wx.Menu()
ID_FILE_NEW = 1
file_menu.Append(ID_FILE_NEW,"New Window","This is a new window")
menubar.Append(file_menu,"File")
self.SetMenuBar(menubar)
#bind and layout
self.Bind(wx.EVT_MENU, self.get_new_window)
panel_gch.Layout()
self.panel.Layout()
panel_drt.Layout()
self.Layout()
def get_new_window(self,event): # create new window
self.new = NewWindow(self.panel,-1)
self.new.Show(True)
self.new.Bind(wx.EVT_MOVE,self.window2_on_move)
def window2_on_move(self,event): # Window2 must stay in
x, y = event.GetPosition()
v,w =self.panel.GetScreenPosition()
s,t = self.panel.GetClientSizeTuple()
if x < v:
self.new.Move((v,-1))
if y < w:
self.new.Move((-1,w))
if x+200 > v+s:
self.new.Move((v+s-200,-1))
if y+200 > w+t:
self.new.Move((-1,w+t-200))
class NewWindow(wx.MiniFrame):
def __init__(self,MainWindow,id):
wx.MiniFrame.__init__(self, MainWindow, id, 'New Window', size=(200,200),\
style = wx.MINIMIZE | wx.CAPTION | wx.CLOSE_BOX | wx.CLOSE_BOX)
self.CenterOnParent()
if __name__=='__main__':
app=wx.PySimpleApp()
frame=MainWindow(parent=None,id=-1)
frame.Show()
app.MainLoop()
What you probably want is AUI. I personally recommend the wx.lib.agw.aui set rather than wx.aui as the former is pure Python and has had a LOT more recent work done on it. There are multiple examples in the wxPython demo package. You can also read about it here:
http://wxpython.org/Phoenix/docs/html/lib.agw.aui.framemanager.AuiManager.html
Thanks you very much Mike, exactly what I needed.
With wxpython I found This way:
the child stays in the panel area and it follows the window parent when moving.
import wx
class MainWindow(wx.Frame):
def __init__(self,parent,id):
wx.Frame.__init__(self,parent,id,'Python Test App',size=(600,400))
self.new = None
#Widgets
self.position = (0,0)
panel_gch = wx.Panel(self,-1,size = (150,-1))
panel_gch.SetBackgroundColour('white')
self.panel=wx.Panel(self,-1,size=(300,400))
self.panel.SetBackgroundColour((200,230,200))
panel_drt = wx.Panel(self,-1,size = (150,-1))
panel_drt.SetBackgroundColour('white')
box = wx.BoxSizer(wx.HORIZONTAL)
self.SetSizer(box)
#Add
box.Add(panel_gch,0,wx.EXPAND)
box.Add(self.panel,1,wx.EXPAND)
box.Add(panel_drt,0,wx.EXPAND)
#Menu
status=self.CreateStatusBar()
menubar=wx.MenuBar()
file_menu=wx.Menu()
ID_FILE_NEW = 1
file_menu.Append(ID_FILE_NEW,"New Window","This is a new window")
menubar.Append(file_menu,"File")
self.SetMenuBar(menubar)
#bind and layout
self.Bind(wx.EVT_MENU, self.get_new_window)
panel_gch.Layout()
self.panel.Layout()
panel_drt.Layout()
self.Layout()
def get_new_window(self,event): # create new window
if self.new == None:
self.win_one_move = False
self.new = NewWindow(self.panel,-1)
self.new.Show(True)
self.new.Bind(wx.EVT_MOVE,self.window2_on_move)
self.Bind(wx.EVT_MOVE,self.window1_on_move)
v,w =self.GetPosition()
x, y = self.new.GetPosition()
self.get_windows_position((x-v),(y-w))
def get_windows_position(self,x,y):
self.position = (x,y)
print "check",self.position
def window2_on_move(self,event): # Window2 must stay in
if self.win_one_move == False:
x, y = event.GetPosition()
v,w =self.panel.GetScreenPosition()
s,t = self.panel.GetClientSizeTuple()
new_x,new_y = self.new.GetClientSizeTuple()
if x < v:
self.new.Move((v,-1))
if y < w:
self.new.Move((-1,w))
if x+new_x > v+s:
self.new.Move((v+s-new_x,-1))
if y+new_y > w+t:
self.new.Move((-1,w+t-new_y))
v,w =self.GetPosition()
x,y = self.new.GetPosition()
self.get_windows_position((x-v),(y-w))
if self.win_one_move == True:
self.win_one_move = False
def window1_on_move(self,event):
self.win_one_move = True
print "1 move"
x,y = self.GetPosition()
self.new.Move((x+self.position[0],y+self.position[1]))
print self.position
class NewWindow(wx.MiniFrame):
def __init__(self,MainWindow,id):
wx.MiniFrame.__init__(self, MainWindow, id, 'New Window', size=(200,200),\
style = wx.CAPTION | wx.CLOSE_BOX | wx.CLOSE_BOX)
self.CenterOnParent()
if __name__=='__main__':
app=wx.PySimpleApp()
frame=MainWindow(parent=None,id=-1)
frame.Show()
app.MainLoop()
Can be use by another.Thanks