Kivy save widget without being drawn results in blank Fbo - kivy

I am trying to render some labels over the top of a canvas texture i have previously saved. When i render the widget to an Fbo without drawing it to the screen first i get a blank output. I believe this is replicated in the snippet below, save_file called from on_start writes the file incorrectly, but if the same logic is called from the button press it saves a file of the widget rendered correctly.
Is there a way to render the widget to the Fbo without rendering to the canvas?
Is there an update or draw command i need to call on the widget?
(notshown.texture seems to be None, and adding notshown.canvas.draw() seems to crash with no stacktrace or reason?)
from kivy.app import App
from kivy.factory import Factory
class TestApp(App):
def build(self, *kwargs):
layout = Factory.BoxLayout()
test1 = Factory.Button(text='test1', size_hint=(None,None), size=(200,200))
test1.bind(on_press=self.save_file1)
layout.add_widget(test1)
test2 = Factory.Button(text='test2', size_hint=(None,None), size=(200,200))
test2.bind(on_press=self.save_file2)
layout.add_widget(test2)
return layout
def save_file1(self, widget, value=False):
fbo = Factory.Fbo(size=widget.size, with_stencilbuffer=True)
fbo.add(widget.canvas)
fbo.draw()
fbo.texture.save('C:/Temp/test1.png', flipped=True)
def save_file2(self, widget, value=False):
notshown = Factory.Button(text='notshown', size_hint=(None,None), size=(200,200))
fbo = Factory.Fbo(size=notshown.size, with_stencilbuffer=True)
fbo.add(notshown.canvas)
fbo.draw()
fbo.texture.save('C:/Temp/test2.png', flipped=True)
TestApp().run()

Per John Anderson's input, the issue was not giving the application enough time to render a frame before attempting to save the texture.
Using Clock.schedule_once to await the next frame means that export_to_png or all other uses of the texture of the widget succeed with little to no fuss.
img = Factory.FinalImage(imagedata, size_hint=(None,None), size=imagedata.size)
Clock.schedule_once( lambda x: img.export_to_png(filename=imagename) )

Related

Moving a tiled surface in a kivy widget

I'm building a simple game in kivy where I move a player on a map and the map moves consequently to keep the player at the center of the screen.
But, the problem is that the map is composed by individual tiles with individual properties. How can I move all the elements at the same time??
I'm in trouble with that and I can't reach a solution.
This is the portion of code:
'''
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Rectangle
from kivy.uix.behaviors import DragBehavior
class MainGame(Widget):
def __init__(self, **kwargs):
super().__init__(**kwargs)
with self.canvas:
Rectangle(source= "stone_tile.png",pos=(220,-300), size=(1000,1000))
Rectangle(source= "grass_tile.png", pos=(0,-400), size=(1000,1000))
class FarIslands(App):
def build(self):
return MainGame()
if __name__ == '__main__':
FarIslands().run()
'''
I need to move the two rectangles at the same time. In the final version there will be hundreds of elements to create a game map.
Maybe, is there a better way, instead using Widget canvas?
Because I need to add other functions to the tiles...

Is there a way to store circle coordinates and move them around in python turtle?

I know it's possible to store polygons in a dictionary since they have definitive coordinates, but is there a way to store the coordinates of a circle into a dictionary and move them around? My program consists of detecting whether a mouse click is within a circle and from there, gets the coordinate of that circle moving it to wherever the user desires with another mouse click (the get.poly function, instead of moving the current circle that I already drew, makes another copy). Below is an incomplete snippet of what I want to do:
def buttonclick(x, y): # detects mouseclick
return pen.goto(x, y)
def check(ctr, pt): # check whether the click is within the circle
if (pt[0] - ctr[0])** 2 + (pt[1] - ctr[1])**2 < 5**2:
return True
if check((0,5), mouseclick coordinates): # if true, move circle to next click
# pen = the circle thats detected
# move circle coordinates to next mouseclick
# break
I tried with the code provided by /u/cdlane as follows and here is what I meant by generating a new copy
pen.goto(0,0)
pen.pd()
pen.begin_poly()
pen.circle(radius)
pen.end_poly()
shape.addcomponent(pen.get_poly(), 'red', 'black')
screen.register_shape('1', shape)
pen = Turtle(shape = '1')
pen.pu()
Function does exactly what I need it to do but using an existing circle instead of generating a new copy.
Rather than fix your implementation, I'm going to suggest a different implementation to achieve what you describe.
The circles themselves will be turtles, and clicking on one will "select" it. Clicking anywhere on the screen after that will move it. However, we don't want turtle.onclick() active at the same time as screen.onclick() since they both will get invoked, causing complications. Below is my two turtle example:
from turtle import Screen, Turtle
from functools import partial
selected = None
def on_screen_click(x, y):
global selected
screen.onclick(None)
selected.goto(x, y)
outline, fill = selected.color()
selected.color(fill, outline)
selected = None
for turtle in screen.turtles():
turtle.onclick(partial(on_turtle_click, turtle))
def on_turtle_click(self, x, y):
global selected
for turtle in screen.turtles():
turtle.onclick(None)
selected = self
outline, fill = selected.color()
selected.color(fill, outline)
# (re)enable screen onclick handler after this function, not during
screen.ontimer(partial(screen.onclick, on_screen_click))
screen = Screen()
turtle_1 = Turtle()
turtle_1.shape('circle')
turtle_1.color('red', 'black')
turtle_1.penup()
turtle_2 = turtle_1.clone()
turtle_2.goto(100, 100)
for turtle in screen.turtles():
turtle.onclick(partial(on_turtle_click, turtle))
screen.mainloop()

Python turtle after drawing , click mouse inside drawing, clear screen and redraw

I am working on using python turtle to draw a circle.when i draw the circle, mouse click inside circle erase, click anywhere in the turtle,redraw it and click again inside again erase. So the process like this :
black screen
1.Click mouse
2.Start drawing circle
3.finish
4.click inside circle
5.clear screen
6.click again anywehre in the screen redraw circle
7.click inside circle clear screen
Thanks
https://i.stack.imgur.com/LR8CH.png
import PositionService
import turtle
t=turtle.Turtle()
ts=tur
tle.Screen()
ts.bgpic("shape_window.png")
t.up()
def get_click(x,y):# get click (x,y)
#if counter == 0:
draw_circle(x,y,"green",80)# draw the circle
print("clicking at ({}, {})".format(x,y))
def draw_circle(x,y,color,rad): # draw the circle
t.goto(x,y)
t.down()
t.circle(80)
t.color("green")
t.up()
t.hideturtle()
#t.home()
def main():
#maybe use global _pen
ts.onclick(get_click) # clicker
#set_position( x,y)?
#is_visible(draw_square)
I think were from the same class. I was also struggling until I realized that they supplied a PositionService.py file. You are supposed to use the functions within that file to help.

Flow of Control Python OpenCv : Why cv2.setMouseCallback isn't inside loop?

I am confused with the flow of control in the following program. The purpose of the code is to draw a rectangle in the live video stream from the webcam.
Working Principal: First click will be initializing coordinates of the starting corner of the rectangle and mark it with a bold circle. Second click will be completing the rectangle.
Now my Question is: Why is cv2.setMouseCallback('Test',draw_rectangle) statement isn't inside the loop?
The code is working perfectly fine but I am unable to understand the flow of control. Please help me out.
import cv2
import os
os.environ["OPENCV_VIDEOIO_PRIORITY_MSMF"] = "0"
#CALLBACK FUNCTION RECTANGLE
def draw_rectangle(event,x,y,flags,param): #Param is the just the additional paramter which u can receive
global pt1, pt2, topLeft_Clicked, botRight_Clicked
if event ==cv2.EVENT_LBUTTONDOWN:
#Reset if rectangle is drawing i.e both var are true
if topLeft_Clicked and botRight_Clicked:
pt1=(0,0)
pt2=(0,0)
topLeft_Clicked=False
botRight_Clicked=False
if topLeft_Clicked == False:
pt1=(x,y)
topLeft_Clicked=True
elif botRight_Clicked == False:
pt2=(x,y)
botRight_Clicked=True
#GLOBAL VARIABLES
pt1=(0,0)
pt2=(0,0)
topLeft_Clicked= False
botRight_Clicked= False
#COnnect to the Callback
cap=cv2.VideoCapture(0)
cv2.namedWindow('Test')
cv2.setMouseCallback('Test',draw_rectangle)
while True:
ret,frame=cap.read()
#Drawing Based on Global Variables
if topLeft_Clicked: # If topleft is true
cv2.circle(frame,center=pt1,radius=5,color=(0,0,255),thickness=-1)
if topLeft_Clicked and botRight_Clicked:
cv2.rectangle(frame,pt1,pt2,(0,0,255),3)
cv2.imshow('Test',frame)
if(cv2.waitKey(1) & 0xFF==ord('q')):
break
cap.release()
cv2.destroyAllWindows()
Callback functions are called on events. Unlike a regular function, you don't need to make a function call every time you want it to run.
The line cv2.setMouseCallback('Test',draw_rectangle) will set the function draw_rectangle as a response to any event received from mouse on the OpenCV window "Test". Once the callback is set, inside your while loop you will catch all your mouse events on your "Test" window.
Callback is the function that is called every time you move the mouse over the display window. It is independent from the flow in main, i.e., it is in a new thread waiting for changes in mouse input.
The reason why you use loop in main is because you want to update the image you display. After calling imshow, you need to call waitKey for impact on rendering.
cv2.setMouseCallback('Test',draw_rectangle) this function is actually an event handler, which is setting the call for draw_rectangle function on every left mouse click. The rest of the code inside the while loop are for all the dynamic operations and at last cv2.imshow is to render that in the image.
One token of gift for better handling of opencv closeWindows, closing only currently used window only:
def showImage(imageName, image):
img = image.copy()
cv2.imshow(imageName, img)
while(1):
pressedKey = cv2.waitKey(0) & 0xFF
if(pressedKey == ord('q')):
cv2.destroyWindow(imageName)
break
else:
cv2.putText(img, "\press q to exit", (10,10), cv2.FONT_HERSHEY_SIMPLEX, 0.45, color=(255,0,0))
cv2.imshow(imageName, img)

Using PdfStamper to add an image with AffineTransform

I am using PdfStamper getOverContent() so I can add an image to the output PDF file using an AffineTransform of Identity type.
PdfContentByte content = stamper.getOverContent(1);
data.image.setAbsolutePosition(desc.X,desc.Y);
content.addImage(data.image,desc.transform);
//content.addImage(data.image);
if I use the commented line without the transform it works perfectly adding the image to the PDF generated but with the AffineTransform (setToIdentity()) it does not show up.
could someone help me out with that ? I intend to use more sophisticated transform but the Identity should work first...
EDIT (copied from the invalid answer)
I removed the call to setAbsolutePosition and used a setToIdentity() as the only transformation and the image is not show... Then added the setToTranslation(X,Y) where X and Y are the same values used in the successful case where I do NOT give the transformation as the second parameter and still it does NOT show the image. Is there an example with the AffineTransform as a parameter to PdfContentByte addImage() call using an AffineTransform as a parameter ? I have bought your book but could not fins any.
I have examined your problem and I am pretty sure that your image gets added. However: you can't see it because the dimension of the image is 1 user unit by 1 user unit.
I've made an example to show you how to fix this problem: AddImageAffineTransform
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
Image image = Image.getInstance(IMG);
AffineTransform at = AffineTransform.getTranslateInstance(36, 300);
at.concatenate(AffineTransform.getScaleInstance(image.getScaledWidth(), image.getScaledHeight()));
PdfContentByte canvas = stamper.getOverContent(1);
canvas.addImage(image, at);
stamper.close();
reader.close();
}
In this example, I start with a translation: 36 user units from the left border and 300 user units from the bottom. If I would add the image using this transformation, I'd add the image at those coordinates, but it would be too small to see with the naked eye.
To make sure the image is visible, I concatenate a scale transformation scaling the image to its width in X direction and to its height in Y direction.

Resources