Here's a way to take a fullscreen screenshot using the PIL ImageGrab module, that is already installed in many Python environments:
fullscreenShot[] := ExternalEvaluate["Python", "import PIL.ImageGrab
PIL.ImageGrab.grab()"]
Here is a function to take a screenshot of a specified screen rectangle using the same module:
screenShot = ExternalFunction["Python", "import PIL.ImageGrab
def screenshot(left_x, top_y, right_x, bottom_y):
return PIL.ImageGrab.grab(bbox=(left_x, top_y, right_x, bottom_y))"
]
Here is a pure Python function to take a screenshot of the current active window on Windows(1),(2):
activeWindowScreenShot[] := ExternalEvaluate["Python", "import ctypes
from ctypes import wintypes
import PIL.ImageGrab
user32 = ctypes.windll.user32
handle = user32.GetForegroundWindow()
rect = wintypes.RECT()
user32.GetWindowRect(handle, ctypes.pointer(rect))
PIL.ImageGrab.grab(bbox=(rect.left,rect.top,rect.right,rect.bottom))"]
And here is a function to take a screenshot of the current Notebook window:
Clear[currentNotebookImage0]
currentNotebookImage0[margins_ : 6] :=
Module[{screenInformation = First[SystemInformation["Devices", "ScreenInformation"]],
windowMargins = AbsoluteCurrentValue[WindowMargins], resolution, screenArea, width,
height, left, right, bottom, top},
{resolution, screenArea} = {"Resolution", "ScreenArea"} /. screenInformation;
{width, height} = Round[screenArea[[All, 2]]*resolution/72.];
{{left, right}, {bottom, top}} =
Round[If[MatrixQ[windowMargins],
windowMargins + {{margins, margins}, {margins, margins}}, {{0, 0}, {0, 0}}]*
resolution/72.];
{{left, right}, {top, bottom}} = {Clip[{left, width - right}, {0, width}],
Clip[{top, height - bottom}, {0, height}]};
ExternalEvaluate["Python", <|"Command" -> "import PIL.ImageGrab
def screenshot(left_x, top_y, right_x, bottom_y):
return PIL.ImageGrab.grab(bbox=(left_x, top_y, right_x, bottom_y))",
"Arguments" -> {left, top, right, bottom}|>]]
The default value 6
for the parameter margins
is taken from what returns AbsoluteCurrentValue[WindowMargins]
when the Notebook window is maximized on my system (Mathematica 13.1.0 on Windows 10 x64):
AbsoluteCurrentValue[WindowMargins]
-6
With Mathematica 12.3.1 on the same machine I get the value -8
.
The value -6
means that when the Notebook window is maximized, Mathematica thinks that the window is actually cropped by 6
printer's points from all sides. Translating into normal language, this means that the window is always enlarged by 6 printer's points on all sides as compared to its maximized size. The latter is equivalent in the meaning to the ImageMargins
option of Graphics
in Mathematica. Hence the margins
parameter name.
These considerations are confirmed by comparing the values of the window width obtained from AbsoluteCurrentValue[WindowSize]
and AbsoluteCurrentValue[WindowMargins]
(output is for Mathematica 13.1.0):
windowMargins = AbsoluteCurrentValue[WindowMargins];
screenArea = "ScreenArea" /. First[SystemInformation["Devices", "ScreenInformation"]];
windowSize = AbsoluteCurrentValue[WindowSize];
screenArea[[All, 2]] - Total /@ windowMargins - windowSize
{12., 44.25}
(for Mathematica 12.3.1 the output is {16, 59}
).
We see that the window width reported by AbsoluteCurrentValue[WindowSize]
is 6*2 = 12
printer's points smaller than the value obtain from AbsoluteCurrentValue[WindowMargins]
. Hence we can calculate the value for the parameter margins
from AbsoluteCurrentValue[WindowSize]
as follows:
margins = If[MatrixQ[windowMargins],
(screenArea[[1, 2]] - Total[windowMargins[[1]]] - windowSize[[1]])/2, -windowMargins]
6.
With this definition, currentNotebookImage0
can be rewritten as follows:
Clear[currentNotebookImage]
currentNotebookImage[] :=
Module[{screenInformation = First[SystemInformation["Devices", "ScreenInformation"]],
windowMargins = AbsoluteCurrentValue[WindowMargins], margins, left, right, bottom, top,
windowSize = AbsoluteCurrentValue[WindowSize], resolution, screenArea, width, height},
{resolution, screenArea} = {"Resolution", "ScreenArea"} /. screenInformation;
margins = If[MatrixQ[windowMargins],
(screenArea[[1, 2]] - Total[windowMargins[[1]]] - windowSize[[1]])/2, -windowMargins];
{width, height} = Round[screenArea[[All, 2]]*resolution/72.];
{{left, right}, {bottom, top}} =
Round[(windowMargins + margins*{{1, 1}, {1, 1}})*resolution/72.];
{{left, right}, {top, bottom}} = {Clip[{left, width - right}, {0, width}],
Clip[{top, height - bottom}, {0, height}]};
ExternalEvaluate["Python", <|"Command" -> "import PIL.ImageGrab
def screenshot(left_x, top_y, right_x, bottom_y):
return PIL.ImageGrab.grab(bbox=(left_x, top_y, right_x, bottom_y))",
"Arguments" -> {left, top, right, bottom}|>]]
The main advantage of this approach is that it does not require "magic" numbers and works perfectly well both with version 13.1.0 and 12.3.1:
currentNotebookImage[]
It is easy to rewrite the great cCurrentNotebookImage[]
function by Ben Izd using the new method for determining the window rectangle coordinates:
Needs["JLink`"];
LoadJavaClass /@ {"java.awt.Robot", "java.awt.Rectangle",
"java.io.ByteArrayOutputStream", "javax.imageio.ImageIO"};
Clear[cCurrentNotebookImage]
cCurrentNotebookImage[] :=
Module[{screenInformation = First[SystemInformation["Devices", "ScreenInformation"]],
windowMargins = AbsoluteCurrentValue[WindowMargins], margins, left, right, bottom, top,
windowSize = AbsoluteCurrentValue[WindowSize], resolution, screenArea, width, height},
{resolution, screenArea} = {"Resolution", "ScreenArea"} /. screenInformation;
margins = If[MatrixQ[windowMargins],
(screenArea[[1, 2]] - Total[windowMargins[[1]]] - windowSize[[1]])/2, -windowMargins];
{width, height} = Round[screenArea[[All, 2]]*resolution/72.];
{{left, right}, {bottom, top}} =
Round[(windowMargins + margins*{{1, 1}, {1, 1}})*resolution/72.];
{{left, right}, {top, bottom}} = {Clip[{left, width - right}, {0, width}],
Clip[{top, height - bottom}, {0, height}]};
JavaBlock[robot = JavaNew["java.awt.Robot"];
temp = JavaNew["java.io.ByteArrayOutputStream"];
img = robot@
createScreenCapture[
JavaNew["java.awt.Rectangle", left, top, right - left, bottom - top]];
ImageIO`write[img, "png", temp];
ImportByteArray[ByteArray@Mod[temp@toByteArray[], 2^8], "PNG"]]]
There is an old but still working GUIScreenShot[]
from "GUIKit`"
. To take a fullscreen screenshot, just evaluate:
Needs["GUIKit`"]
GUIScreenShot[]
To take a screenshot of the currently active Notebook, use the following function:
gCurrentNotebookImage[] :=
Module[{screenInformation = First[SystemInformation["Devices", "ScreenInformation"]],
windowMargins = AbsoluteCurrentValue[WindowMargins], margins, left, right, bottom, top,
windowSize = AbsoluteCurrentValue[WindowSize], resolution, screenArea, width,
height}, {resolution, screenArea} = {"Resolution", "ScreenArea"} /. screenInformation;
margins = If[MatrixQ[windowMargins],
(screenArea[[1, 2]] - Total[windowMargins[[1]]] - windowSize[[1]])/2, -windowMargins];
{width, height} = Round[screenArea[[All, 2]]*resolution/72.];
{{left, right}, {bottom, top}} =
Round[(windowMargins + margins*{{1, 1}, {1, 1}})*resolution/72.];
{{left, right}, {top, bottom}} = {Clip[{left, width - right}, {0, width}],
Clip[{top, height - bottom}, {0, height}]};
GUIKit`GUIScreenShot[{{left, right}, {top, bottom}}]]
Module
withJavaBlock
and insertrobot = JavaNew["java.awt.Robot"];
, to prevent memory leaking. $\endgroup$