2010-07-31 06:52:47 +04:00
|
|
|
#
|
|
|
|
# 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.
|
|
|
|
#
|
2014-08-26 17:47:10 +04:00
|
|
|
import os
|
2019-10-12 21:40:11 +03:00
|
|
|
import shutil
|
2018-11-03 06:24:10 +03:00
|
|
|
import subprocess
|
2019-07-06 23:40:53 +03:00
|
|
|
import sys
|
2022-01-15 08:08:37 +03:00
|
|
|
import warnings
|
2019-09-26 15:12:28 +03:00
|
|
|
from shlex import quote
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2019-07-06 23:40:53 +03:00
|
|
|
from PIL import Image
|
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
_viewers = []
|
|
|
|
|
2014-08-26 17:47:10 +04:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
def register(viewer, order=1):
|
2020-06-14 13:00:23 +03:00
|
|
|
"""
|
2022-02-21 12:32:21 +03:00
|
|
|
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
|
2020-06-14 13:00:23 +03:00
|
|
|
|
|
|
|
:param viewer: The viewer to be registered.
|
|
|
|
:param order:
|
2020-06-20 14:10:10 +03:00
|
|
|
Zero or a negative integer to prepend this viewer to the list,
|
|
|
|
a positive integer to append it.
|
2020-06-14 13:00:23 +03:00
|
|
|
"""
|
2010-07-31 06:52:47 +04:00
|
|
|
try:
|
|
|
|
if issubclass(viewer, Viewer):
|
|
|
|
viewer = viewer()
|
|
|
|
except TypeError:
|
2014-08-26 17:47:10 +04:00
|
|
|
pass # raised if viewer wasn't a class
|
2010-07-31 06:52:47 +04:00
|
|
|
if order > 0:
|
|
|
|
_viewers.append(viewer)
|
2020-06-18 14:40:38 +03:00
|
|
|
else:
|
2010-07-31 06:52:47 +04:00
|
|
|
_viewers.insert(0, viewer)
|
|
|
|
|
2014-08-26 17:47:10 +04:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
def show(image, title=None, **options):
|
2016-09-28 02:26:57 +03:00
|
|
|
r"""
|
2016-05-24 10:36:14 +03:00
|
|
|
Display a given image.
|
|
|
|
|
2016-07-08 13:36:49 +03:00
|
|
|
:param image: An image object.
|
2020-06-14 13:00:23 +03:00
|
|
|
:param title: Optional title. Not all viewers can display the title.
|
2016-07-08 13:36:49 +03:00
|
|
|
:param \**options: Additional viewer options.
|
2020-06-14 13:00:23 +03:00
|
|
|
:returns: ``True`` if a suitable viewer was found, ``False`` otherwise.
|
2016-05-24 10:36:14 +03:00
|
|
|
"""
|
2010-07-31 06:52:47 +04:00
|
|
|
for viewer in _viewers:
|
|
|
|
if viewer.show(image, title=title, **options):
|
2022-02-11 23:49:20 +03:00
|
|
|
return True
|
|
|
|
return False
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2014-08-26 17:47:10 +04:00
|
|
|
|
2019-09-30 17:56:31 +03:00
|
|
|
class Viewer:
|
2016-05-24 10:36:14 +03:00
|
|
|
"""Base class for viewers."""
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
# main api
|
|
|
|
|
|
|
|
def show(self, image, **options):
|
2020-06-14 13:00:23 +03:00
|
|
|
"""
|
|
|
|
The main function for displaying an image.
|
|
|
|
Converts the given image to the target format and displays it.
|
|
|
|
"""
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2019-06-12 13:33:00 +03:00
|
|
|
if not (
|
2020-06-29 15:14:40 +03:00
|
|
|
image.mode in ("1", "RGBA")
|
|
|
|
or (self.format == "PNG" and image.mode in ("I;16", "LA"))
|
2019-06-12 13:33:00 +03:00
|
|
|
):
|
2010-07-31 06:52:47 +04:00
|
|
|
base = Image.getmodebase(image.mode)
|
2019-06-12 13:33:00 +03:00
|
|
|
if image.mode != base:
|
|
|
|
image = image.convert(base)
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2013-10-31 05:40:21 +04:00
|
|
|
return self.show_image(image, **options)
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
# hook methods
|
|
|
|
|
|
|
|
format = None
|
2020-06-14 13:00:23 +03:00
|
|
|
"""The format to convert the image into."""
|
2017-05-13 16:08:08 +03:00
|
|
|
options = {}
|
2020-06-14 13:00:23 +03:00
|
|
|
"""Additional options used to convert the image."""
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
def get_format(self, image):
|
2020-06-14 13:00:23 +03:00
|
|
|
"""Return format name, or ``None`` to save as PGM/PPM."""
|
2010-07-31 06:52:47 +04:00
|
|
|
return self.format
|
|
|
|
|
|
|
|
def get_command(self, file, **options):
|
2020-06-14 13:00:23 +03:00
|
|
|
"""
|
|
|
|
Returns the command used to display the file.
|
|
|
|
Not implemented in the base class.
|
|
|
|
"""
|
2010-07-31 06:52:47 +04:00
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
def save_image(self, image):
|
2020-06-14 13:00:23 +03:00
|
|
|
"""Save to temporary file and return filename."""
|
2017-05-13 16:08:08 +03:00
|
|
|
return image._dump(format=self.get_format(image), **self.options)
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
def show_image(self, image, **options):
|
2020-06-14 13:00:23 +03:00
|
|
|
"""Display the given image."""
|
2010-07-31 06:52:47 +04:00
|
|
|
return self.show_file(self.save_image(image), **options)
|
|
|
|
|
2022-01-15 08:08:37 +03:00
|
|
|
def show_file(self, path=None, **options):
|
2022-01-16 03:38:34 +03:00
|
|
|
"""
|
|
|
|
Display given file.
|
|
|
|
|
|
|
|
Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated,
|
2022-01-26 01:08:41 +03:00
|
|
|
and will be removed in Pillow 10.0.0 (2023-07-01). ``path`` should be used
|
|
|
|
instead.
|
2022-01-16 03:38:34 +03:00
|
|
|
"""
|
2022-01-15 08:08:37 +03:00
|
|
|
if path is None:
|
|
|
|
if "file" in options:
|
|
|
|
warnings.warn(
|
|
|
|
"The 'file' argument is deprecated and will be removed in Pillow "
|
|
|
|
"10 (2023-07-01). Use 'path' instead.",
|
|
|
|
DeprecationWarning,
|
|
|
|
)
|
|
|
|
path = options.pop("file")
|
|
|
|
else:
|
|
|
|
raise TypeError("Missing required argument: 'path'")
|
|
|
|
os.system(self.get_command(path, **options))
|
2010-07-31 06:52:47 +04:00
|
|
|
return 1
|
|
|
|
|
2019-03-21 16:28:20 +03:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
# --------------------------------------------------------------------
|
|
|
|
|
2017-11-22 13:13:13 +03:00
|
|
|
|
2020-06-14 13:00:23 +03:00
|
|
|
class WindowsViewer(Viewer):
|
|
|
|
"""The default viewer on Windows is the default system application for PNG files."""
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2020-06-14 13:00:23 +03:00
|
|
|
format = "PNG"
|
|
|
|
options = {"compress_level": 1}
|
|
|
|
|
|
|
|
def get_command(self, file, **options):
|
|
|
|
return (
|
2020-07-16 12:43:29 +03:00
|
|
|
f'start "Pillow" /WAIT "{file}" '
|
2020-06-14 13:00:23 +03:00
|
|
|
"&& ping -n 2 127.0.0.1 >NUL "
|
2020-07-16 12:43:29 +03:00
|
|
|
f'&& del /f "{file}"'
|
2020-06-14 13:00:23 +03:00
|
|
|
)
|
2014-08-26 17:47:10 +04:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2020-06-14 13:00:23 +03:00
|
|
|
if sys.platform == "win32":
|
2010-07-31 06:52:47 +04:00
|
|
|
register(WindowsViewer)
|
|
|
|
|
|
|
|
|
2020-06-14 13:00:23 +03:00
|
|
|
class MacViewer(Viewer):
|
2021-10-02 02:50:27 +03:00
|
|
|
"""The default viewer on macOS using ``Preview.app``."""
|
2020-06-14 13:00:23 +03:00
|
|
|
|
|
|
|
format = "PNG"
|
|
|
|
options = {"compress_level": 1}
|
2014-08-26 17:47:10 +04:00
|
|
|
|
2020-06-14 13:00:23 +03:00
|
|
|
def get_command(self, file, **options):
|
|
|
|
# on darwin open returns immediately resulting in the temp
|
|
|
|
# file removal while app is opening
|
|
|
|
command = "open -a Preview.app"
|
2020-07-16 12:43:29 +03:00
|
|
|
command = f"({command} {quote(file)}; sleep 20; rm -f {quote(file)})&"
|
2020-06-14 13:00:23 +03:00
|
|
|
return command
|
|
|
|
|
2022-01-15 08:08:37 +03:00
|
|
|
def show_file(self, path=None, **options):
|
2022-01-16 03:38:34 +03:00
|
|
|
"""
|
|
|
|
Display given file.
|
|
|
|
|
|
|
|
Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated,
|
2022-01-26 01:08:41 +03:00
|
|
|
and will be removed in Pillow 10.0.0 (2023-07-01). ``path`` should be used
|
|
|
|
instead.
|
2022-01-16 03:38:34 +03:00
|
|
|
"""
|
2022-01-15 08:08:37 +03:00
|
|
|
if path is None:
|
|
|
|
if "file" in options:
|
|
|
|
warnings.warn(
|
|
|
|
"The 'file' argument is deprecated and will be removed in Pillow "
|
|
|
|
"10 (2023-07-01). Use 'path' instead.",
|
|
|
|
DeprecationWarning,
|
|
|
|
)
|
|
|
|
path = options.pop("file")
|
|
|
|
else:
|
|
|
|
raise TypeError("Missing required argument: 'path'")
|
2022-01-17 00:59:17 +03:00
|
|
|
subprocess.call(["open", "-a", "Preview.app", path])
|
2022-02-12 00:04:40 +03:00
|
|
|
subprocess.Popen(
|
|
|
|
[
|
|
|
|
sys.executable,
|
|
|
|
"-c",
|
|
|
|
"import os, sys, time; time.sleep(20); os.remove(sys.argv[1])",
|
|
|
|
path,
|
|
|
|
]
|
|
|
|
)
|
2020-06-14 13:00:23 +03:00
|
|
|
return 1
|
|
|
|
|
2018-11-03 06:24:10 +03:00
|
|
|
|
2020-06-14 13:00:23 +03:00
|
|
|
if sys.platform == "darwin":
|
2010-07-31 06:52:47 +04:00
|
|
|
register(MacViewer)
|
|
|
|
|
|
|
|
|
2020-06-14 13:00:23 +03:00
|
|
|
class UnixViewer(Viewer):
|
|
|
|
format = "PNG"
|
|
|
|
options = {"compress_level": 1}
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2020-06-14 13:00:23 +03:00
|
|
|
def get_command(self, file, **options):
|
|
|
|
command = self.get_command_ex(file, **options)[0]
|
2022-02-14 01:33:58 +03:00
|
|
|
return f"({command} {quote(file)}"
|
2017-05-08 23:19:16 +03:00
|
|
|
|
2022-01-17 00:59:17 +03:00
|
|
|
|
|
|
|
class XDGViewer(UnixViewer):
|
|
|
|
"""
|
|
|
|
The freedesktop.org ``xdg-open`` command.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def get_command_ex(self, file, **options):
|
|
|
|
command = executable = "xdg-open"
|
|
|
|
return command, executable
|
|
|
|
|
2022-01-15 08:08:37 +03:00
|
|
|
def show_file(self, path=None, **options):
|
2022-01-16 03:38:34 +03:00
|
|
|
"""
|
|
|
|
Display given file.
|
|
|
|
|
|
|
|
Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated,
|
2022-01-26 01:08:41 +03:00
|
|
|
and will be removed in Pillow 10.0.0 (2023-07-01). ``path`` should be used
|
|
|
|
instead.
|
2022-01-16 03:38:34 +03:00
|
|
|
"""
|
2022-01-15 08:08:37 +03:00
|
|
|
if path is None:
|
|
|
|
if "file" in options:
|
|
|
|
warnings.warn(
|
|
|
|
"The 'file' argument is deprecated and will be removed in Pillow "
|
|
|
|
"10 (2023-07-01). Use 'path' instead.",
|
|
|
|
DeprecationWarning,
|
|
|
|
)
|
|
|
|
path = options.pop("file")
|
|
|
|
else:
|
|
|
|
raise TypeError("Missing required argument: 'path'")
|
2022-01-17 00:59:17 +03:00
|
|
|
subprocess.Popen(["xdg-open", path])
|
2020-06-14 13:00:23 +03:00
|
|
|
return 1
|
2018-11-03 06:24:10 +03:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2020-06-14 13:00:23 +03:00
|
|
|
class DisplayViewer(UnixViewer):
|
2021-10-23 11:51:46 +03:00
|
|
|
"""
|
|
|
|
The ImageMagick ``display`` command.
|
|
|
|
This viewer supports the ``title`` parameter.
|
|
|
|
"""
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2021-10-23 07:53:08 +03:00
|
|
|
def get_command_ex(self, file, title=None, **options):
|
2020-06-14 13:00:23 +03:00
|
|
|
command = executable = "display"
|
2021-10-23 07:53:08 +03:00
|
|
|
if title:
|
2022-02-09 08:21:47 +03:00
|
|
|
command += f" -title {quote(title)}"
|
2020-06-14 13:00:23 +03:00
|
|
|
return command, executable
|
2017-11-22 13:13:13 +03:00
|
|
|
|
2022-01-17 00:59:17 +03:00
|
|
|
def show_file(self, path=None, **options):
|
|
|
|
"""
|
|
|
|
Display given file.
|
|
|
|
|
|
|
|
Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated,
|
|
|
|
and ``path`` should be used instead.
|
|
|
|
"""
|
|
|
|
if path is None:
|
|
|
|
if "file" in options:
|
|
|
|
warnings.warn(
|
|
|
|
"The 'file' argument is deprecated and will be removed in Pillow "
|
|
|
|
"10 (2023-07-01). Use 'path' instead.",
|
|
|
|
DeprecationWarning,
|
|
|
|
)
|
|
|
|
path = options.pop("file")
|
|
|
|
else:
|
|
|
|
raise TypeError("Missing required argument: 'path'")
|
|
|
|
args = ["display"]
|
|
|
|
if "title" in options:
|
2022-02-09 08:21:47 +03:00
|
|
|
args += ["-title", options["title"]]
|
2022-01-17 00:59:17 +03:00
|
|
|
args.append(path)
|
|
|
|
|
|
|
|
subprocess.Popen(args)
|
|
|
|
return 1
|
|
|
|
|
2017-11-18 18:25:58 +03:00
|
|
|
|
2021-03-21 17:36:18 +03:00
|
|
|
class GmDisplayViewer(UnixViewer):
|
|
|
|
"""The GraphicsMagick ``gm display`` command."""
|
|
|
|
|
|
|
|
def get_command_ex(self, file, **options):
|
|
|
|
executable = "gm"
|
|
|
|
command = "gm display"
|
|
|
|
return command, executable
|
|
|
|
|
2022-01-17 00:59:17 +03:00
|
|
|
def show_file(self, path=None, **options):
|
|
|
|
"""
|
|
|
|
Display given file.
|
|
|
|
|
|
|
|
Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated,
|
|
|
|
and ``path`` should be used instead.
|
|
|
|
"""
|
|
|
|
if path is None:
|
|
|
|
if "file" in options:
|
|
|
|
warnings.warn(
|
|
|
|
"The 'file' argument is deprecated and will be removed in Pillow "
|
|
|
|
"10 (2023-07-01). Use 'path' instead.",
|
|
|
|
DeprecationWarning,
|
|
|
|
)
|
|
|
|
path = options.pop("file")
|
|
|
|
else:
|
|
|
|
raise TypeError("Missing required argument: 'path'")
|
|
|
|
subprocess.Popen(["gm", "display", path])
|
|
|
|
return 1
|
|
|
|
|
2021-03-21 17:36:18 +03:00
|
|
|
|
2020-06-14 13:00:23 +03:00
|
|
|
class EogViewer(UnixViewer):
|
|
|
|
"""The GNOME Image Viewer ``eog`` command."""
|
|
|
|
|
|
|
|
def get_command_ex(self, file, **options):
|
2021-05-23 09:56:44 +03:00
|
|
|
executable = "eog"
|
|
|
|
command = "eog -n"
|
2020-06-14 13:00:23 +03:00
|
|
|
return command, executable
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2022-01-17 00:59:17 +03:00
|
|
|
def show_file(self, path=None, **options):
|
|
|
|
"""
|
|
|
|
Display given file.
|
|
|
|
|
|
|
|
Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated,
|
|
|
|
and ``path`` should be used instead.
|
|
|
|
"""
|
|
|
|
if path is None:
|
|
|
|
if "file" in options:
|
|
|
|
warnings.warn(
|
|
|
|
"The 'file' argument is deprecated and will be removed in Pillow "
|
|
|
|
"10 (2023-07-01). Use 'path' instead.",
|
|
|
|
DeprecationWarning,
|
|
|
|
)
|
|
|
|
path = options.pop("file")
|
|
|
|
else:
|
|
|
|
raise TypeError("Missing required argument: 'path'")
|
|
|
|
subprocess.Popen(["eog", "-n", path])
|
|
|
|
return 1
|
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2020-06-14 13:00:23 +03:00
|
|
|
class XVViewer(UnixViewer):
|
|
|
|
"""
|
|
|
|
The X Viewer ``xv`` command.
|
|
|
|
This viewer supports the ``title`` parameter.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def get_command_ex(self, file, title=None, **options):
|
|
|
|
# note: xv is pretty outdated. most modern systems have
|
|
|
|
# imagemagick's display command instead.
|
|
|
|
command = executable = "xv"
|
|
|
|
if title:
|
2020-07-16 12:43:29 +03:00
|
|
|
command += f" -name {quote(title)}"
|
2020-06-14 13:00:23 +03:00
|
|
|
return command, executable
|
|
|
|
|
2022-01-17 00:59:17 +03:00
|
|
|
def show_file(self, path=None, **options):
|
|
|
|
"""
|
|
|
|
Display given file.
|
|
|
|
|
|
|
|
Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated,
|
|
|
|
and ``path`` should be used instead.
|
|
|
|
"""
|
|
|
|
if path is None:
|
|
|
|
if "file" in options:
|
|
|
|
warnings.warn(
|
|
|
|
"The 'file' argument is deprecated and will be removed in Pillow "
|
|
|
|
"10 (2023-07-01). Use 'path' instead.",
|
|
|
|
DeprecationWarning,
|
|
|
|
)
|
|
|
|
path = options.pop("file")
|
|
|
|
else:
|
|
|
|
raise TypeError("Missing required argument: 'path'")
|
|
|
|
args = ["xv"]
|
|
|
|
if "title" in options:
|
|
|
|
args += ["-name", options["title"]]
|
|
|
|
args.append(path)
|
|
|
|
|
|
|
|
subprocess.Popen(args)
|
|
|
|
return 1
|
|
|
|
|
2020-06-14 13:00:23 +03:00
|
|
|
|
|
|
|
if sys.platform not in ("win32", "darwin"): # unixoids
|
2021-12-20 12:08:31 +03:00
|
|
|
if shutil.which("xdg-open"):
|
|
|
|
register(XDGViewer)
|
2020-06-14 13:00:23 +03:00
|
|
|
if shutil.which("display"):
|
|
|
|
register(DisplayViewer)
|
2021-03-21 17:36:18 +03:00
|
|
|
if shutil.which("gm"):
|
|
|
|
register(GmDisplayViewer)
|
2020-06-14 13:00:23 +03:00
|
|
|
if shutil.which("eog"):
|
|
|
|
register(EogViewer)
|
2019-10-12 21:40:11 +03:00
|
|
|
if shutil.which("xv"):
|
2010-07-31 06:52:47 +04:00
|
|
|
register(XVViewer)
|
|
|
|
|
2021-03-04 00:55:24 +03:00
|
|
|
|
|
|
|
class IPythonViewer(Viewer):
|
2021-03-04 00:56:49 +03:00
|
|
|
"""The viewer for IPython frontends."""
|
|
|
|
|
2021-03-04 00:55:24 +03:00
|
|
|
def show_image(self, image, **options):
|
2021-03-04 00:56:03 +03:00
|
|
|
ipython_display(image)
|
2021-03-04 00:55:24 +03:00
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
2021-03-04 00:56:03 +03:00
|
|
|
from IPython.display import display as ipython_display
|
2021-03-04 00:55:24 +03:00
|
|
|
except ImportError:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
register(IPythonViewer)
|
|
|
|
|
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
if __name__ == "__main__":
|
2018-01-06 13:51:45 +03:00
|
|
|
|
|
|
|
if len(sys.argv) < 2:
|
2021-05-08 05:37:06 +03:00
|
|
|
print("Syntax: python3 ImageShow.py imagefile [title]")
|
2018-01-06 13:51:45 +03:00
|
|
|
sys.exit()
|
|
|
|
|
2020-02-18 12:49:05 +03:00
|
|
|
with Image.open(sys.argv[1]) as im:
|
|
|
|
print(show(im, *sys.argv[2:]))
|