Java Robot 从 Windows 系统托盘启动

huangapple go评论59阅读模式
英文:

Java Robot launched from Windows System Tray

问题

我想让我的程序从系统托盘菜单执行屏幕捕获。

我已经让它工作了,但菜单项本身隐藏得不够快,导致与屏幕的其余部分一起被捕获。

这是一个MCVE,基于Java教程的TrayIconDemo示例的精简版本:
(编辑:由于Holger的评论,进一步简化)

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.net.URL;

public class TrayCapture {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                try {
                    createAndShowGUI();
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    private static void createAndShowGUI() throws Exception {
        // 检查是否支持SystemTray
        if (!SystemTray.isSupported()) {
            System.err.println("SystemTray is not supported");
            return;
        }
        final PopupMenu popup = new PopupMenu();
        final TrayIcon trayIcon = new TrayIcon(ImageIO.read(new URL("https://i.stack.imgur.com/wCF8S.png")));
        trayIcon.setImageAutoSize(true);

        // 创建弹出菜单组件
        MenuItem captureItem = new MenuItem("Capture");
        MenuItem exitItem = new MenuItem("Exit");

        // 将组件添加到弹出菜单
        popup.add(captureItem);
        popup.add(exitItem);

        trayIcon.setPopupMenu(popup);

        SystemTray.getSystemTray().add(trayIcon);

        captureItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                try {
                    File outputFile = new File("capture.png");
                    ImageIO.write(new Robot().createScreenCapture(
                            new Rectangle(Toolkit.getDefaultToolkit().getScreenSize())),
                            "png",
                            outputFile);
                    JOptionPane.showMessageDialog(null, "Screenshot saved to: " + outputFile.getAbsolutePath());
                }
                catch (AWTException | IOException ex) {
                    ex.printStackTrace();
                }
            }
        });

        exitItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                SystemTray.getSystemTray().remove(trayIcon);
                System.exit(0);
            }
        });
    }
}

这是生成的截图。

Java Robot 从 Windows 系统托盘启动

如您所见,弹出菜单已关闭,但“Capture”菜单项仍然可见,当机器人进行捕获时,它成为捕获的一部分。

是否有一种方法可以强制重新绘制以隐藏菜单项,或者获取菜单项隐藏时的通知,以便我可以确保机器人不会捕捉到它?我宁愿避免使用任意的Thread.sleep(),因为这只是一个盲目的竞态条件...

KR,Vicne

英文:

I'd like my program to perform a screen capture from a System Tray menu.

I got it to work, but the MenuItem itself does not hide fast enough and is captured along with the rest of the screen.

Here's a MCVE, based on a stripped down version of The Java Tutorial's TrayIconDemo sample:
(Edit: made even more minimal thanks to Holger's comment)

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class TrayCapture {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
createAndShowGUI();
}
catch (Exception e) {
e.printStackTrace();
}
}
});
}
private static void createAndShowGUI() throws Exception {
//Check the SystemTray support
if (!SystemTray.isSupported()) {
System.err.println("SystemTray is not supported");
return;
}
final PopupMenu popup = new PopupMenu();
final TrayIcon trayIcon = new TrayIcon(ImageIO.read(new URL("https://i.stack.imgur.com/wCF8S.png")));
trayIcon.setImageAutoSize(true);
// Create a popup menu components
MenuItem captureItem = new MenuItem("Capture");
MenuItem exitItem = new MenuItem("Exit");
//Add components to popup menu
popup.add(captureItem);
popup.add(exitItem);
trayIcon.setPopupMenu(popup);
SystemTray.getSystemTray().add(trayIcon);
captureItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
File outputFile = new File("capture.png");
ImageIO.write(new Robot().createScreenCapture(
new Rectangle(Toolkit.getDefaultToolkit().getScreenSize())),
"png",
outputFile);
JOptionPane.showMessageDialog(null, "Screenshot saved to: " + outputFile.getAbsolutePath());
}
catch (AWTException | IOException ex) {
ex.printStackTrace();
}
}
});
exitItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
SystemTray.getSystemTray().remove(trayIcon);
System.exit(0);
}
});
}
}

And here is a resulting capture.

Java Robot 从 Windows 系统托盘启动

As you can see, the PopupMenu is closed but the "Capture" MenuItem was still visible when the Robot took the capture and is part of the captured image.

Is there a way to force a repaint to hide the MenuItem or get a notification when it's hidden so I can make sure the Robot will not pick it up ? I'd rather avoid an arbitrary Thread.sleep() because it's just a blind race condition...

KR, Vicne

答案1

得分: 2

这不是您的Java应用程序的一个特征,因为它是一个特定于Windows的已选择菜单项的动画,即使弹出菜单已关闭,该动画仍然继续。

据我所知,除了捕获屏幕并查看是否仍然存在该特征之外,从Java端监视动画进度的方法是不存在的。

最佳选择是在进行屏幕截图之前设置可配置的延迟(例如,合理的默认值为300毫秒)。我知道的所有屏幕截图工具都使用可配置的延迟。

英文:

This is not an artifact of your Java application, as it is a Windows-specific animation of the selected menu item that continues while the popup menu is already closed.

As far as I know, there is no way to monitor the animation progress from the Java side (apart from capturing the screen and looking whether the artifact is still there).

The best option is to have a configurable delay (with a reasonable default of, e.g. 300 ms) before doing the screenshots. All screenshot tools I know of, use a configurable delay anyway.

huangapple
  • 本文由 发表于 2020年7月29日 22:04:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/63155462.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定