open-cogsci / OpenSesame

Graphical experiment builder for the social sciences
http://osdoc.cogsci.nl/
GNU General Public License v3.0
238 stars 112 forks source link

Ability to save screenshot of canvas in inline script #318

Open dschreij opened 9 years ago

dschreij commented 9 years ago

(Feature request)

Several times in the past I have found it necessary to save the contents of a canvas to an image file (i.e. make a screenshot). I wrote a function for this, that I copy paste each time, but I think it might be of added value to integrate this function in the OpenSesame canvas object, especially since all backends offer this functionality natively and it is just a matter of passing a few commands (and do some checks if saving the file is possible of course). Here is the code that does the saving:

def save_as_file(canvas, filename, buffer='front'):
    """ Save the contents of the current screenbuffer to an image file.

    Parameters
    ----------
    canvas : openexp.Canvas
        The canvas object to save the image from

    filename : string
        The location and filenam to save the image to. If an absolute path is 
        provided, the file will be saved there. If a relative path is provided, 
        the file will be saved to that folder relative to the location of the 
        experiment file

    buffer : string, default 'front'
        Only relevant when using the psychopy backend. Indicates whether the 
        front buffer or back buffer should be saved, so this value should be 
        'front' or 'back'

    Returns
    -------
    string : The full path to the file that has just been saved

    Raises
    ------
    libopensesame.exceptions.osexception if an incorrect file format or buffer 
    value have been provided    
    """
    import os
    from libopensesame.exceptions import osexception

    # Check if supplied filename has a valid image extension
    if not os.path.splitext(filename)[1] in ['.jpg', '.jpeg', '.png', '.bmp', '.tga']:
        raise osexception("The filename does not have a valid image extension. Use .jpg, .jpeg, .png, .bmp or .tga")

    if not buffer in ['front','back']:
        raise osexception("Invalid buffer value; the only options are 'front' or 'back'.")

    # Check if image file path is absolute, if not, find relative path to current experiment file
    if not os.path.isabs(filename):
        filename = os.path.abspath(os.path.join(self.experiment.experiment_path, filename))

    # Create any subfolders if necessary
    file_dir = os.path.dirname(filename)
    if not os.path.isdir(file_dir):
        os.makedirs(file_dir)

    # Backend specific saving of canvas to image file
    if self.experiment.get("canvas_backend") == "legacy":
        import pygame
        pygame.image.save(canvas.surface, filename) 
    elif self.experiment.get("canvas_backend") == "xpyriment":  
        canvas._canvas.save(filename)
    elif self.experiment.get("canvas_backend") == "psycho":
        win.getMovieFrame(buffer)
        win.saveMovieFrames(filename)

    # Return the abspath of the saved file (to easily reference it later)
    return filename

My idea would be that this function is contained in the canvas object (so this does not need to be passed as the first parameter). To call this function, one could simply do:

canvas = self.offline_canvas()
canvas.text("Dit wordt een gave screenshot")
canvas.show()
canvas.save_as_file("screenshot.png")

What do you think? (maybe you can think of a more appropriate name than 'save_af_file')

smathot commented 9 years ago

Hi Daniel,

Yes, that seems like a useful addition. As you say, it should be included in the canvas back-end, perhaps as a screenshot() or screen_capture() method. To me, save_as_file() is a bit ambiguous, because it could also mean that you're pickling the object.

If you want to add this, please use the ising branch, which is where all the new functionality should go. Or, if this is still too unstable to be useful, you can work from heisenberg, but I will still merge it into ising.

You know what the routine is for modifying the backends?

Cheers! Sebastiaan

dschreij commented 9 years ago

Ok, thanks for the explanation. I'll see what I can do in the near future!

dschreij commented 5 years ago

I needed this function again recently and came across this post, which I had completely forgot. Is this still relevant enough to implement in the OS Canvases themselves?? Has anything changed regarding your instructions on how to integrate this with the OpenSesame code base?

mariansauter commented 3 years ago

Has this been implemented? It does not show up in the doc.

smathot commented 3 years ago

@mariansauter This hasn't been implemented yet. There's still a PR open (#658 ) but it has gone off the radar. I don't think @dschreij has time for this currently, but if someone (you?) feels like picking this up and properly testing it against the loewenfeld branch then perhaps we can still merge it?