How to keep a Tkinter widget on top of the others - python-2.7

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)

Related

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

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.

Python 2.7, Tkinter, make a point "trace" a waveform

I'm trying to make a widget that demonstrates the relationship between a sine wave and it's phasor diagram. I want a pointer that follows the user's mouse movements but instead of freely moving around the page, I want it to stick to the waveform I've plotted and only follow the co-ordinates that make up that line. Kind of like a slider but along a sine wave! Any suggestions? Thanks. Here's what I've got so far;
import math
import Tkinter as tk
from Tkinter import PhotoImage, Canvas
from PIL import Image, ImageTk
sinwidget = tk.Tk()
main_canvas = tk.Canvas(sinwidget, width = 400, height = 400, bg= "white")
main_canvas.pack()
def callback(event):
canvas = event.widget
x = canvas.canvasx(event.x)
y = canvas.canvasy(event.y)
print canvas.find_closest(x, y)
draw(event.x, event.y)
def draw(x, y):
box.coords(pointer, x-20, y-20, x+20, y+20)
def exit_():
sinwidget.destroy()
box = main_canvas
box.bind('<Motion>', callback)
box.pack()
pointer = box.create_rectangle(0.2,0.2,0.2,0.2)
wavelength = 360
height = 400
center = height//2
degree = 1
increment = 0.0175
amplitude = -80
sin = []
for x in range(360):
sin.append(x * degree)
sin.append(int(math.sin(x * increment) * amplitude) + center)
sinwave = main_canvas.create_line(sin, fill="red", width=2.0)
xaxis = main_canvas.create_line(0, center, wavelength, center, fill="black",
width=3.0)
yaxis = main_canvas.create_line(2, 100, 2, 300, fill="black", width=3.0)
exit_button = tk.Button(sinwidget, text = "exit", command = exit_)
exit_button.pack()
sinwidget.mainloop()
Thanks for any help you can offer!

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.

Make part of image transparent with pyglet

I have two images, "desert" image over "winter" image:
import pyglet
desert_img = pyglet.image.load('assets/desert.jpg')
desert = pyglet.sprite.Sprite(desert_img, x=50, y=50)
winter_img = pyglet.image.load('assets/winter.jpg')
winter = pyglet.sprite.Sprite(winter_img, x=0, y=0)
window = pyglet.window.Window()
#window.event
def on_draw():
winter.draw()
desert.draw()
pyglet.app.run()
Result is:
I would like draw a square of "transparency" on desert image (winter image should be visible in this square). Is it possible ? How to do that ?
I found many question who permit to make transparency with image itself (png, alpha ... but no like i want).
Based on Torxed suggestion, replace image content with transparent bytes where we want to make it transparent:
import io
from PIL import Image
import pyglet
from PIL.PngImagePlugin import PngImageFile
def replace_content_with_transparency(img: PngImageFile, x, y, width, height):
pixels = img.load()
for i in range(x, width):
for j in range(y, height):
pixels[i, j] = (0, 0, 0, 0)
desert_png = Image.open('assets/desert.png')
replace_content_with_transparency(desert_png, 32, 32, 123, 123)
fake_file = io.BytesIO()
desert_png.save(fake_file, format='PNG')
desert_img = pyglet.image.load('img.png', file=fake_file)
desert = pyglet.sprite.Sprite(desert_img, x=50, y=50)
winter_img = pyglet.image.load('assets/winter.jpg')
winter = pyglet.sprite.Sprite(winter_img, x=0, y=0)
window = pyglet.window.Window()
#window.event
def on_draw():
winter.draw()
desert.draw()
pyglet.app.run()

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)