## The Python Imaging Library.# $Id$## im.show() drivers## History:# 2008-04-06 fl Created## Copyright (c) Secret Labs AB 2008.## See the README file for information on usage and redistribution.#from__future__importannotationsimportabcimportosimportshutilimportsubprocessimportsysfromshleximportquotefromtypingimportAnyfrom.importImage_viewers=[][docs]defregister(viewer:type[Viewer]|Viewer,order:int=1)->None:""" The :py:func:`register` function is used to register additional viewers:: from PIL import ImageShow ImageShow.register(MyViewer()) # MyViewer will be used as a last resort ImageShow.register(MySecondViewer(), 0) # MySecondViewer will be prioritised ImageShow.register(ImageShow.XVViewer(), 0) # XVViewer will be prioritised :param viewer: The viewer to be registered. :param order: Zero or a negative integer to prepend this viewer to the list, a positive integer to append it. """ifisinstance(viewer,type)andissubclass(viewer,Viewer):viewer=viewer()iforder>0:_viewers.append(viewer)else:_viewers.insert(0,viewer) [docs]defshow(image:Image.Image,title:str|None=None,**options:Any)->bool:r""" Display a given image. :param image: An image object. :param title: Optional title. Not all viewers can display the title. :param \**options: Additional viewer options. :returns: ``True`` if a suitable viewer was found, ``False`` otherwise. """forviewerin_viewers:ifviewer.show(image,title=title,**options):returnTruereturnFalse [docs]classViewer:"""Base class for viewers."""# main api[docs]defshow(self,image:Image.Image,**options:Any)->int:""" The main function for displaying an image. Converts the given image to the target format and displays it. """ifnot(image.modein("1","RGBA")or(self.format=="PNG"andimage.modein("I;16","LA"))):base=Image.getmodebase(image.mode)ifimage.mode!=base:image=image.convert(base)returnself.show_image(image,**options) # hook methodsformat:str|None=None"""The format to convert the image into."""options:dict[str,Any]={}"""Additional options used to convert the image."""[docs]defget_format(self,image:Image.Image)->str|None:"""Return format name, or ``None`` to save as PGM/PPM."""returnself.format [docs]defget_command(self,file:str,**options:Any)->str:""" Returns the command used to display the file. Not implemented in the base class. """msg="unavailable in base viewer"raiseNotImplementedError(msg) [docs]defsave_image(self,image:Image.Image)->str:"""Save to temporary file and return filename."""returnimage._dump(format=self.get_format(image),**self.options) [docs]defshow_image(self,image:Image.Image,**options:Any)->int:"""Display the given image."""returnself.show_file(self.save_image(image),**options) [docs]defshow_file(self,path:str,**options:Any)->int:""" Display given file. """ifnotos.path.exists(path):raiseFileNotFoundErroros.system(self.get_command(path,**options))# nosecreturn1 # --------------------------------------------------------------------[docs]classWindowsViewer(Viewer):"""The default viewer on Windows is the default system application for PNG files."""format="PNG"options={"compress_level":1,"save_all":True}defget_command(self,file:str,**options:Any)->str:return(f'start "Pillow" /WAIT "{file}" '"&& ping -n 4 127.0.0.1 >NUL "f'&& del /f "{file}"')defshow_file(self,path:str,**options:Any)->int:""" Display given file. """ifnotos.path.exists(path):raiseFileNotFoundErrorsubprocess.Popen(self.get_command(path,**options),shell=True,creationflags=getattr(subprocess,"CREATE_NO_WINDOW"),)# nosecreturn1 ifsys.platform=="win32":register(WindowsViewer)[docs]classMacViewer(Viewer):"""The default viewer on macOS using ``Preview.app``."""format="PNG"options={"compress_level":1,"save_all":True}defget_command(self,file:str,**options:Any)->str:# on darwin open returns immediately resulting in the temp# file removal while app is openingcommand="open -a Preview.app"command=f"({command}{quote(file)}; sleep 20; rm -f{quote(file)})&"returncommanddefshow_file(self,path:str,**options:Any)->int:""" Display given file. """ifnotos.path.exists(path):raiseFileNotFoundErrorsubprocess.call(["open","-a","Preview.app",path])pyinstaller=getattr(sys,"frozen",False)andhasattr(sys,"_MEIPASS")executable=(notpyinstallerandsys.executable)orshutil.which("python3")ifexecutable:subprocess.Popen([executable,"-c","import os, sys, time; time.sleep(20); os.remove(sys.argv[1])",path,])return1 ifsys.platform=="darwin":register(MacViewer)[docs]classUnixViewer(abc.ABC,Viewer):format="PNG"options={"compress_level":1,"save_all":True}@abc.abstractmethoddefget_command_ex(self,file:str,**options:Any)->tuple[str,str]:passdefget_command(self,file:str,**options:Any)->str:command=self.get_command_ex(file,**options)[0]returnf"{command}{quote(file)}" classXDGViewer(UnixViewer):""" The freedesktop.org ``xdg-open`` command. """defget_command_ex(self,file:str,**options:Any)->tuple[str,str]:command=executable="xdg-open"returncommand,executabledefshow_file(self,path:str,**options:Any)->int:""" Display given file. """ifnotos.path.exists(path):raiseFileNotFoundErrorsubprocess.Popen(["xdg-open",path])return1classDisplayViewer(UnixViewer):""" The ImageMagick ``display`` command. This viewer supports the ``title`` parameter. """defget_command_ex(self,file:str,title:str|None=None,**options:Any)->tuple[str,str]:command=executable="display"iftitle:command+=f" -title{quote(title)}"returncommand,executabledefshow_file(self,path:str,**options:Any)->int:""" Display given file. """ifnotos.path.exists(path):raiseFileNotFoundErrorargs=["display"]title=options.get("title")iftitle:args+=["-title",title]args.append(path)subprocess.Popen(args)return1classGmDisplayViewer(UnixViewer):"""The GraphicsMagick ``gm display`` command."""defget_command_ex(self,file:str,**options:Any)->tuple[str,str]:executable="gm"command="gm display"returncommand,executabledefshow_file(self,path:str,**options:Any)->int:""" Display given file. """ifnotos.path.exists(path):raiseFileNotFoundErrorsubprocess.Popen(["gm","display",path])return1classEogViewer(UnixViewer):"""The GNOME Image Viewer ``eog`` command."""defget_command_ex(self,file:str,**options:Any)->tuple[str,str]:executable="eog"command="eog -n"returncommand,executabledefshow_file(self,path:str,**options:Any)->int:""" Display given file. """ifnotos.path.exists(path):raiseFileNotFoundErrorsubprocess.Popen(["eog","-n",path])return1classXVViewer(UnixViewer):""" The X Viewer ``xv`` command. This viewer supports the ``title`` parameter. """defget_command_ex(self,file:str,title:str|None=None,**options:Any)->tuple[str,str]:# note: xv is pretty outdated. most modern systems have# imagemagick's display command instead.command=executable="xv"iftitle:command+=f" -name{quote(title)}"returncommand,executabledefshow_file(self,path:str,**options:Any)->int:""" Display given file. """ifnotos.path.exists(path):raiseFileNotFoundErrorargs=["xv"]title=options.get("title")iftitle:args+=["-name",title]args.append(path)subprocess.Popen(args)return1ifsys.platformnotin("win32","darwin"):# unixoidsifshutil.which("xdg-open"):register(XDGViewer)ifshutil.which("display"):register(DisplayViewer)ifshutil.which("gm"):register(GmDisplayViewer)ifshutil.which("eog"):register(EogViewer)ifshutil.which("xv"):register(XVViewer)[docs]classIPythonViewer(Viewer):"""The viewer for IPython frontends."""defshow_image(self,image:Image.Image,**options:Any)->int:ipython_display(image)return1 try:fromIPython.displayimportdisplayasipython_displayexceptImportError:passelse:register(IPythonViewer)if__name__=="__main__":iflen(sys.argv)<2:print("Syntax: python3 ImageShow.py imagefile [title]")sys.exit()withImage.open(sys.argv[1])asim:print(show(im,*sys.argv[2:]))