ImageShow.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. #
  2. # The Python Imaging Library.
  3. # $Id$
  4. #
  5. # im.show() drivers
  6. #
  7. # History:
  8. # 2008-04-06 fl Created
  9. #
  10. # Copyright (c) Secret Labs AB 2008.
  11. #
  12. # See the README file for information on usage and redistribution.
  13. #
  14. import os
  15. import shutil
  16. import subprocess
  17. import sys
  18. import tempfile
  19. from shlex import quote
  20. from PIL import Image
  21. _viewers = []
  22. def register(viewer, order=1):
  23. """
  24. The :py:func:`register` function is used to register additional viewers.
  25. :param viewer: The viewer to be registered.
  26. :param order:
  27. Zero or a negative integer to prepend this viewer to the list,
  28. a positive integer to append it.
  29. """
  30. try:
  31. if issubclass(viewer, Viewer):
  32. viewer = viewer()
  33. except TypeError:
  34. pass # raised if viewer wasn't a class
  35. if order > 0:
  36. _viewers.append(viewer)
  37. else:
  38. _viewers.insert(0, viewer)
  39. def show(image, title=None, **options):
  40. r"""
  41. Display a given image.
  42. :param image: An image object.
  43. :param title: Optional title. Not all viewers can display the title.
  44. :param \**options: Additional viewer options.
  45. :returns: ``True`` if a suitable viewer was found, ``False`` otherwise.
  46. """
  47. for viewer in _viewers:
  48. if viewer.show(image, title=title, **options):
  49. return 1
  50. return 0
  51. class Viewer:
  52. """Base class for viewers."""
  53. # main api
  54. def show(self, image, **options):
  55. """
  56. The main function for displaying an image.
  57. Converts the given image to the target format and displays it.
  58. """
  59. if not (
  60. image.mode in ("1", "RGBA")
  61. or (self.format == "PNG" and image.mode in ("I;16", "LA"))
  62. ):
  63. base = Image.getmodebase(image.mode)
  64. if image.mode != base:
  65. image = image.convert(base)
  66. return self.show_image(image, **options)
  67. # hook methods
  68. format = None
  69. """The format to convert the image into."""
  70. options = {}
  71. """Additional options used to convert the image."""
  72. def get_format(self, image):
  73. """Return format name, or ``None`` to save as PGM/PPM."""
  74. return self.format
  75. def get_command(self, file, **options):
  76. """
  77. Returns the command used to display the file.
  78. Not implemented in the base class.
  79. """
  80. raise NotImplementedError
  81. def save_image(self, image):
  82. """Save to temporary file and return filename."""
  83. return image._dump(format=self.get_format(image), **self.options)
  84. def show_image(self, image, **options):
  85. """Display the given image."""
  86. return self.show_file(self.save_image(image), **options)
  87. def show_file(self, file, **options):
  88. """Display the given file."""
  89. os.system(self.get_command(file, **options))
  90. return 1
  91. # --------------------------------------------------------------------
  92. class WindowsViewer(Viewer):
  93. """The default viewer on Windows is the default system application for PNG files."""
  94. format = "PNG"
  95. options = {"compress_level": 1}
  96. def get_command(self, file, **options):
  97. return (
  98. f'start "Pillow" /WAIT "{file}" '
  99. "&& ping -n 2 127.0.0.1 >NUL "
  100. f'&& del /f "{file}"'
  101. )
  102. if sys.platform == "win32":
  103. register(WindowsViewer)
  104. class MacViewer(Viewer):
  105. """The default viewer on MacOS using ``Preview.app``."""
  106. format = "PNG"
  107. options = {"compress_level": 1}
  108. def get_command(self, file, **options):
  109. # on darwin open returns immediately resulting in the temp
  110. # file removal while app is opening
  111. command = "open -a Preview.app"
  112. command = f"({command} {quote(file)}; sleep 20; rm -f {quote(file)})&"
  113. return command
  114. def show_file(self, file, **options):
  115. """Display given file"""
  116. fd, path = tempfile.mkstemp()
  117. with os.fdopen(fd, "w") as f:
  118. f.write(file)
  119. with open(path) as f:
  120. subprocess.Popen(
  121. ["im=$(cat); open -a Preview.app $im; sleep 20; rm -f $im"],
  122. shell=True,
  123. stdin=f,
  124. )
  125. os.remove(path)
  126. return 1
  127. if sys.platform == "darwin":
  128. register(MacViewer)
  129. class UnixViewer(Viewer):
  130. format = "PNG"
  131. options = {"compress_level": 1}
  132. def get_command(self, file, **options):
  133. command = self.get_command_ex(file, **options)[0]
  134. return f"({command} {quote(file)}; rm -f {quote(file)})&"
  135. def show_file(self, file, **options):
  136. """Display given file"""
  137. fd, path = tempfile.mkstemp()
  138. with os.fdopen(fd, "w") as f:
  139. f.write(file)
  140. with open(path) as f:
  141. command = self.get_command_ex(file, **options)[0]
  142. subprocess.Popen(
  143. ["im=$(cat);" + command + " $im; rm -f $im"], shell=True, stdin=f
  144. )
  145. os.remove(path)
  146. return 1
  147. class DisplayViewer(UnixViewer):
  148. """The ImageMagick ``display`` command."""
  149. def get_command_ex(self, file, **options):
  150. command = executable = "display"
  151. return command, executable
  152. class GmDisplayViewer(UnixViewer):
  153. """The GraphicsMagick ``gm display`` command."""
  154. def get_command_ex(self, file, **options):
  155. executable = "gm"
  156. command = "gm display"
  157. return command, executable
  158. class EogViewer(UnixViewer):
  159. """The GNOME Image Viewer ``eog`` command."""
  160. def get_command_ex(self, file, **options):
  161. executable = "eog"
  162. command = "eog -n"
  163. return command, executable
  164. class XVViewer(UnixViewer):
  165. """
  166. The X Viewer ``xv`` command.
  167. This viewer supports the ``title`` parameter.
  168. """
  169. def get_command_ex(self, file, title=None, **options):
  170. # note: xv is pretty outdated. most modern systems have
  171. # imagemagick's display command instead.
  172. command = executable = "xv"
  173. if title:
  174. command += f" -name {quote(title)}"
  175. return command, executable
  176. if sys.platform not in ("win32", "darwin"): # unixoids
  177. if shutil.which("display"):
  178. register(DisplayViewer)
  179. if shutil.which("gm"):
  180. register(GmDisplayViewer)
  181. if shutil.which("eog"):
  182. register(EogViewer)
  183. if shutil.which("xv"):
  184. register(XVViewer)
  185. class IPythonViewer(Viewer):
  186. """The viewer for IPython frontends."""
  187. def show_image(self, image, **options):
  188. ipython_display(image)
  189. return 1
  190. try:
  191. from IPython.display import display as ipython_display
  192. except ImportError:
  193. pass
  194. else:
  195. register(IPythonViewer)
  196. if __name__ == "__main__":
  197. if len(sys.argv) < 2:
  198. print("Syntax: python3 ImageShow.py imagefile [title]")
  199. sys.exit()
  200. with Image.open(sys.argv[1]) as im:
  201. print(show(im, *sys.argv[2:]))