Saving screenshot of a widget

1,889 views
Skip to first unread message

Jason Kuen

unread,
Dec 7, 2012, 1:11:57 AM12/7/12
to kivy-...@googlegroups.com
Is there any method for me to save screenshots of a widget instead of the whole window?

I did it in this way to make the screenshot saving button hidden/transparent before saving the screenshot but the button still appears on the screenshot. How can I do it without including the button on the screenshot? Thanks.

class MyPaintApp(App):
parent = Widget() 
ssbtn = Button(text='Save')
parent.add_widget(ssbtn)
def print_screen(): 
ssbtn.opacity = 0
Window.screenshot(name='screenshot.jpg')
ssbtn.bind(on_release=print_screen) 

 

Mathieu Virbel

unread,
Dec 7, 2012, 5:44:59 AM12/7/12
to kivy-...@googlegroups.com
Hi,

Not really easy, but not that hard too.
The first thing is to render your widget on a FBO, and then, extract the
pixels and save it to a file.

It would look like this: (not tested)

import pygame
from kivy.graphics.fbo import Fbo
from kivy.core.gl import glReadPixels, GL_RGBA, GL_UNSIGNED_BYTE
from kivy.graphics.texture import Texture

def widgetshot(widget, filename='output.png'):
# detach the widget from the parent
parent = widget.parent
if parent:
parent.remove_widget(widget)

# put the widget canvas on a Fbo
texture = Texture.create(size=widget.size, colorfmt='rgb')
fbo = Fbo(size=widget.size, texture=texture)
fbo.add(widget.canvas)

# clear the fbo background
fbo.bind()
fbo.clear_buffer()
fbo.release()

# draw!
fbo.draw()

# get the fbo data
fbo.bind()
data = glReadPixels(0, 0, size[0], size[1], GL_RGBA, GL_UNSIGNED_BYTE)
fbo.release()

# save to a file
surf = pygame.image.fromstring(data, widget.size, 'RGBA', True)
pygame.image.save(surf, filename)

# reattach to the parent
if parent:
parent.add_widget(widget)

return True



Le 07/12/2012 07:11, Jason Kuen a �crit :
> --
>
>

Thomas Hansen

unread,
Dec 7, 2012, 9:03:40 AM12/7/12
to kivy-...@googlegroups.com
If you don't mind the screenshot being of the  window, you can make the button transparent. It not working in your code because you take the screenshot right after settin opacity = 0; at this point the screen has not been redrawn. You could instead schedule a function using Clock.schedule_once which will make the call to take the screenshot. 
--


Sam Brotherton

unread,
Dec 7, 2012, 1:11:05 PM12/7/12
to kivy-...@googlegroups.com
You can see an example of this technique being used at https://github.com/chaosbuffalolabs/CBLParticleSystem/blob/master/main.py, line 246


--
 
 

Jussi Salmela

unread,
Nov 10, 2013, 7:35:21 AM11/10/13
to kivy-...@googlegroups.com
Wanting green, getting red, feeling blue.

Hi Mathieu!

Sorry for the word play, I just couldn't resist.

My environment: Mac OS X Mavericks & Kivy v1.8.0-dev

I'm trying to create a modest chart program and have the need to save the chart. Luckily I found your example and started to play with it. The code needed the line:
         data = glReadPixels(0, 0, size[0], size[1], GL_RGBA, GL_UNSIGNED_BYTE)
to be changed to:
         data = glReadPixels(0, 0, widget.size[0], widget.size[1], GL_RGBA, GL_UNSIGNED_BYTE)
after which it works, sort of.

The trouble is that the colour green on display gets saved as red in the .png and red is saved as green. 

Here's my code:
=========================================================================================
# File name: mycharts.py
import random

import kivy
kivy.require('1.7.0')
from kivy.app import App
from kivy.uix.anchorlayout import AnchorLayout
from kivy.clock import Clock
from kivy.properties import StringProperty
from kivy.graphics import Color, Ellipse
from kivy.uix.image import Image

#from widgetshot import widgetshot

# This is Mathieu Virbel's answer to a question on kivy-users forum
# about how to save a widget's canvas contents as a .png file
import pygame 
from kivy.graphics.fbo import Fbo 
from kivy.core.gl import glReadPixels, GL_RGBA, GL_UNSIGNED_BYTE 
from kivy.graphics.texture import Texture 

def widgetshot(widget, filename='output.png'): 
# detach the widget from the parent 
parent = widget.parent 
if parent:
parent.remove_widget(widget) 

# put the widget canvas on a Fbo 
texture = Texture.create(size=widget.size, colorfmt='rgb') 
fbo = Fbo(size=widget.size, texture=texture) 
fbo.add(widget.canvas) 

# clear the fbo background 
fbo.bind() 
fbo.clear_buffer() 
fbo.release() 

# draw! 
fbo.draw() 

# get the fbo data 
fbo.bind() 
data = glReadPixels(0, 0, widget.size[0], widget.size[1], GL_RGBA, GL_UNSIGNED_BYTE) 
fbo.release() 

# save to a file 
surf = pygame.image.fromstring(data, widget.size, 'RGBA', True) 
pygame.image.save(surf, filename) 

# reattach to the parent 
if parent:
parent.add_widget(widget) 

return True 


from juts_utils import *

#juts_deb()

Builder.load_string('''
<MyCharts>:
#timer_label: time_label.text
BoxLayout:
id: box1
#rows: 1
#cols: 2
orientation: 'horizontal'
AnchorLayout:
width: 100
size_hint: None,1
anchor_x: 'left'
anchor_y: 'top'
GridLayout:
id: gr2
rows: 3
cols: 1
#padding: (self.width - self.cols*draw.width)/2, (self.height - self.rows*draw.height)/2
Button:
id: draw
text: 'Draw'
size_hint: None,None
on_press: root.draw()
Button:
text: 'Save'
size_hint: None,None
on_press: root.save()
Button:
text: 'Load'
size_hint: None,None
on_press: root.load()
Label:
#width: int(0.9*root.width)
#size_hint: 0.9,None
id: chart
''')

class MyCharts(AnchorLayout):

def draw(self):
with self.canvas:
d = random.randrange(1., 100.)
#Color(random.randrange(0,2), random.randrange(0,2), random.randrange(0,2))
Color(0,1,0)
Ellipse(pos=(random.randrange(0,self.width), random.randrange(0,self.height)), size=(d, d))
def save(self):
widgetshot(self, filename = juts_gsd() + 'mycharts.png')
#self.ids.score_all.text = '%d/%d' % (self.succ_count,self.try_count)

def load(self):
with self.canvas:
Image(source='mycharts.png')
class MyChartsApp(App):
    def build(self):
        return MyCharts()

if __name__=="__main__":
    MyChartsApp().run()
=========================================================================================

I've done some (almost) random changes and testing but my knowledge level apparently is far too low, so I'm stuck.
If I can do anything to help you to get into the bottom of this, please ask.

Jussi
Le 07/12/2012 07:11, Jason Kuen a �crit :

Jussi Salmela

unread,
Nov 11, 2013, 6:05:52 PM11/11/13
to kivy-...@googlegroups.com
There's apparently a bug in how pygame stores the .png format. If I change the format to .jpg the colours are saved correctly.

The correct colours for .png can be created by adding the following code:
data2 = list(data)
for i in range(len(data)/4):
data2[4*i+1], data2[4*i] = data2[4*i], data2[4*i+1]
data = ''.join(data2)
between the call to glReadPixels and the call to pygame.image.fromstring.

Jussi

Alexander Taylor

unread,
Apr 22, 2014, 11:33:33 AM4/22/14
to kivy-...@googlegroups.com
I've seen a couple of people refer to this thread recently, so just in case anyone doesn't know, kivy now (in the dev version at time of writing) has a built in method to save a picture of a widget and its children. This is Widget.export_to_png.
Reply all
Reply to author
Forward
0 new messages