Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ImageShow.py 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  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. from shlex import quote
  19. from PIL import Image
  20. from ._deprecate import deprecate
  21. _viewers = []
  22. def register(viewer, order=1):
  23. """
  24. The :py:func:`register` function is used to register additional viewers::
  25. from PIL import ImageShow
  26. ImageShow.register(MyViewer()) # MyViewer will be used as a last resort
  27. ImageShow.register(MySecondViewer(), 0) # MySecondViewer will be prioritised
  28. ImageShow.register(ImageShow.XVViewer(), 0) # XVViewer will be prioritised
  29. :param viewer: The viewer to be registered.
  30. :param order:
  31. Zero or a negative integer to prepend this viewer to the list,
  32. a positive integer to append it.
  33. """
  34. try:
  35. if issubclass(viewer, Viewer):
  36. viewer = viewer()
  37. except TypeError:
  38. pass # raised if viewer wasn't a class
  39. if order > 0:
  40. _viewers.append(viewer)
  41. else:
  42. _viewers.insert(0, viewer)
  43. def show(image, title=None, **options):
  44. r"""
  45. Display a given image.
  46. :param image: An image object.
  47. :param title: Optional title. Not all viewers can display the title.
  48. :param \**options: Additional viewer options.
  49. :returns: ``True`` if a suitable viewer was found, ``False`` otherwise.
  50. """
  51. for viewer in _viewers:
  52. if viewer.show(image, title=title, **options):
  53. return True
  54. return False
  55. class Viewer:
  56. """Base class for viewers."""
  57. # main api
  58. def show(self, image, **options):
  59. """
  60. The main function for displaying an image.
  61. Converts the given image to the target format and displays it.
  62. """
  63. if not (
  64. image.mode in ("1", "RGBA")
  65. or (self.format == "PNG" and image.mode in ("I;16", "LA"))
  66. ):
  67. base = Image.getmodebase(image.mode)
  68. if image.mode != base:
  69. image = image.convert(base)
  70. return self.show_image(image, **options)
  71. # hook methods
  72. format = None
  73. """The format to convert the image into."""
  74. options = {}
  75. """Additional options used to convert the image."""
  76. def get_format(self, image):
  77. """Return format name, or ``None`` to save as PGM/PPM."""
  78. return self.format
  79. def get_command(self, file, **options):
  80. """
  81. Returns the command used to display the file.
  82. Not implemented in the base class.
  83. """
  84. raise NotImplementedError
  85. def save_image(self, image):
  86. """Save to temporary file and return filename."""
  87. return image._dump(format=self.get_format(image), **self.options)
  88. def show_image(self, image, **options):
  89. """Display the given image."""
  90. return self.show_file(self.save_image(image), **options)
  91. def show_file(self, path=None, **options):
  92. """
  93. Display given file.
  94. Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated,
  95. and will be removed in Pillow 10.0.0 (2023-07-01). ``path`` should be used
  96. instead.
  97. """
  98. if path is None:
  99. if "file" in options:
  100. deprecate("The 'file' argument", 10, "'path'")
  101. path = options.pop("file")
  102. else:
  103. msg = "Missing required argument: 'path'"
  104. raise TypeError(msg)
  105. os.system(self.get_command(path, **options)) # nosec
  106. return 1
  107. # --------------------------------------------------------------------
  108. class WindowsViewer(Viewer):
  109. """The default viewer on Windows is the default system application for PNG files."""
  110. format = "PNG"
  111. options = {"compress_level": 1, "save_all": True}
  112. def get_command(self, file, **options):
  113. return (
  114. f'start "Pillow" /WAIT "{file}" '
  115. "&& ping -n 4 127.0.0.1 >NUL "
  116. f'&& del /f "{file}"'
  117. )
  118. if sys.platform == "win32":
  119. register(WindowsViewer)
  120. class MacViewer(Viewer):
  121. """The default viewer on macOS using ``Preview.app``."""
  122. format = "PNG"
  123. options = {"compress_level": 1, "save_all": True}
  124. def get_command(self, file, **options):
  125. # on darwin open returns immediately resulting in the temp
  126. # file removal while app is opening
  127. command = "open -a Preview.app"
  128. command = f"({command} {quote(file)}; sleep 20; rm -f {quote(file)})&"
  129. return command
  130. def show_file(self, path=None, **options):
  131. """
  132. Display given file.
  133. Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated,
  134. and will be removed in Pillow 10.0.0 (2023-07-01). ``path`` should be used
  135. instead.
  136. """
  137. if path is None:
  138. if "file" in options:
  139. deprecate("The 'file' argument", 10, "'path'")
  140. path = options.pop("file")
  141. else:
  142. msg = "Missing required argument: 'path'"
  143. raise TypeError(msg)
  144. subprocess.call(["open", "-a", "Preview.app", path])
  145. executable = sys.executable or shutil.which("python3")
  146. if executable:
  147. subprocess.Popen(
  148. [
  149. executable,
  150. "-c",
  151. "import os, sys, time; time.sleep(20); os.remove(sys.argv[1])",
  152. path,
  153. ]
  154. )
  155. return 1
  156. if sys.platform == "darwin":
  157. register(MacViewer)
  158. class UnixViewer(Viewer):
  159. format = "PNG"
  160. options = {"compress_level": 1, "save_all": True}
  161. def get_command(self, file, **options):
  162. command = self.get_command_ex(file, **options)[0]
  163. return f"({command} {quote(file)}"
  164. class XDGViewer(UnixViewer):
  165. """
  166. The freedesktop.org ``xdg-open`` command.
  167. """
  168. def get_command_ex(self, file, **options):
  169. command = executable = "xdg-open"
  170. return command, executable
  171. def show_file(self, path=None, **options):
  172. """
  173. Display given file.
  174. Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated,
  175. and will be removed in Pillow 10.0.0 (2023-07-01). ``path`` should be used
  176. instead.
  177. """
  178. if path is None:
  179. if "file" in options:
  180. deprecate("The 'file' argument", 10, "'path'")
  181. path = options.pop("file")
  182. else:
  183. msg = "Missing required argument: 'path'"
  184. raise TypeError(msg)
  185. subprocess.Popen(["xdg-open", path])
  186. return 1
  187. class DisplayViewer(UnixViewer):
  188. """
  189. The ImageMagick ``display`` command.
  190. This viewer supports the ``title`` parameter.
  191. """
  192. def get_command_ex(self, file, title=None, **options):
  193. command = executable = "display"
  194. if title:
  195. command += f" -title {quote(title)}"
  196. return command, executable
  197. def show_file(self, path=None, **options):
  198. """
  199. Display given file.
  200. Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated,
  201. and ``path`` should be used instead.
  202. """
  203. if path is None:
  204. if "file" in options:
  205. deprecate("The 'file' argument", 10, "'path'")
  206. path = options.pop("file")
  207. else:
  208. msg = "Missing required argument: 'path'"
  209. raise TypeError(msg)
  210. args = ["display"]
  211. title = options.get("title")
  212. if title:
  213. args += ["-title", title]
  214. args.append(path)
  215. subprocess.Popen(args)
  216. return 1
  217. class GmDisplayViewer(UnixViewer):
  218. """The GraphicsMagick ``gm display`` command."""
  219. def get_command_ex(self, file, **options):
  220. executable = "gm"
  221. command = "gm display"
  222. return command, executable
  223. def show_file(self, path=None, **options):
  224. """
  225. Display given file.
  226. Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated,
  227. and ``path`` should be used instead.
  228. """
  229. if path is None:
  230. if "file" in options:
  231. deprecate("The 'file' argument", 10, "'path'")
  232. path = options.pop("file")
  233. else:
  234. msg = "Missing required argument: 'path'"
  235. raise TypeError(msg)
  236. subprocess.Popen(["gm", "display", path])
  237. return 1
  238. class EogViewer(UnixViewer):
  239. """The GNOME Image Viewer ``eog`` command."""
  240. def get_command_ex(self, file, **options):
  241. executable = "eog"
  242. command = "eog -n"
  243. return command, executable
  244. def show_file(self, path=None, **options):
  245. """
  246. Display given file.
  247. Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated,
  248. and ``path`` should be used instead.
  249. """
  250. if path is None:
  251. if "file" in options:
  252. deprecate("The 'file' argument", 10, "'path'")
  253. path = options.pop("file")
  254. else:
  255. msg = "Missing required argument: 'path'"
  256. raise TypeError(msg)
  257. subprocess.Popen(["eog", "-n", path])
  258. return 1
  259. class XVViewer(UnixViewer):
  260. """
  261. The X Viewer ``xv`` command.
  262. This viewer supports the ``title`` parameter.
  263. """
  264. def get_command_ex(self, file, title=None, **options):
  265. # note: xv is pretty outdated. most modern systems have
  266. # imagemagick's display command instead.
  267. command = executable = "xv"
  268. if title:
  269. command += f" -name {quote(title)}"
  270. return command, executable
  271. def show_file(self, path=None, **options):
  272. """
  273. Display given file.
  274. Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated,
  275. and ``path`` should be used instead.
  276. """
  277. if path is None:
  278. if "file" in options:
  279. deprecate("The 'file' argument", 10, "'path'")
  280. path = options.pop("file")
  281. else:
  282. msg = "Missing required argument: 'path'"
  283. raise TypeError(msg)
  284. args = ["xv"]
  285. title = options.get("title")
  286. if title:
  287. args += ["-name", title]
  288. args.append(path)
  289. subprocess.Popen(args)
  290. return 1
  291. if sys.platform not in ("win32", "darwin"): # unixoids
  292. if shutil.which("xdg-open"):
  293. register(XDGViewer)
  294. if shutil.which("display"):
  295. register(DisplayViewer)
  296. if shutil.which("gm"):
  297. register(GmDisplayViewer)
  298. if shutil.which("eog"):
  299. register(EogViewer)
  300. if shutil.which("xv"):
  301. register(XVViewer)
  302. class IPythonViewer(Viewer):
  303. """The viewer for IPython frontends."""
  304. def show_image(self, image, **options):
  305. ipython_display(image)
  306. return 1
  307. try:
  308. from IPython.display import display as ipython_display
  309. except ImportError:
  310. pass
  311. else:
  312. register(IPythonViewer)
  313. if __name__ == "__main__":
  314. if len(sys.argv) < 2:
  315. print("Syntax: python3 ImageShow.py imagefile [title]")
  316. sys.exit()
  317. with Image.open(sys.argv[1]) as im:
  318. print(show(im, *sys.argv[2:]))