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.
|
|
|
|
#
|
|
|
|
|
2012-10-16 06:27:35 +04:00
|
|
|
from __future__ import print_function
|
|
|
|
|
2013-03-07 20:20:28 +04:00
|
|
|
from PIL import Image
|
2014-08-26 17:47:10 +04:00
|
|
|
import os
|
|
|
|
import sys
|
2018-11-03 06:24:10 +03:00
|
|
|
import subprocess
|
|
|
|
import tempfile
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2018-02-06 19:39:28 +03:00
|
|
|
if sys.version_info.major >= 3:
|
2013-10-31 05:29:15 +04:00
|
|
|
from shlex import quote
|
|
|
|
else:
|
|
|
|
from pipes import quote
|
|
|
|
|
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):
|
|
|
|
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)
|
|
|
|
elif order < 0:
|
|
|
|
_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.
|
|
|
|
: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.
|
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):
|
|
|
|
return 1
|
|
|
|
return 0
|
|
|
|
|
2014-08-26 17:47:10 +04:00
|
|
|
|
2015-05-26 17:07:21 +03:00
|
|
|
class Viewer(object):
|
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):
|
|
|
|
|
|
|
|
# save temporary image to disk
|
|
|
|
if image.mode[:4] == "I;16":
|
|
|
|
# @PIL88 @PIL101
|
|
|
|
# "I;16" isn't an 'official' mode, but we still want to
|
|
|
|
# provide a simple way to show 16-bit images.
|
|
|
|
base = "L"
|
|
|
|
# FIXME: auto-contrast if max() > 255?
|
|
|
|
else:
|
|
|
|
base = Image.getmodebase(image.mode)
|
2019-05-25 09:24:42 +03:00
|
|
|
if not (base == image.mode or
|
|
|
|
image.mode in ("1", "RGBA") or
|
|
|
|
(self.format == "PNG" and image.mode == "LA")):
|
2010-07-31 06:52:47 +04:00
|
|
|
image = image.convert(base)
|
|
|
|
|
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
|
2017-05-13 16:08:08 +03:00
|
|
|
options = {}
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
def get_format(self, image):
|
2016-05-24 10:36:14 +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):
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
def save_image(self, image):
|
2016-05-24 10:36:14 +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):
|
2016-05-24 10:36:14 +03:00
|
|
|
"""Display given image"""
|
2010-07-31 06:52:47 +04:00
|
|
|
return self.show_file(self.save_image(image), **options)
|
|
|
|
|
|
|
|
def show_file(self, file, **options):
|
2016-05-24 10:36:14 +03:00
|
|
|
"""Display given file"""
|
2010-07-31 06:52:47 +04:00
|
|
|
os.system(self.get_command(file, **options))
|
|
|
|
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
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
if sys.platform == "win32":
|
|
|
|
|
|
|
|
class WindowsViewer(Viewer):
|
|
|
|
format = "BMP"
|
2014-08-26 17:47:10 +04:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
def get_command(self, file, **options):
|
2019-03-21 16:28:20 +03:00
|
|
|
return (
|
|
|
|
'start "Pillow" /WAIT "%s" '
|
|
|
|
"&& ping -n 2 127.0.0.1 >NUL "
|
|
|
|
'&& del /f "%s"' % (file, file)
|
|
|
|
)
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
register(WindowsViewer)
|
|
|
|
|
|
|
|
elif sys.platform == "darwin":
|
|
|
|
|
|
|
|
class MacViewer(Viewer):
|
2017-05-08 23:19:16 +03:00
|
|
|
format = "PNG"
|
2019-03-21 16:28:20 +03:00
|
|
|
options = {"compress_level": 1}
|
2014-08-26 17:47:10 +04:00
|
|
|
|
2010-07-31 06:52:47 +04: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 /Applications/Preview.app"
|
2019-03-21 16:28:20 +03:00
|
|
|
command = "(%s %s; sleep 20; rm -f %s)&" % (
|
|
|
|
command,
|
|
|
|
quote(file),
|
|
|
|
quote(file),
|
|
|
|
)
|
2010-07-31 06:52:47 +04:00
|
|
|
return command
|
|
|
|
|
2018-11-03 06:24:10 +03:00
|
|
|
def show_file(self, file, **options):
|
|
|
|
"""Display given file"""
|
2018-12-31 00:27:07 +03:00
|
|
|
fd, path = tempfile.mkstemp()
|
2019-03-21 16:28:20 +03:00
|
|
|
with os.fdopen(fd, "w") as f:
|
2018-12-31 00:27:07 +03:00
|
|
|
f.write(file)
|
2018-11-03 06:24:10 +03:00
|
|
|
with open(path, "r") as f:
|
2019-03-21 16:28:20 +03:00
|
|
|
subprocess.Popen(
|
|
|
|
[
|
|
|
|
"im=$(cat);"
|
|
|
|
"open -a /Applications/Preview.app $im;"
|
|
|
|
"sleep 20;"
|
|
|
|
"rm -f $im"
|
|
|
|
],
|
|
|
|
shell=True,
|
|
|
|
stdin=f,
|
|
|
|
)
|
2018-11-03 06:24:10 +03:00
|
|
|
os.remove(path)
|
|
|
|
return 1
|
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
register(MacViewer)
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
# unixoids
|
|
|
|
|
|
|
|
def which(executable):
|
|
|
|
path = os.environ.get("PATH")
|
|
|
|
if not path:
|
|
|
|
return None
|
|
|
|
for dirname in path.split(os.pathsep):
|
|
|
|
filename = os.path.join(dirname, executable)
|
2016-05-03 13:59:52 +03:00
|
|
|
if os.path.isfile(filename) and os.access(filename, os.X_OK):
|
2010-07-31 06:52:47 +04:00
|
|
|
return filename
|
|
|
|
return None
|
|
|
|
|
|
|
|
class UnixViewer(Viewer):
|
2017-05-08 23:19:16 +03:00
|
|
|
format = "PNG"
|
2019-03-21 16:28:20 +03:00
|
|
|
options = {"compress_level": 1}
|
2017-05-08 23:19:16 +03:00
|
|
|
|
2018-11-03 05:13:04 +03:00
|
|
|
def get_command(self, file, **options):
|
|
|
|
command = self.get_command_ex(file, **options)[0]
|
|
|
|
return "(%s %s; rm -f %s)&" % (command, quote(file), quote(file))
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2018-11-03 06:24:10 +03:00
|
|
|
def show_file(self, file, **options):
|
|
|
|
"""Display given file"""
|
2018-12-31 00:27:07 +03:00
|
|
|
fd, path = tempfile.mkstemp()
|
2019-03-21 16:28:20 +03:00
|
|
|
with os.fdopen(fd, "w") as f:
|
2018-12-31 00:27:07 +03:00
|
|
|
f.write(file)
|
2018-11-03 06:24:10 +03:00
|
|
|
with open(path, "r") as f:
|
|
|
|
command = self.get_command_ex(file, **options)[0]
|
2019-03-21 16:28:20 +03:00
|
|
|
subprocess.Popen(
|
|
|
|
["im=$(cat);" + command + " $im;" "rm -f $im"], shell=True, stdin=f
|
|
|
|
)
|
2018-11-03 06:24:10 +03:00
|
|
|
os.remove(path)
|
|
|
|
return 1
|
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
# implementations
|
|
|
|
|
|
|
|
class DisplayViewer(UnixViewer):
|
|
|
|
def get_command_ex(self, file, **options):
|
2017-11-18 23:00:57 +03:00
|
|
|
command = executable = "display"
|
2010-07-31 06:52:47 +04:00
|
|
|
return command, executable
|
|
|
|
|
2017-11-22 13:13:13 +03:00
|
|
|
if which("display"):
|
|
|
|
register(DisplayViewer)
|
|
|
|
|
2017-11-18 23:00:57 +03:00
|
|
|
class EogViewer(UnixViewer):
|
|
|
|
def get_command_ex(self, file, **options):
|
|
|
|
command = executable = "eog"
|
|
|
|
return command, executable
|
2017-11-18 18:25:58 +03:00
|
|
|
|
2017-11-22 13:13:13 +03:00
|
|
|
if which("eog"):
|
2017-11-18 23:00:57 +03:00
|
|
|
register(EogViewer)
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
class XVViewer(UnixViewer):
|
|
|
|
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:
|
2014-05-10 08:36:15 +04:00
|
|
|
command += " -name %s" % quote(title)
|
2010-07-31 06:52:47 +04:00
|
|
|
return command, executable
|
|
|
|
|
|
|
|
if which("xv"):
|
|
|
|
register(XVViewer)
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
2018-01-06 13:51:45 +03:00
|
|
|
|
|
|
|
if len(sys.argv) < 2:
|
|
|
|
print("Syntax: python ImageShow.py imagefile [title]")
|
|
|
|
sys.exit()
|
|
|
|
|
2012-10-16 06:27:35 +04:00
|
|
|
print(show(Image.open(sys.argv[1]), *sys.argv[2:]))
|