2012-06-14 12 views
8

Tôi đang sử dụng java.awt.Robot để kiểm tra tích hợp ứng dụng Swing của mình, nhưng tôi đang gặp sự cố khi chạy tác vụ của mình theo đúng thứ tự. Làm thế nào tôi có thể nói cho các chủ đề mà các cuộc gọi robot.mousePressed(...) để chặn cho đến khi Swing được hoàn thành cử sự kiện đó? Rõ ràng, robot.setAutoWaitForIdle(true) không tốt.Liệu java.awt.Robot.waitForIdle() có chờ các sự kiện được gửi đi không?

Đây là bản trình diễn của tôi. Tôi hy vọng "robot đã hoàn thành!" thông báo luôn đến sau "Hành động đã hoàn tất chặn". Tuy nhiên, thay vào đó nó thường diễn ra quá sớm.

import java.awt.AWTException; 
import java.awt.GraphicsConfiguration; 
import java.awt.GraphicsDevice; 
import java.awt.GraphicsEnvironment; 
import java.awt.Point; 
import java.awt.Rectangle; 
import java.awt.Robot; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.event.InputEvent; 
import java.sql.Date; 
import java.text.DateFormat; 
import java.util.logging.ConsoleHandler; 
import java.util.logging.Formatter; 
import java.util.logging.LogManager; 
import java.util.logging.LogRecord; 
import java.util.logging.Logger; 

import javax.swing.GroupLayout; 
import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.SwingUtilities; 
import javax.swing.WindowConstants; 


public class RobotWaitForIdleDemo { 
    /** 
    * Create the device that contains the given point in screen coordinates. 
    * Robot has to be constructed differently for each monitor. 
    */ 
    public static GraphicsDevice getDevice(Point p) { 
     GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); 
     GraphicsDevice[] gs = ge.getScreenDevices(); 

     // Search the devices for the one that draws the specified point. 
     for (GraphicsDevice device : gs) { 
      GraphicsConfiguration configuration = device.getDefaultConfiguration(); 
      Rectangle bounds = configuration.getBounds(); 
      if (bounds.contains(p)) { 
       return device; 
      } 
     } 
     return null; 
    } 
    public static final Logger logger = Logger.getLogger(RobotWaitForIdleDemo.class.getName()); 
    public static void main(String[] args) { 
     LogManager.getLogManager().reset(); 
     Formatter formatter = new Formatter() { 
      @Override 
      public String format(LogRecord arg0) { 
       Date date = new Date(arg0.getMillis()); 
       DateFormat.getTimeInstance().format(date); 
       return String.format("%s %s %s %s%n", 
         DateFormat.getTimeInstance().format(date), 
         arg0.getLoggerName(), 
         arg0.getLevel(), 
         arg0.getMessage()); 
      } 
     }; 
     ConsoleHandler consoleHandler = new ConsoleHandler(); 
     consoleHandler.setFormatter(formatter); 
     logger.addHandler(consoleHandler); 

     final JFrame jframe = new JFrame("Robot experiment"); 
     GroupLayout groupLayout = new GroupLayout(jframe.getContentPane()); 

     final JButton jbutton = new JButton("Click me!"); 
     jbutton.addActionListener(new ActionListener() { 
      @Override public void actionPerformed(ActionEvent e) { 
       // Simulate a heavy Swing event handler. 
       logger.info("(swing thread) Action starting to block..."); 
       try { 
        Thread.sleep(500); 
       } catch (InterruptedException e1) {} 
       logger.info("(swing thread) Action finished blocking."); 
      } 
     }); 

     JButton tryAgainBUtton = new JButton("Automatically click above button."); 
     tryAgainBUtton.addActionListener(new ActionListener() { 
      @Override public void actionPerformed(ActionEvent e) { 
       new Thread(new Runnable() { 
        @Override public void run() { 
         try { 
          Point point = new Point(jbutton.getWidth()/2,jbutton.getHeight()/2); 
          SwingUtilities.convertPointToScreen(point, jbutton); 
          GraphicsDevice device = getDevice(point); 
          Point offset = device.getDefaultConfiguration().getBounds().getLocation(); 

          Robot robot = new Robot(device); 
          robot.setAutoWaitForIdle(true); 
          robot.setAutoDelay(30); 

          robot.mouseMove(point.x - offset.x, point.y - offset.y); 
          String threadName = Thread.currentThread().getName(); 
          logger.info(String.format("(%s) robot.mousePress(%d)", threadName, InputEvent.BUTTON1_MASK)); 
          robot.mousePress(InputEvent.BUTTON1_MASK); 
          logger.info(String.format("(%s) robot.mouseRelease(%d)", threadName, InputEvent.BUTTON1_MASK)); 
          robot.mouseRelease(InputEvent.BUTTON1_MASK); 
          logger.info(String.format("(%s) robot finished!", threadName, InputEvent.BUTTON1_MASK)); 
         } catch (AWTException ex) { 
          ex.printStackTrace(); 
         } 
        } 
       }, "robot thread").start(); 
      } 
     }); 

     jframe.getContentPane().setLayout(groupLayout); 
     groupLayout.setAutoCreateGaps(true); 
     groupLayout.setAutoCreateContainerGaps(true); 
     groupLayout.setVerticalGroup(
       groupLayout.createSequentialGroup() 
        .addComponent(jbutton) 
        .addComponent(tryAgainBUtton)); 
     groupLayout.setHorizontalGroup(
       groupLayout.createParallelGroup() 
        .addComponent(jbutton) 
        .addComponent(tryAgainBUtton)       ); 

     jframe.setSize(300, 300); 
     jframe.setVisible(true); 
     jframe.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); 
    } 
} 

Tôi đang chạy Java 1.6 trên Ubuntu.

+0

+1 cho [sscce] (http://sscce.org/); xem thêm [Chủ đề ban đầu] (http://download.oracle.com/javase/tutorial/uiswing/concurrency/initial.html). – trashgod

Trả lời

5

có lẽ cái này có thể giúp bạn, thông báo không được kiểm tra trong Java7

bạn có thể kiểm tra rằng trong mỗi bước cho isEventDispatchThread()

import java.awt.*; 
import java.awt.event.*; 
import javax.swing.*; 
import sun.awt.SunToolkit; 

public class TestMenu { 

    /** 
    * Without a delay, SunToolkit may encounter a problem in SunToolkit (at 
    * least in JDK 6, where the drop down size problem is not present). 
    * 
    * Note: SunToolkit also has some mechanism to delay, but I forgot how it 
    * worked. 
    * 
    * <pre> 
    * Exception in thread "main" sun.awt.SunToolkit$InfiniteLoop 
    *   at sun.awt.SunToolkit.realSync(Unknown Source) 
    *   at TestMenu.syncAndDelay(TestMenu.java:172) 
    *   at TestMenu.click(TestMenu.java:88) 
    *   at TestMenu.moveAndClickCenter(TestMenu.java:150) 
    *   at TestMenu.main(TestMenu.java:45) 
    * </pre> 
    * 
    * As a bonus, the delay makes the scenario better visible for the human 
    * eye. 
    */ 
    private static int delay = 500; 
    private static JMenu[] menus = new JMenu[5]; 
    private static Dimension[] parentSizes; 
    private static Robot robot; 
    private static SunToolkit toolkit; 

    public static void main(String[] args) throws Exception { 
     robot = new Robot(); 
     toolkit = (SunToolkit) Toolkit.getDefaultToolkit(); 
     parentSizes = new Dimension[menus.length]; 
     createGUI(); // Open the first menu. Then get the drop down size of all menu's 
     moveAndClickCenter(menus[0]); 
     for (int index = 0; index < menus.length; index++) { 
      parentSizes[index] = getDropDownSize(index); 
     }// Click the last item on the last menu.   
     Component item = menus[menus.length - 1].getMenuComponent(menus[menus.length - 1].getMenuComponentCount() - 1); 
     moveAndClickCenter(item); 
     // Open the last drop down again. Then get the drop down sizes once more. If size not equal to previous size, then it's a bug. 
     boolean bug = false; 
     moveAndClickCenter(menus[menus.length - 1]); 
     for (int index = menus.length - 1; index >= 0; index--) { 
      Dimension currentSize = getDropDownSize(index); 
      System.out.print("old: " + parentSizes[index] + ", new: " + currentSize); 
      if (!parentSizes[index].equals(currentSize)) { 
       bug = true; 
       System.out.println(" ERROR"); 
      } else { 
       System.out.println(); 
      } 
     } 
     if (bug) { 
      throw new RuntimeException("JMenu drop down size is changed for no reason."); 
     } 

    } 

    private static Dimension getDropDownSize(int index) throws Exception { 
     moveToCenter(menus[index]); 
     return menus[index].getMenuComponent(0).getParent().getSize(); 
    } 

    private static void click() throws Exception { 
     robot.mousePress(InputEvent.BUTTON1_MASK); 
     robot.mouseRelease(InputEvent.BUTTON1_MASK); 
     syncAndDelay(); 
    } 

    private static void createGUI() throws Exception { 

     SwingUtilities.invokeAndWait(new Runnable() { 

      @Override 
      public void run() { 
       UIManager.LookAndFeelInfo[] infos = UIManager.getInstalledLookAndFeels();// The L&F defines the drop down policy. 
       for (final UIManager.LookAndFeelInfo info : infos) { 
        if (info.getName().toLowerCase().indexOf("metal") >= 0) { 
         if (!UIManager.getLookAndFeel().getName().equals(info.getName())) { 
          try { 
           UIManager.setLookAndFeel(info.getClassName()); 
           System.out.println("Attempt to set look and feel to " + info.getName()); 
          } catch (Exception e) { 
           e.printStackTrace(); 
          } 
         } else { 
          System.out.println("Metal look and feel is the default"); 
         } 
         break; 
        } 
       } 
       System.out.println("Testing with " + UIManager.getLookAndFeel().getName()); // Setup the GUI. 
       JFrame frame = new JFrame("A frame"); 
       frame.setJMenuBar(new JMenuBar()); 
       for (int menuIndex = 0; menuIndex < menus.length; menuIndex++) { 
        menus[menuIndex] = new JMenu("Menu " + menuIndex); 
        frame.getJMenuBar().add(menus[menuIndex]); 
        for (int itemIndex = 0; itemIndex <= menus.length - menuIndex; itemIndex++) { 
         // It seems that the problem only occurs if the drop down is displayed outside the frame at the right 
         // (not sure though). A rather long item name. 
         JMenuItem item = new JMenuItem("Menu " + menuIndex + " item " + itemIndex); 
         menus[menuIndex].add(item); 
        } 
       } 
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       frame.pack(); 
       frame.setLocationRelativeTo(null); 
       frame.setVisible(true); 
      } 
     }); 
     syncAndDelay(); 
    } 

    private static void moveAndClickCenter(Component c) throws Exception { 
     moveToCenter(c); 
     click(); 
    } 

    private static void moveToCenter(final Component c) throws Exception { 
     final Point cp = new Point(); 
     SwingUtilities.invokeAndWait(new Runnable() { 

      @Override 
      public void run() { 
       Point p = new Point(c.getWidth()/2, c.getHeight()/2); 
       SwingUtilities.convertPointToScreen(p, c); 
       cp.setLocation(p); 
      } 
     }); 
     robot.mouseMove(cp.x, cp.y); 
     syncAndDelay(); 
    } 

    private static void syncAndDelay() throws Exception { 
     if (delay > 0) { 
      Thread.sleep(delay); 
     } 
     toolkit.realSync(); 
    } 

    private TestMenu() { 
    } 
} 
+0

Cảm ơn! SunToolkit.realSync chính xác là những gì tôi cần. Tuy nhiên, tôi không thể biên dịch mã của bạn vì "Giới hạn truy cập: Loại SunToolkit không thể truy cập được do hạn chế trên thư viện yêu cầu /usr/lib/jvm/ia32-java-6-sun-1.6.0.26/jre/lib/rt .jar ". Tuy nhiên, bằng cách sử dụng sự phản chiếu đã làm việc ('toolkit.getClass(). GetMethod (" realSync ") gọi (bộ công cụ)'). Bạn đã biên dịch nó theo một số cách đặc biệt? – yonran

+0

từ IDE, tôi sẽ xem xét sau này tại nhà (bây giờ từ cellmobile) – mKorbel

+0

thử nghiệm, không có vấn đề về phía tôi, chạy từ IDE, JDK6_019 ord JDK6_022 – mKorbel

2

mKorbel của câu trả lời (SunToolkit.realSync()) là đúng, nhưng realSync là chậm và ném SunToolkit.InfiniteLoop. Tôi đã sử dụng biến thể này sau khi nghiên cứu realSync:

import java.awt.Toolkit; 
import java.lang.reflect.InvocationTargetException; 
import java.lang.reflect.Method; 
import java.util.Arrays; 
import java.util.List; 

import javax.swing.SwingUtilities; 

import sun.awt.SunToolkit; 


public class ToolkitUtils { 
    private Method syncNativeQueue; 
    private boolean isSyncNativeQueueZeroArguments; 
    public ToolkitUtils() { 
     syncNativeQueue = null; 
     isSyncNativeQueueZeroArguments = true; 
     try { 
      // Since it's a protected method, we have to iterate over declared 
      // methods and setAccessible. 
      Method[] methods = SunToolkit.class.getDeclaredMethods(); 
      for (Method method: methods) { 
       String name = method.getName(); 
       if ("syncNativeQueue".equals(name)) { 
        List<Class<?>> parameterTypes = Arrays.asList(method.getParameterTypes()); 
        if (Arrays.<Class<?>>asList(long.class).equals(parameterTypes)) { 
         isSyncNativeQueueZeroArguments = false; 
        } else if (parameterTypes.isEmpty() && null == syncNativeQueue) { 
         isSyncNativeQueueZeroArguments = true; 
        } else { 
         continue; 
        } 
        syncNativeQueue = method; 
        syncNativeQueue.setAccessible(true); 
       } 
      } 
     } catch (SecurityException e) { 
      throw new RuntimeException(e); 
     } 
     if (syncNativeQueue == null) 
      throw new IllegalStateException("Could not find method SunToolkit.syncNativeQueue."); 
    } 

    /** 
    * Block until Swing has dispatched events caused by the Robot or user. 
    * 
    * <p> 
    * It is based on {@link SunToolkit#realSync()}. Use that method if you want 
    * to try to wait for everything to settle down (e.g. if an event listener 
    * calls {@link java.awt.Component#requestFocus()}, 
    * {@link SwingUtilities#invokeLater(Runnable)}, or 
    * {@link javax.swing.Timer}, realSync will block until all of those are 
    * done, or throw exception after trying). The disadvantage of realSync is 
    * that it throws {@link SunToolkit.InfiniteLoop} when the queues don't 
    * become idle after 20 tries. 
    * 
    * <p> 
    * Use this method if you only want to wait until the direct event listeners 
    * have been called. For example, if you need to simulate a user click 
    * followed by a stream input, then you can ensure that they will reach the 
    * program under test in the right order: 
    * 
    * <pre> 
    * robot.mousePress(InputEvent.BUTTON1); 
    * toolkitUtils.flushInputEvents(10000); 
    * writer.write("done with press"); 
    * </pre> 
    * 
    * @see {@link java.awt.Robot#waitForIdle()} is no good; does not wait for 
    *  OS input events to get to the Java process. 
    * @see {@link SunToolkit#realSync()} tries 20 times to wait for queues to 
    *  settle and then throws exception. In contrast, flushInputEvents does 
    *  not wait for queues to settle, just to flush what's already on them 
    *  once. 
    * @see {@link java.awt.Toolkit#sync()} flushes graphics pipeline but not 
    *  input events. 
    * 
    * @param syncNativeQueueTimeout 
    *   timeout to use for syncNativeQueue. Something like 10000 is 
    *   reasonable. 
    */ 
    public void flushInputEvents(long syncNativeQueueTimeout) { 
     SunToolkit toolkit = (SunToolkit) Toolkit.getDefaultToolkit(); 

     // 1) SunToolkit.syncNativeQueue: block until the operating system 
     // delivers Robot or user events to the process. 
     try { 
      if (isSyncNativeQueueZeroArguments) { 
       // java 1.6 
       syncNativeQueue.invoke(toolkit); 
      } else { 
       // java 1.7 
       syncNativeQueue.invoke(toolkit, syncNativeQueueTimeout); 
      } 
     } catch (IllegalArgumentException e) { 
      throw new RuntimeException(e); 
     } catch (IllegalAccessException e) { 
      throw new RuntimeException(e); 
     } catch (InvocationTargetException e) { 
      throw new RuntimeException(e); 
     } 

     // 2) SunToolkit.flushPendingEvents: block until the Toolkit thread 
     // (aka AWT-XAWT, AWT-AppKit, or AWT-Windows) delivers enqueued events 
     // to the EventQueue 
     SunToolkit.flushPendingEvents(); 

     // 3) SwingUtilities.invokeAndWait: block until the Swing thread (aka 
     // AWT-EventQueue-0) has dispatched all the enqueued input events. 
     try { 
      SwingUtilities.invokeAndWait(new Runnable(){ 
       @Override public void run() {}}); 
     } catch (InterruptedException e) { 
      throw new RuntimeException(e); 
     } catch (InvocationTargetException e) { 
      throw new RuntimeException(e); 
     } 
    } 
} 
+0

điều gì đó thú vị sai +1 – mKorbel

Các vấn đề liên quan