为JAVA添加信号/槽支持(一)

    QT语言以信号/槽方式取代callback机制,为编程提供了灵活性,而备受赞誉。本文将介绍如何为JAVA添加信号/槽机制的支持,并给出一个有趣的使用信号/槽机制的例子。

    在QT中以connect函数注册信号/槽,使用emit函数发送信号,在我们的程序中也使用这两个函数进行信号/槽注册和信号的发送。创建类SignalSlotHandle类,内容如下:

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cn.org.liuf.framework.signalmash;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author liuf
 */
public class SignalSlotHandle {

    /**
     * 同步列表,保存注册的信号/槽信息
     */
    static List<ConnectInfo> conns = Collections.synchronizedList(new ArrayList<ConnectInfo>());

    /**
     * 同步函数,注册信号/槽
     * @param o 信号发送对象
     * @param signal 信号
     * @param ot 信号接收对象
     * @param slot 信号处理函数,使用字符串表示的函数名
     * @param c_param  信号传递的参数类数组,必须是信号处理函数实际参数类型,且顺序必须一致
     */
    public static synchronized void connect(Object o, String signal, Object ot, String slot, Class<?>... c_param) {
        ConnectInfo ci = new ConnectInfo();
        ci.setSignalObject(o);
        ci.setSlotObject(ot);
        ci.setSignal(signal);
        ci.setSlot(slot);
        ci.setParams(c_param);
        conns.add(ci);
    }

    /**
     * 同步函数,发送信号和参数
     * @param o 发送信号对象
     * @param signal 信号
     * @param args  参数对象数组,允许参数个数大于注册时的参数个数,但参数类型必须与注册时保持一致,且个数不能小于注册时个数
     */
    public static synchronized void emit(Object o, String signal, Object... args) {
        
        final Object[] argst = args;
        for (ConnectInfo ci : conns) {
            if (ci.getSignalObject() == o) {
                if (ci.getSignal().compareTo(signal) == 0) {
                    final ConnectInfo cit = ci;
                    new Thread() {      // 为使用所有处理信号的函数能够同时执行,使用线程方式调用
                        @Override
                        public void run() {
                            // 使用反射调用信号处理函数
                            try {
                                Method method;
                                method = cit.getSlotObject().getClass().getMethod(cit.getSlot(), cit.getParams());
                                int alen = argst.length;
                                int plen = cit.getParams().length;
                                Object[] t;
                                if (alen > plen) { // 用于支持信号传递的参数个数大于实际参数个数,要求相应参数类型必须相同
                                    t = Arrays.copyOf(argst, plen);
                                } else {
                                    t = argst;
                                }
                                method.invoke(cit.getSlotObject(), t);
                            } catch (IllegalAccessException ex) {
                                Logger.getLogger(SignalSlotHandle.class.getName()).log(Level.SEVERE, null, ex);
                            } catch (IllegalArgumentException ex) {
                                Logger.getLogger(SignalSlotHandle.class.getName()).log(Level.SEVERE, null, ex);
                            } catch (InvocationTargetException ex) {
                                Logger.getLogger(SignalSlotHandle.class.getName()).log(Level.SEVERE, null, ex);
                            } catch (NoSuchMethodException ex) {
                                Logger.getLogger(SignalSlotHandle.class.getName()).log(Level.SEVERE, null, ex);
                            }
                        }
                    }.start();

                }
            }
        }
    }
}

class ConnectInfo {

    Object signalObject;        // 发送信号对象
    Object slotObject;          // 接受信号对象
    String signal;              // 信号
    String slot;                // 信号处理函数
    Class<?>[] params;          // 传递的参数类型数组

    public Object getSignalObject() {
        return signalObject;
    }

    public void setSignalObject(Object signalObject) {
        this.signalObject = signalObject;
    }

    public Object getSlotObject() {
        return slotObject;
    }

    public void setSlotObject(Object slotObject) {
        this.slotObject = slotObject;
    }

    public String getSignal() {
        return signal;
    }

    public void setSignal(String signal) {
        this.signal = signal;
    }

    public String getSlot() {
        return slot;
    }

    public void setSlot(String slots) {
        this.slot = slots;
    }

    public Class<?>[] getParams() {
        return params;
    }

    public void setParams(Class<?>[] params) {
        this.params = params;
    }
}

    在信号/槽处理类中,使用辅助类ConnectInfo,一个ConnectInfo类对象表示一个信号/槽注册信息。connect信号/槽注册函数的实现很简单,其功能就是向同步列表中添加ConnectInfo对象。emit信号发送函数,在外部调用时可以认为是信号发送,但其内部实现却是执行信号相应处理函数。就是这个100行左右代码的类便可以在你的工程中添加信号/槽机制支持。下面是使用这个类实现的一个有趣的例子,先来看看运行情况。

 

    在这个例子中,模拟了一出舞台场景,场景中有4个演员A(饰演皇帝)、B(饰演忠臣1)、C(饰演忠臣2)、D(饰演奸臣)。场景的启动是由导演的一声Action开始(在程序由点击Action按钮实现),皇帝首先发出命令要求忠臣2自刎,忠臣2在2秒钟后显示出手足无措,忠臣1在4秒钟后对皇帝进行劝说,奸臣在8秒钟后对皇帝进行火上浇油的劝说,而后皇帝大怒要求他们闭嘴,然后在沉默10秒钟后皇帝发出新的命令让忠臣2继续做他的宰相。在此期间,忠臣2根据忠臣1、奸臣和皇帝的态度作出不同的反应。测试类如下:

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package test.ui;

import cn.org.liuf.framework.signalmash.SignalSlotHandle;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author liuf
 */
public class TestJFrame extends javax.swing.JFrame {

    private int sayed = 0;
    private boolean worried = true;

    /**
     * Creates new form TestJFrame
     */
    public TestJFrame() {
        initComponents();
        buttonGroup1.add(this.cExplainRadioButton);
        buttonGroup1.add(this.cKillRadioButton);
        SignalSlotHandle.connect(this, "action", this, "aAct", new Class[]{});                             // 导演说开始
        SignalSlotHandle.connect(this, "action", this, "bAct", new Class[]{});                             // 导演说开始
        SignalSlotHandle.connect(this, "action", this, "cAct", new Class[]{});                              // 导演说开始
        SignalSlotHandle.connect(this, "action", this, "dAct", new Class[]{});                              // 导演说开始
        SignalSlotHandle.connect(this, "action", this, "directAct", new Class[]{});

        SignalSlotHandle.connect(this, "bdShutup", this, "bInRole", String.class);        // 皇帝演员让忠臣1闭嘴, 将信号通过参数String传递,以便槽函数根据信号作出处理
        SignalSlotHandle.connect(this, "bdShutup", this, "dInRole", String.class);         // 皇帝演员让奸臣1闭嘴,String参数同上
        SignalSlotHandle.connect(this, "bdShutup", this, "cInRole", String.class, Boolean.class); // 皇帝发火了,忠臣2忐忑中,String参数同上,boolean用于设置忐忑状态, ·始终是true(表示进入忐忑状态)
        SignalSlotHandle.connect(this.bjLabel, "sayed", this, "cInRole", String.class, Boolean.class);        //  忠臣1演员劝说完, 忠臣2演员选择辨解,boolean类型变量用于标识忠臣1(true)和奸臣1(false),String参数同上
        SignalSlotHandle.connect(this.djLabel, "sayed", this, "cInRole", String.class, Boolean.class);        //  奸臣1演员火上浇油完,忠臣2选择遵从命令(自杀),String参数同上
        SignalSlotHandle.connect(this, "bdsayed", this, "aInRole", String.class);                  // 忠臣1和奸臣1说完话后,皇帝大怒,发送“bdShutup"信号,String参数同上

        SignalSlotHandle.connect(this, "newcommond", this, "bInRole", String.class);        // 皇帝发布新的命令,不杀忠臣2
        SignalSlotHandle.connect(this, "newcommond", this, "cInRole", String.class, Boolean.class); // boolean类型始终为false
        SignalSlotHandle.connect(this, "newcommond", this, "dInRole", String.class);
    }

    public void aInRole(String signal) {
        if (signal != null) {
            if (signal.compareTo("bdsayed") == 0) {
                this.aTextField.setText("你们闭嘴");
                SignalSlotHandle.emit(this, "bdShutup", "bdShutup", true);
                this.directjTextArea.setText("皇帝发火了!!大家沉默中,\n10秒后皇帝发布新的命令");
                try {
                    TimeUnit.SECONDS.sleep(10);
                } catch (InterruptedException ex) {
                    Logger.getLogger(TestJFrame.class.getName()).log(Level.SEVERE, null, ex);
                }
                this.aTextField.setText("忠臣2,你继续做你的宰相吧");
                SignalSlotHandle.emit(this, "newcommond", "newcommond", false);
                this.directjTextArea.setText("场景剧本结束");
            }
        }
    }

    public void bInRole(String signal) {
        if (signal != null) {
            if (signal.compareTo("bdShutup") == 0) {
                this.bTextField.setText("我沉默中...");
            }else if(signal.compareTo("newcommond") == 0){
                this.bTextField.setText("高兴中...");
            }
        }
    }

    public void cInRole(String signal, Boolean isGood) {
        worried = isGood;
        if (signal == null) {
            this.cTextField.setText("不知道接收的是什么信号");
            return;
        }
        if (signal.compareTo("sayed") == 0 && isGood) {
            this.cTextField.setText("我要为自己辨解");
            this.cExplainRadioButton.setSelected(true);
        } else if (signal.compareTo("sayed") == 0 && !isGood) {
            this.cTextField.setText("皇帝下命令了,我就要死了");
            this.cKillRadioButton.setSelected(true);
        } else if (signal.compareTo("bdShutup") == 0) {
            this.cTextField.setText("我十分的忐忑啊");
            
            while (this.worried) {
                this.cExplainRadioButton.doClick();
                try {
                    TimeUnit.MILLISECONDS.sleep(100);
                } catch (InterruptedException ex) {
                    Logger.getLogger(TestJFrame.class.getName()).log(Level.SEVERE, null, ex);
                }
                this.cKillRadioButton.doClick();
            }
        }else if(signal.compareTo("newcommond") == 0){
            this.cTextField.setText("好险啊,还好皇帝放过了我");
            this.cExplainRadioButton.setSelected(false);
        }
    }

    public void dInRole(String signal) {
        if (signal != null) {
            if (signal.compareTo("bdShutup") == 0) {
                this.dTextField.setText("我沉默中...");
            }else if(signal.compareTo("newcommond") == 0){
                this.dTextField.setText("郁闷中...");
            }
        }
    }

    public void directAct() {
        this.directjTextArea.setText("皇帝发布命令,忠臣1 4秒\n后动作,忠臣2 2秒后动\n作,奸臣1 8秒后动作 ");
    }

    public void aAct() {
        this.aTextField.setText("忠臣2,你自刎吧");
    }

    public void bAct() {
        this.bTextField.setText("开演了,进入角色");
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException ex) {
            Logger.getLogger(TestJFrame.class.getName()).log(Level.SEVERE, null, ex);
        }
        this.bTextField.setText("劝说皇帝,不杀忠臣2");
        SignalSlotHandle.emit(this.bjLabel, "sayed", "sayed", true);
         try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException ex) {
            Logger.getLogger(TestJFrame.class.getName()).log(Level.SEVERE, null, ex);
        }
        sayed++;
        if (sayed == 2) {
            SignalSlotHandle.emit(this, "bdsayed", "bdsayed");
            sayed = 0;
        }
    }

    public void cAct() {
        this.cTextField.setText("开演了,进入角色");
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException ex) {
            Logger.getLogger(TestJFrame.class.getName()).log(Level.SEVERE, null, ex);
        }
        this.cTextField.setText("心理独白:我该怎么办");
    }

    public void dAct() {
        this.dTextField.setText("开演了,进入角色");
        try {
            TimeUnit.SECONDS.sleep(8);
        } catch (InterruptedException ex) {
            Logger.getLogger(TestJFrame.class.getName()).log(Level.SEVERE, null, ex);
        }
        this.dTextField.setText("火上浇油,杀了忠臣2");
        SignalSlotHandle.emit(this.djLabel, "sayed", "sayed", false);
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException ex) {
            Logger.getLogger(TestJFrame.class.getName()).log(Level.SEVERE, null, ex);
        }
        sayed++;
        if (sayed == 2) {
            SignalSlotHandle.emit(this, "bdsayed", "bdsayed");
            sayed = 0;
        }
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">
    private void initComponents() {

        buttonGroup1 = new javax.swing.ButtonGroup();
        actionButton = new javax.swing.JButton();
        aTextField = new javax.swing.JTextField();
        jLabel1 = new javax.swing.JLabel();
        ajLabel = new javax.swing.JLabel();
        bTextField = new javax.swing.JTextField();
        bjLabel = new javax.swing.JLabel();
        cjLabel = new javax.swing.JLabel();
        cExplainRadioButton = new javax.swing.JRadioButton();
        cKillRadioButton = new javax.swing.JRadioButton();
        dTextField = new javax.swing.JTextField();
        djLabel = new javax.swing.JLabel();
        cTextField = new javax.swing.JTextField();
        jScrollPane1 = new javax.swing.JScrollPane();
        directjTextArea = new javax.swing.JTextArea();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        actionButton.setText("Action");
        actionButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                actionButtonMouseClicked(evt);
            }
        });

        aTextField.setText("我要开演");

        jLabel1.setText("导演");

        ajLabel.setText("演员A(皇帝)");

        bjLabel.setText("演员B(忠臣1)");

        cjLabel.setText("演员C(忠臣2)");

        cExplainRadioButton.setText("辩解");

        cKillRadioButton.setText("遵从命令");

        djLabel.setText("演员D(奸臣)");

        directjTextArea.setEditable(false);
        directjTextArea.setColumns(20);
        directjTextArea.setRows(5);
        directjTextArea.setText("由我安排");
        directjTextArea.setAutoscrolls(false);
        directjTextArea.setName(""); // NOI18N
        jScrollPane1.setViewportView(directjTextArea);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addGroup(layout.createSequentialGroup()
                                .addGap(39, 39, 39)
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                    .addComponent(actionButton)
                                    .addComponent(jLabel1)))
                            .addGroup(layout.createSequentialGroup()
                                .addContainerGap()
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                    .addComponent(djLabel)
                                    .addComponent(dTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 146, javax.swing.GroupLayout.PREFERRED_SIZE))))
                        .addGap(0, 0, Short.MAX_VALUE))
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                        .addGap(0, 12, Short.MAX_VALUE)
                        .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 185, javax.swing.GroupLayout.PREFERRED_SIZE)))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                    .addComponent(cKillRadioButton)
                    .addComponent(cExplainRadioButton)
                    .addComponent(cjLabel)
                    .addComponent(aTextField)
                    .addComponent(ajLabel)
                    .addComponent(bTextField)
                    .addComponent(bjLabel)
                    .addComponent(cTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 173, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGap(30, 30, 30))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jLabel1)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(actionButton)
                        .addGap(10, 10, 10)
                        .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(ajLabel)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(aTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addComponent(bjLabel)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(bTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGap(18, 18, 18)
                        .addComponent(cjLabel)
                        .addGap(2, 2, 2)
                        .addComponent(cTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(cExplainRadioButton)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(cKillRadioButton)
                        .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                    .addGroup(layout.createSequentialGroup()
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addComponent(djLabel)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(dTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGap(78, 78, 78))))
        );

        pack();
    }// </editor-fold>

    private void actionButtonMouseClicked(java.awt.event.MouseEvent evt) {
        SignalSlotHandle.emit(this, "action", new Object[]{});
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(TestJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(TestJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(TestJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(TestJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new TestJFrame().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify
    private javax.swing.JTextField aTextField;
    private javax.swing.JButton actionButton;
    private javax.swing.JLabel ajLabel;
    private javax.swing.JTextField bTextField;
    private javax.swing.JLabel bjLabel;
    private javax.swing.ButtonGroup buttonGroup1;
    private javax.swing.JRadioButton cExplainRadioButton;
    private javax.swing.JRadioButton cKillRadioButton;
    private javax.swing.JTextField cTextField;
    private javax.swing.JLabel cjLabel;
    private javax.swing.JTextField dTextField;
    private javax.swing.JTextArea directjTextArea;
    private javax.swing.JLabel djLabel;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JScrollPane jScrollPane1;
    // End of variables declaration
}

    只需要关注这个类上部分,此类界面由Netbeans自动生成。使用信号/槽机制,首先是注册信号/槽对,在构造函数完成。

 SignalSlotHandle.connect(this, "action", this, "aAct", new Class[]{});                             // 导演说开始
        SignalSlotHandle.connect(this, "action", this, "bAct", new Class[]{});                             // 导演说开始
        SignalSlotHandle.connect(this, "action", this, "cAct", new Class[]{});                              // 导演说开始
        SignalSlotHandle.connect(this, "action", this, "dAct", new Class[]{});                              // 导演说开始
        SignalSlotHandle.connect(this, "action", this, "directAct", new Class[]{});

        SignalSlotHandle.connect(this, "bdShutup", this, "bInRole", String.class);        // 皇帝演员让忠臣1闭嘴, 将信号通过参数String传递,以便槽函数根据信号作出处理
        SignalSlotHandle.connect(this, "bdShutup", this, "dInRole", String.class);         // 皇帝演员让奸臣1闭嘴,String参数同上
        SignalSlotHandle.connect(this, "bdShutup", this, "cInRole", String.class, Boolean.class); // 皇帝发火了,忠臣2忐忑中,String参数同上,boolean用于设置忐忑状态, ·始终是true(表示进入忐忑状态)
        SignalSlotHandle.connect(this.bjLabel, "sayed", this, "cInRole", String.class, Boolean.class);        //  忠臣1演员劝说完, 忠臣2演员选择辨解,boolean类型变量用于标识忠臣1(true)和奸臣1(false),String参数同上
        SignalSlotHandle.connect(this.djLabel, "sayed", this, "cInRole", String.class, Boolean.class);        //  奸臣1演员火上浇油完,忠臣2选择遵从命令(自杀),String参数同上
        SignalSlotHandle.connect(this, "bdsayed", this, "aInRole", String.class);                  // 忠臣1和奸臣1说完话后,皇帝大怒,发送“bdShutup"信号,String参数同上

        SignalSlotHandle.connect(this, "newcommond", this, "bInRole", String.class);        // 皇帝发布新的命令,不杀忠臣2
        SignalSlotHandle.connect(this, "newcommond", this, "cInRole", String.class, Boolean.class); // boolean类型始终为false
        SignalSlotHandle.connect(this, "newcommond", this, "dInRole", String.class);

    当点击Action按钮时,当前类发出"action"信号,调用相应槽函数aAct、bAct、cAct、dAct。首先皇帝角色发出忠臣2自刎的命令(aAct),其他角色分别等待设定时间。之后忠臣2角色表现无助(cAct);忠臣1角色进行劝说(bAct),发出"sayed"信号,忠臣2角色接收后决定进行辩解(cInRole);奸臣角色进行火上浇油的劝说(dAct),发出"sayed"信号,忠臣2角色接收后决定遵从命令自杀(cInRole)。在bAct、dAct槽函数有个计数,用于当忠臣1和奸臣都在说时,发出"bdsayed"信号给皇帝,皇帝角色命令他们闭嘴(发出信号"bdShutup"),忠臣1和奸臣收到信号后保持沉默(bInRole和dInRole),忠臣2收到信号后表现出忐忑不安(cInRole)。皇帝角色在大家沉默10秒后,发布新命令(信号"newcommond")赦免忠臣2。忠臣1、忠臣2、奸臣分别表现不同的反应(bInRole、cInRole、dInRole)。   

     在程序中有个boolean类型变量worried,用于控制忠臣2角色是否表现出忐忑不安,当worried为true时,界面上“辩解”和“遵从命令”单选按钮不停交替选中,以表现忐忑不安的状态。设置这个变量是因为每次函数调用都是在独立的线程中执行,第二次调用并不影响第一次调用,所以使用一个“全局”量控制忐忑不安状态时调用函数的结束。

问题:

1、 由于信号/槽处理类为静态类,从包加载起对象就已经存在,直到JVM销毁。所以同步列表需要实时维护,当注册的对象不存在时应该及时的清理列表,以减少资源占用。

2、 在emit函数中使用Thread类进行线程创建,存在与Swing图形组件的兼容性问题。

3、 例子程序在反复点击Action按钮时,会抛出InvocationTargetException异常和ClassCastException异常(java.lang.Character cannot be cast to javax.swing.Painter)。异常的发生通常在cInRole函数中的这个循环中。当减小此循环的持续时间时,异常出现的概率明显减小。

while (this.worried) {
                this.cExplainRadioButton.doClick();
                try {
                    TimeUnit.MILLISECONDS.sleep(100);
                } catch (InterruptedException ex) {
                    Logger.getLogger(TestJFrame.class.getName()).log(Level.SEVERE, null, ex);
                }
                this.cKillRadioButton.doClick();
            }


 

好的,下面是一个简单的示例程序: 首先,我们需要在Qt中使用Java Native Interface(JNI)来调用Java代码。在Qt中,我们可以使用QAndroidJniObject类来实现这一点。 接下来,我们需要使用Android SDK中的ZXing库来实现二维码扫描。为了使用ZXing,我们需要在Qt中导入ZXing库的Java代码。 因此,我们需要在Qt工程中添加以下文件: 1. ZXing Android库文件 将ZXing库的Android代码导入到Qt工程中。这可以通过将zxing-core-3.3.3.jar文件(或最新版本)复制到Qt工程的android/libs目录中来实现。 2. Java代码 ``` package com.example.qrcode; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; import com.google.zxing.BarcodeFormat; import com.google.zxing.DecodeHintType; import com.google.zxing.Result; import com.google.zxing.client.android.BeepManager; import com.google.zxing.client.android.CaptureActivityHandler; import com.google.zxing.client.android.DecodeFormatManager; import com.google.zxing.client.android.DecodeHintManager; import com.google.zxing.client.android.InactivityTimer; import com.google.zxing.client.android.IntentSource; import com.google.zxing.client.android.PreferencesActivity; import com.google.zxing.client.android.ResultHandler; import com.google.zxing.client.android.ResultHandlerFactory; import com.google.zxing.client.android.camera.CameraManager; import com.google.zxing.client.android.result.ResultHandler; import com.google.zxing.client.android.result.ResultHandlerFactory; import java.io.IOException; import java.util.ArrayList; import java.util.EnumMap; import java.util.List; import java.util.Map; public class ScanActivity extends Activity implements SurfaceHolder.Callback { private static final String TAG = ScanActivity.class.getSimpleName(); private CaptureActivityHandler handler; private CameraManager cameraManager; private Result savedResultToShow; private ViewfinderView viewfinderView; private TextView statusView; private Button buttonBack; private BeepManager beepManager; private InactivityTimer inactivityTimer; private List<BarcodeFormat> decodeFormats; private Map<DecodeHintType, ?> decodeHints; private String characterSet; private IntentSource source; /** * 当活动首次创建时调用。 */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_scan); // 初始化相机管理器 cameraManager = new CameraManager(getApplication()); viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view); statusView = (TextView) findViewById(R.id.status_view); buttonBack = (Button) findViewById(R.id.button_back); beepManager = new BeepManager(this); inactivityTimer = new InactivityTimer(this); // 显示返回按钮 buttonBack.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { finish(); } }); } /** * 当活动已经可见时调用。 */ @Override public void onResume() { super.onResume(); handler = null; savedResultToShow = null; // 初始化相机视图 SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view); SurfaceHolder surfaceHolder = surfaceView.getHolder(); if (surfaceHolder == null) { throw new IllegalStateException("No SurfaceHolder?"); } if (cameraManager.isOpen()) { Log.w(TAG, "initCamera() while already open -- late SurfaceView callback?"); return; } try { cameraManager.openDriver(surfaceHolder); // 创建捕获活动处理程序 if (handler == null) { handler = new CaptureActivityHandler(this, decodeFormats, decodeHints, characterSet, cameraManager); } decodeOrStoreSavedBitmap(null, null); } catch (IOException ioe) { Log.w(TAG, ioe); displayFrameworkBugMessageAndExit(); } catch (RuntimeException e) { Log.w(TAG, "Unexpected error initializing camera", e); displayFrameworkBugMessageAndExit(); } // 启动电源管理器和闪光灯 beepManager.updatePrefs(); inactivityTimer.onResume(); source = IntentSource.NONE; decodeFormats = null; characterSet = null; } /** * 当活动不再可见时调用。 */ @Override public void onPause() { if (handler != null) { handler.quitSynchronously(); handler = null; } inactivityTimer.onPause(); beepManager.close(); cameraManager.closeDriver(); if (!hasSurface) { SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view); SurfaceHolder surfaceHolder = surfaceView.getHolder(); surfaceHolder.removeCallback(this); } super.onPause(); } /** * 当活动被销毁时调用。 */ @Override public void onDestroy() { inactivityTimer.shutdown(); super.onDestroy(); } /** * 处理扫描结果。 * * @param rawResult 扫描结果 * @param barcode 扫描结果的位图 */ public void handleDecode(Result rawResult, com.google.zxing.Result barcode) { inactivityTimer.onActivity(); beepManager.playBeepSoundAndVibrate(); String result = barcode.getText(); Toast.makeText(this, result, Toast.LENGTH_SHORT).show(); // 在状态视图中显示结果 statusView.setText(barcode.getText()); // 将结果保存并显示 savedResultToShow = rawResult; ResultHandler resultHandler = ResultHandlerFactory.makeResultHandler(this, barcode); String displayContents = resultHandler.getDisplayContents(); if (displayContents != null) { statusView.setText(displayContents); } // 将扫描结果返回给Qt应用 QAndroidJniObject jResult = QAndroidJniObject::fromString(result); QAndroidJniObject::callStaticMethod<void>("com/example/qrcode/ScanActivity", "onScanResult", "(Ljava/lang/String;)V", jResult.object<jstring>()); } /** * 在UI线程上显示有关框架错误的消息,并退出应用程序。 */ private void displayFrameworkBugMessageAndExit() { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.app_name)); builder.setMessage(getString(R.string.msg_camera_framework_bug)); builder.setPositiveButton(R.string.button_ok, new FinishListener(this)); builder.setOnCancelListener(new FinishListener(this)); builder.show(); } /** * 将扫描结果返回给Qt应用。 */ public static void onScanResult(QString result) { emit scanResult(result); } } ``` 3. Qt代码 ``` #include <QtAndroidExtras> #include <QAndroidJniObject> #include <QDebug> ... // 在Android上扫描二维码 void MainWindow::scanQRCode() { QAndroidJniObject::callStaticMethod<void>("com/example/qrcode/ScanActivity", "startScan", "()V"); } // 处理扫描结果 void MainWindow::onScanResult(QString result) { qDebug() << "Scan result:" << result; } // 处理扫描结果信号 void MainWindow::handleScanResult() { QAndroidJniEnvironment env; if (env->ExceptionCheck()) { env->ExceptionClear(); return; } QAndroidJniObject jResult = QAndroidJniObject::callStaticObjectMethod("com/example/qrcode/ScanActivity", "getResult", "()Ljava/lang/String;"); QString result = jResult.toString(); if (result.isEmpty()) { return; } onScanResult(result); } ... // 连接到扫描结果信号 connect(this, SIGNAL(scanResult(QString)), this, SLOT(onScanResult(QString))); // 请求获取Android权限 QtAndroid::PermissionResultCallback callback = [](const QtAndroid::PermissionResult &result) { if (result == QtAndroid::PermissionResult::Granted) { // 已授权 scanQRCode(); } else { // 未授权 qDebug() << "Permission denied!"; } }; QtAndroid::requestPermissionsSync(QStringList() << "android.permission.CAMERA", callback); ... ``` 这个程序将会请求获取相机权限,然后启动扫描二维码的Activity。扫描结果将被传递回Qt应用程序中的函数中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fengdavid

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值