Cairo shows just last drawing? - python

I create a widget that plots data and allows users a selections:
https://gobblin.se/u/kellogs/m/bildschirmfoto-vom-2013-11-12-13-23-54/
Sadly, in this screenshot should appear multiple selections (here red), but rendering n of this models doesn't work for some reason. I made sure, that the data is available and the rendering is called and works fine (right position and dimensions)
So my widget just creates a cairo surface that is used in a iteration of the following method to render the selections on top of the plotted data line:
def render_to(self, cairosurface):
'''external trigger to redraw widget on channel widget surface (only within plotarea!)'''
cr = cairosurface
pos_x=self.__getXPos()
w = self.__getWidth()
h = self.__getHeight()
eogclass=self.eogclassification.eogclass
#background
r,g,b=eogclass.color
alpha=0.9
color=(r,g,b,alpha)
cr.set_source_rgba(*color)
cr.rectangle(pos_x, 0, w, h)
cr.fill()
#label
label=eogclass.name
cr.set_source_rgb(*eogclass.text_color)
cr.set_font_size(13)
(x, y, width, height, dx, dy) = cr.text_extents(label)
cr.move_to(pos_x+w/2 - width/2, h/2) #center within our block
cr.text_path(label)
cr.clip()
cr.stroke()
cr.paint()
Can anybody give me a tip what might be the problem?
I'm not sure, but can this be a problem with compositing?

Related

Image won't clear when saved from Tkinter canvas

I'm making a handwritten digit interpreter with Tkinter using a model I made. I save the canvas image, then read it back through my model to get the prediction. I clear the canvas with cv.delete("all") then I draw another digit to predict and I get a wonky prediction.
Initial Outcome: (3 is 3 :)!!!)
Then I clear it and write another number:
And I go to my folder where the image saves and the picture looks like this:
Here's my code to define my canvas and the image I'm going to draw on.
# define the canvas and image to save
lastx, lasty = None, None
cv = Canvas(root, width=420, height=420, bg='black')
image1 = PIL.Image.new("L",(420,420),"black")
draw = ImageDraw.Draw(image1)
cv.bind('<1>', activate_paint)
cv.pack(expand=YES, fill=BOTH)
Here's the code I used to paint:
def activate_paint(e):
global lastx, lasty
cv.bind('<B1-Motion>', paint)
lastx, lasty = e.x, e.y
def paint(e):
global lastx, lasty
x, y = e.x, e.y
cv.create_line((lastx, lasty, x, y), fill = 'white',width=30)
# --- PIL
draw.line((lastx, lasty, x, y), fill='white', width=30)
lastx, lasty = x, y
And here's the code I use to save it when I click the predict button:
filename = f'img_to_predict.png'
image1.save(filename)
I just need the clear button to make the image truly blank so it doesn't save over the previous image. Can anyone push me in the right direction?
Thank you!
Jackson
You need to clear the image as well by calling
draw.rectangle((0, 0, 420, 420), fill="black")
However, I would propose to remove drawing on the image. Just drawing on the canvas, and then take a snapshot on the canvas using ImageGrab.grab() (from Pillow module as well) and save the snapshot to file when you need to do the prediction:
# get the canvas bounding box on screen
x, y = cv.winfo_rootx(), cv.winfo_rooty()
w, h = cv.winfo_width(), cv.winfo_height()
# take a snapshot on the canvas and save the image to file
ImageGrab.grab((x, y, x+w, y+h)).save('img_to_predict.png')
UPDATE: This doesn't seem to matter at all, so please ignore it
Assuming you copy pasted the line cv.delete("all"), the issue might be using "all" as a string. You might need cv.delete(ALL) to clear it away?

Pygame touble when drawing a rectangle

So, I'm randomly generating a "world" and drawing it with pygame. That part worked perfectly fine until I decided to add something above what I already drew.
The code is as follows. What each thing is is of no consequence, but DISPLAY is the surface I'm working on, y.colour is a size 3 Tuple, y.coord is a (x,y) Tuple
for x in W_Map:
for y in x:
DISPLAY.fill(y.colour, pygame.Rect(y.coord[0]-tile_size,
y.coord[1]-tile_size,
y.coord[0]+tile_size,
y.coord[1]+tile_size))
DISPLAY.fill(lime, pygame.Rect(300,300,310,310))
According to the game above, this should create a lime coloured 10x10 square centered on 305x305. The result, however, is the following picture:
As you can see, the first part of the code draws the terrain perfectly, but when creating the lime square on top of what's already drawn, it goes completely crazy. The whole function is:
pygame.init()
DISPLAY = pygame.display.set_mode(
(shape[0]*2*tile_size, shape[1]*2*tile_size))
DISPLAY.fill((0,0,0))
#Make and draw the Rects
for x in W_Map:
for y in x:
DISPLAY.fill(y.colour, pygame.Rect(y.coord[0]-tile_size,
y.coord[1]-tile_size,
y.coord[0]+tile_size,
y.coord[1]+tile_size))
DISPLAY.fill(lime, pygame.Rect(300,300,310,310))
Pygame's Rect takes four arguments: x, y, width, and height, where x and y are relative to the top left of the viewport. Your lime rectangle is created with pygame.Rect(300,300,310,310), meaning a width and height of 310 pixels and a location of (300, 300).
To create a 10x10 rectangle centered at (305, 305) you'll need to use pygame.Rect(300, 300, 10, 10). You can also create a helper function to translate size and center point to the necessary rectangle parameters:
def center_rect(x, y, width, height):
return pygame.Rect(x - width / 2, y - height / 2, width, height)
Then you could use this helper function like so:
DISPLAY.fill(lime, center_rect(305, 305, 10, 10))

Drawing from one surface to another surface

import cairo
import math
w = 2000
h = 2000
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h)
ctx = cairo.Context(surface)
ctx.scale(w, h)
surface_path = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h)
ctx_path = cairo.Context(surface_path)
ctx_path.scale(w, h)
surface_circle = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h)
ctx_circle = cairo.Context(surface_circle)
ctx_circle.scale(w, h)
""" Lots of function calls that draw paths to surface_path and circles to surface_circle """
ctx.set_source_surface(surface_path, 0, 0)
ctx.paint()
ctx.set_source_surface(surface_circle, 0, 0)
ctx.paint()
surface_path.write_to_png("example.png")
surface_circle.write_to_png("example2.png")
surface.write_to_png("result.png")
Imgur link to saved images
I am attempting to compile two surfaces (one with lines, one with circles) onto one separate surface, then save that to a file.
Despite following what the documentation suggests should work, the final image ends up blank. I also tried calling flush() on surface_path and surface_circle, but that seemed to do nothing.
How could I combine the image info in surface_circle (example2.png) on top of surface_path (example.png), then output it to a file?
Try calling ctx.identity_matrix() before your last few paint() calls.
As is right-now, thanks to your call to ctx.scale(w, h), you only get the top-left pixels of those other surfaces scaled up to fill all of the target surface.

Pyglet: How to change resolution when you go fullscreen?

I'm using Pyglet and I have a little that includes an object moving over a background. Both of them are represented by images (png and jpg).
I've created a non-fullscreen window with size 800x600 and it works fine, but when I toggle to fullscreen... background and object have the same size as before and the rest of the screen is filled with black (empty color).
What I want to do is to "scale" the images or change the resolution when I toggle fullscreen mode.
I've read the documentation, but I can't find the answer to this.
I know that with Pygame, this problem solves itself automatically (if you change the window size, everything rescales automatically)... but how do you do this with pyglet?
This is my relevant code:
import pyglet
WIDTH = 800
HEIGHT = 600
working_dir = '/where/i/have/my/images/'
window = pyglet.window.Window(WIDTH, HEIGHT)
background = pyglet.image.load(working_dir + 'background.jpg')
flying_thing = pyglet.image.load(working_dir + 'flying_thing.png')
#window.event
def on_draw():
window.clear()
background.blit(0, 0)
flying_thing.blit(WIDTH // 2, HEIGHT // 2)
#window.event
def on_key_press(symbol, modifiers):
if symbol == pyglet.window.key.SPACE:
window.set_fullscreen(not window.fullscreen)
pyglet.app.run()
You can try this code changing working_dir, background.jpg and flying_thing.png to a working directory of yours and two images in it.
I didn't tried, but from pyglet docs, blit supports width and height. Its signature is
blit(self, x, y, z=0, width=None, height=None)
Have you tried using
background.blit(width=window.width, height=windows.height)
instead? (I'm not sure the window.width changes on full_screen, let's see...).
This answer can also be relevant to your question: https://stackoverflow.com/a/11183462/931303.

coordinates changed when migrating from pygame+rabbyt to pyglet+rabbyt

I'm working on a 2D game and decided to switch from SDL to OpenGL. I took rabbyt as an opengl wrapper for rendering my sprites and using pymunk (chipmunk) for my physics. I used pygame for creating the window and rabbyt for drawing the sprites on the screen.
I discovered that with pygame+rabbyt the (0,0) coordinate is in the middle of the screen. I liked that fact, because the coordinate representation in the physics engine were the same as in my graphics engine (I don't have to recalculate the coordinates when rendering the sprites).
Then I switched to pyglet because I wanted to draw lines with OpenGL - and discovered that suddenly the (0,0) coordinate was at the bottom left of the screen.
I suspected that that has something to do with the glViewport function, but only rabbyt executes that function, pyglet touches it only when the window is resized.
How can I set the (0,0) coordinate at the middle of the Screen?
I'm not very familiar with OpenGL and couldn't find anything after several hours googling and trial&error... I hope someone can help me :)
Edit: Some additional information :)
This is my pyglet screen initialization code:
self.window = Window(width=800, height=600)
rabbyt.set_viewport((800,600))
rabbyt.set_default_attribs()
This is my pygame screen initialization code:
display = pygame.display.set_mode((800,600), \
pygame.OPENGL | pygame.DOUBLEBUF)
rabbyt.set_viewport((800, 600))
rabbyt.set_default_attribs()
Edit 2: I looked at the sources of pyglet and pygame and didn't discover anything in the screen initialization code that has something to do with the OpenGL viewport... Here is the source of the two rabbyt functions:
def set_viewport(viewport, projection=None):
"""
``set_viewport(viewport, [projection])``
Sets how coordinates map to the screen.
``viewport`` gives the screen coordinates that will be drawn to. It
should be in either the form ``(width, height)`` or
``(left, top, right, bottom)``
``projection`` gives the sprite coordinates that will be mapped to the
screen coordinates given by ``viewport``. It too should be in one of the
two forms accepted by ``viewport``. If ``projection`` is not given, it
will default to the width and height of ``viewport``. If only the width
and height are given, ``(0, 0)`` will be the center point.
"""
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
if len(viewport) == 4:
l, t, r, b = viewport
else:
l, t = 0, 0
r, b = viewport
for i in (l,t,r,b):
if i < 0:
raise ValueError("Viewport values cannot be negative")
glViewport(l, t, r-l, b-t)
if projection is not None:
if len(projection) == 4:
l, t, r, b = projection
else:
w,h = projection
l, r, t, b = -w/2, w/2, -h/2, h/2
else:
w,h = r-l, b-t
l, r, b, t = -w/2, w/2, -h/2, h/2
glOrtho(l, r, b, t, -1, 1)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
def set_default_attribs():
"""
``set_default_attribs()``
Sets a few of the OpenGL attributes that sprites expect.
Unless you know what you are doing, you should call this at least once
before rendering any sprites. (It is called automatically in
``rabbyt.init_display()``)
"""
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE)
glEnable(GL_BLEND)
#glEnable(GL_POLYGON_SMOOTH)
Thanks,
Steffen
As l33tnerd suggested the origin can be placed at the center with glTranslatef...
I added the following below my screen initialization code:
pyglet.gl.glTranslatef(width/2, height/2, 0)
Thanks!

Categories