Why can't I draw onto two different bitmaps in wxPython? - python

I am trying to draw a button on a bitmap object. Depending on the y position, it should draw on bitmap1 if the y position is within bmp1's height value, and bitmap2 if it isn't.
For some reason this does not work:
wx.Button(bitmap1 if ypos <= bmp1.GetHeight() else bitmap2, label='Run', id=i, pos=(xpos, ypos))
I can only draw the button on one wx.StaticBitmap image or the panel. The images parents are the panel.
This works fine if I want to switch between the bitmap or onto the panel directly.
What gives?
NOTE:
I managed to work around this using PIL to create a dynamic image large enough to accomodate my generated buttons (a continuous y-size, according to their count and placement), however this idea/code should still be valid.
If I substitute the 'bitmap2' value for the panel, and shift the bitmap2 image drawn on the panel by a bit, then I see that the program draws underneath bitmap2. Why? The image is placed exactly like bitmap1, and bitmap1 has no problems being drawn on it by buttons? :O

I figured out the problem:
The button's parent object should get the ypos according to the parent's dimensions, not on where it is drawn on the frame, like so:
wx.Button(bitmap1 if ypos <= bmp1.GetHeight() else bitmap2, label='Run {i}', id=i, pos=(80, ypos if ypos <= bmp1.GetHeight() else ypos-img_height))
ypos if ypos <= bmp1.GetHeight() else ypos-img_height
Finally!

Related

Repeating texture in a Rectangle of a variable size

I have a texture that I want to repeat inside a Rectangle drawn by canvas.before. The problem is that I don't know what is going to be the size of the Rectangle (it's used as a background for its widget).
For example, I have a Rectangle that has 48 px height and width 100 - 500 px. I want to fill its content by horizontally repeating a 48x48 texture.
I know and tried setting texture.wrap = 'repeat' and texture.uvsize and it works correctly but only if I know the widget size beforehand. For example, setting uvsize = (3, 1) for a widget with size 144x48 works fine.
However, this doesn't work when I want to update uvsize before redrawing the widget. I created a canvas callback and updated uvsize there but this has no effect for some reason:
...
with self.canvas.before:
self.cb = Callback(self.on_canvas_redraw)
...
def on_canvas_redraw(self, instr):
self.texture.uvsize = (self.width / 48, 1)
So how can I dynamically update uvsize? Or is there a better way to handle widget resize or a better way to this altogether?

Python tkinter: how to restrict mouse cursor within canvas?

I use tkinter's canvas to load an image and draw a vector on top of it (using create_line).
I would like to restrict the mouse movement when drawing this vector, so that it cannot be dragged outside of the image area, whatever it may be. The mouse cursor should just snap back to image boundaries.
I tried searching, and found various ways of dealing with this, ideally this would need to be cross-platform. So far, I couldn't make any of those various ways working... so I'm kindly asking for help! Thank you :)
OK in the end I decided not to restrict mouse cursor physically (by forcing it not to go beyond certain coordinates), but rather virtually (by storing the mouse position to a variable, then if-elseing it around the bounding box that it needed to stay in). So the mouse cursor goes wherever it wants, but when it's actually drawing something in - it stays within the designated area I want it to.
Drawing lines on a Canvas was the task, over the loaded image. Line shouldn't pass by the boundaries of the image. This is how it worked out:
imgsize = (int(self.viewport.cget('width')) - 1,int(self.viewport.cget('height')) - 1)
# limit the draggable mouse area to just the image dimensions
if event.x < 4:
currentx = 4
elif event.x > imgsize[0]:
currentx = imgsize[0]
else:
currentx = event.x
if event.y < 4:
currenty = 4
elif event.y > imgsize[1]:
currenty = imgsize[1]
else:
currenty = event.y
Then from that point onward it's create_line time.

How can I convert coordinate in tkinter?

I already tried to google it but I couldn't find anything ...
I created tkinter canvas with width 800, height 600
apparently, the left upside will be (0,0)
I want to change left downside to (0,0)
How can I do this???
You cannot change the coordinate system of the canvas. You can scroll the canvas so that 0,0 is in the bottom left, but the y coordinates will be negative going up.
In order to do that, you have to define your own function. Such:
def conv( coordinatePair ):
return { "x":coordinatePair["x"], "y":canvas.height-coordinatePair["y"]}
Where coordinatePair is a dictionary. That function will take your coordinate, flip the y, and return a new coordinate dictionary.

Cannot align text with a line drawn on an image

I'm trying to do some image manipulation with the python library Pillow (fork of PIL) and am coming across a weird problem. For some reason, when I try to draw a line and draw some text at the same y coordinate, they're not matching up. The text is a bit below the line, yet I have both graphics starting at the same point. Has anyone had this problem before and/or know how to solve it? Here's the code I'm using:
image = Image.open("../path_to_image/image.jpg")
draw = ImageDraw.Draw(image)
font = ImageFont.truetype("../fonts/Arial Bold.ttf", 180)
draw.line((0,2400, 500,2400), fill="#FFF", width=1)
draw.text((0, 2400), "Test Text", font=font)
image.save(os.path.join(root, "test1.jpg"), "JPEG", quality=100)
return
I get something similar (with sizes 10 times smaller):
This is happening because the (x,y) coordinates given to ImageDraw.text() are the top left corner of the text:
PIL.ImageDraw.Draw.text(xy, text, fill=None, font=None, anchor=None)
Draws the string at the given position.
Parameters:
xy – Top left corner of the text.
text – Text to be drawn.
font – An ImageFont instance.
fill – Color to use for the text.
This is confirmed in the code: the text is turned into a bitmap and then drawn at xy.
For those with a similar problem, I ended up creating a helper function that manually adjusts the font size until font.getsize(text)[1] returns the correctly sized text. Here's a snippet:
def adjust_font_size_to_line_height(font_location, desired_point_size, text):
adjusted_points = 1
while True:
font = ImageFont.truetype(font_location, adjusted_points)
height = font.getsize(text)[1]
if height != desired_point_size:
adjusted_points += 1
else:
break
return adjusted_points

How to scroll a tkinter canvas to an absolute position?

I'm using Python and tkinter. I have a Canvas widget that will display just one image. Most times the image will be larger than the canvas dimensions, but sometimes it will be smaller. Let's just focus on the first case (image larger than canvas).
I want to scroll the canvas to an absolute position that I have already calculated (in pixels). How can I do that?
After trying for around half hour, I got another solution that seems better:
self.canvas.xview_moveto(float(scroll_x+1)/img_width)
self.canvas.yview_moveto(float(scroll_y+1)/img_height)
img_width and img_height are the dimensions of the image. In other words, they are the full scrollable area.
scroll_x and scroll_y are the coordinates of the desired top-left corner.
+1 is a magic value to make it work precisely (but should be applied only if scroll_x/y is non-negative)
Note that the current widget dimension is not needed, only the dimension of the contents.
This solution works very well even if the image is smaller than the widget size (and thus the scroll_x/y can be negative).
EDIT: improved version:
offset_x = +1 if scroll_x >= 0 else 0
offset_y = +1 if scroll_y >= 0 else 0
self.canvas.xview_moveto(float(scroll_x + offset_x)/new_width)
self.canvas.yview_moveto(float(scroll_y + offset_y)/new_height)
This is what I have already done:
# Little hack to scroll by 1-pixel increments.
oldincx = self.canvas["xscrollincrement"]
oldincy = self.canvas["yscrollincrement"]
self.canvas["xscrollincrement"] = 1
self.canvas["yscrollincrement"] = 1
self.canvas.xview_moveto(0.0)
self.canvas.yview_moveto(0.0)
self.canvas.xview_scroll(int(scroll_x)+1, UNITS)
self.canvas.yview_scroll(int(scroll_y)+1, UNITS)
self.canvas["xscrollincrement"] = oldincx
self.canvas["yscrollincrement"] = oldincy
But... As you can see... it's very hackish and ugly. To much workaround for something that should be simple. (plus that magic +1 I was required to add, or it would be off-by-one)
Does anyone else have another better and cleaner solution?
In tkinter you can get the width and height of a PhotoImagefile. You can just call it when you use canvas.create_image
imgrender = PhotoImage(file="something.png")
##Other canvas and scrollbar codes here...
canvas.create_image((imgrender.width()/2),(imgrender.height()/2), image=imgrender)
## The top left corner coordinates is (width/2 , height/2)
I found this solution by using self.canvas.scan_dragto(x, y)
Edit: I develop an interface which can scroll, zoom, and rotate an image. Let's extract from my interface the code.
When I want to save the current position of image I use this:
# 1) Save image position
x0canvas = -self.canvas.canvasx(0)
y0canvas = -self.canvas.canvasy(0)
x0, y0 = self.canvas.coords(text)
ximg = x0
yimg = y0
# 2) Restore image position (for example: after a load)
self.text = canvas.create_text(ximg, yimg, anchor='nw', text='')
self.xyscroll(x0canvas, y0canvas)
# Rotate and zoom image
image = Image.open('fileImg.jpg')
..
imageMod = image.resize(new_size)
if rotate != 0:
imageMod = imageMod.rotate(rotate)
imagetk = ImageTk.PhotoImage(imageMod)
imageid = canvas.create_image(canvas.coords(text), anchor='nw', image=imagetk)

Categories

Resources