Подтвердить что ты не робот

Нужно ли запускать роботы в очереди событий?

Робот является частью библиотеки AWT, но, похоже, он отличается от большинства остальных библиотек. Я создаю Swing GUI, который смешивает Swing с Java Native Access (JNA) и Robot, чтобы позволить Java управлять некоторыми рабочими программами MS Windows/Citrix. Я чувствую, что, поскольку робот будет размещать в очереди события в "начальной входной очереди платформы", последнее, что я хочу сделать, это запустить его на EDT, но, с другой стороны, большинство классов в AWT и Swing-библиотеках должен выполняться в потоке событий Swing. Поэтому, чтобы попытаться прояснить это в моем сознании, позвольте мне задать как можно более конкретный вопрос:

Должны ли методы робота (в частности, нажатия и отпускания клавиш, перемещения мыши, нажатия и отпускания мыши) запускаться или выходить из потока отправки событий Swing (EDT)?

4b9b3361

Ответ 1

Методы Robot, о которых вы упомянули, не должны запускаться в EDT. Взглянув на исходный код, выяснилось, что каждый из этих "событийных" методов имеет одну общую черту (вызов afterEvent):

public synchronized void keyPress(int keycode) {
    checkKeycodeArgument(keycode);
    peer.keyPress(keycode);
    afterEvent();
}

public synchronized void mousePress(int buttons) {
    checkButtonsArgument(buttons);
    peer.mousePress(buttons);
    afterEvent();
}

// etc

private void afterEvent() {
    autoWaitForIdle();
    autoDelay();
}

private void autoWaitForIdle() {
    if (isAutoWaitForIdle) {
        waitForIdle();
    }
}

public synchronized void waitForIdle() {
    checkNotDispatchThread();
    /* snip */
}

private void checkNotDispatchThread() {
    if (EventQueue.isDispatchThread()) {
        throw new IllegalThreadStateException("Cannot call method from the event dispatcher thread");
    }
}

Если вы вызовете любой из этих методов на EDT, а Robot.isAutoWaitForIdle true, будет выбрано исключение. Это означает, что даже если isAutoWaitForIdle false, эти методы не следует вызывать из EDT.

Ответ 2

  • API совершенно точно говорит, тогда я понимаю, что роботу следует игнорировать, если он вызван с EDT или нет.

Использование класса для генерации входных событий отличается от отправки событий в очередь событий AWT или AWT-компонентов тем, что события генерируются в собственной входной очереди платформы.

  • Я отношусь к новым в Java, первым касался Java1.6.009, тогда я не могу сравнивать изменения для AWT и (когда родился) Swing в Java1.3 и отдыхать в Java1.4.

мой пример

import javax.imageio.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;

public class CaptureScreen implements ActionListener {

    private JFrame f = new JFrame("Screen Capture");
    private JPanel pane = new JPanel();
    private JButton capture = new JButton("Capture");
    private JDialog d = new JDialog();
    private JScrollPane scrollPane = new JScrollPane();
    private JLabel l = new JLabel();
    private Point location;

    public CaptureScreen() {
        capture.setActionCommand("CaptureScreen");
        capture.setFocusPainted(false);
        capture.addActionListener(this);
        capture.setPreferredSize(new Dimension(300, 50));
        pane.add(capture);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(pane);
        f.setLocation(100, 100);
        f.pack();
        f.setVisible(true);
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                createPicContainer();
            }
        });
    }

    private void createPicContainer() {
        l.setPreferredSize(new Dimension(700, 500));
        scrollPane = new JScrollPane(l,
                ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
                ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        scrollPane.setBackground(Color.white);
        scrollPane.getViewport().setBackground(Color.white);
        d.setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE);
        d.add(scrollPane);
        d.pack();
        d.setVisible(false);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getActionCommand().equals("CaptureScreen")) {
            Dimension d1 = Toolkit.getDefaultToolkit().getScreenSize(); // gets the screen size
            Robot r;
            BufferedImage bI;
            try {
                r = new Robot(); // creates robot not sure exactly how it works
                Thread.sleep(1000); // waits 1 second before capture
                bI = r.createScreenCapture(new Rectangle(d1)); // tells robot to capture the screen
                showPic(bI);
                saveImage(bI);
            } catch (AWTException e1) {
                e1.printStackTrace();
            } catch (InterruptedException e2) {
                e2.printStackTrace();
            }
        }
    }

    private void saveImage(BufferedImage bI) {
        try {
            ImageIO.write(bI, "JPG", new File("screenShot.jpg"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void showPic(BufferedImage bI) {
        ImageIcon pic = new ImageIcon(bI);
        l.setIcon(pic);
        l.revalidate();
        l.repaint();
        d.setVisible(false);
        location = f.getLocationOnScreen();
        int x = location.x;
        int y = location.y;
        d.setLocation(x, y + f.getHeight());
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                d.setVisible(true);
            }
        });
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                CaptureScreen cs = new CaptureScreen();
            }
        });
    }
}

Ответ 3

Усиляясь на продуманном ответе @mKorbel и подтверждая его эмпирический результат, обратите внимание, как различные методы Robot делегируют внутренний экземпляр RobotPeer, встроенная реализация которого зависит от платформы. Кроме того, методы синхронизированы. Синтетические события все поступают на EventQueue, независимо от источника.