SIGNAL-SLOT是Qt的一大特色,使用起来十分方便。在传统的AWT和Swing编程中,我们都是为要在
public static void connect(QObject sender, String signal, Object receiver, String slot) { if (sender.signalSlotMap == null) sender.signalSlotMap = new HashMap<String, List<ReceiverSlot>>(); List<ReceiverSlot> slotList = sender.signalSlotMap.get(signal); if (slotList == null) { slotList = new LinkedList<ReceiverSlot>(); sender.signalSlotMap.put(signal, slotList); } slotList.add(createReceiverSlot(receiver, slot)); }
static class ReceiverSlot { Object receiver; Method slot; Object[] args; }
private static ReceiverSlot createReceiverSlot(Object receiver, String slot) { ReceiverSlot receiverSlot = new ReceiverSlot(); receiverSlot.receiver = receiver; Pattern pattern = Pattern.compile("(\\w+)\\(([\\w+,]*)\\)"); Matcher matcher = pattern.matcher(slot); if (matcher.matches() && matcher.groupCount() == 2) { // 1.Connect SIGNAL to SLOT try { String methodName = matcher.group(1); String argStr = matcher.group(2); ArrayList<String> argList = new ArrayList<String>(); pattern = Pattern.compile("\\w+"); matcher = pattern.matcher(argStr); while (matcher.find()) argList.add(matcher.group()); String[] arguments = argList.toArray(new String[0]); receiverSlot.slot = findMethod(receiver, methodName, arguments); receiverSlot.args = new Object[0]; } catch (Exception e) { e.printStackTrace(); } } else { // 2.Connect SIGNAL to SIGNAL if (receiver instanceof QObject) { receiverSlot.slot = emitMethod; receiverSlot.args = new Object[] { slot }; } } return receiverSlot; }
private static Method emitMethod; protected Map<String, List<ReceiverSlot>> signalSlotMap; static { try { emitMethod = QObject.class.getDeclaredMethod("emit", String.class, Object[].class); } catch (Exception e) { e.printStackTrace(); } }
private static Method findMethod(Object receiver, String methodName, String[] arguments) throws NoSuchMethodException { Method slotMethod = null; if (arguments.length == 0) slotMethod = receiver.getClass().getMethod(methodName, new Class[0]); else { for (Method method : receiver.getClass().getMethods()) { // 1.Check method name if (!method.getName().equals(methodName)) continue; // 2.Check parameter number Class<?>[] paramTypes = method.getParameterTypes(); if (paramTypes.length != arguments.length) continue; // 3.Check parameter type boolean isMatch = true; for (int i = 0; i < paramTypes.length; i++) { if (!paramTypes[i].getSimpleName().equals(arguments[i])) { isMatch = false; break; } } if (isMatch) { slotMethod = method; break; } } if (slotMethod == null) throw new NoSuchMethodException("Cannot find method[" + methodName + "] with parameters: " + Arrays.toString(arguments)); } return slotMethod; }
protected void emit(String signal, Object... args) { System.out.println(getClass().getSimpleName() + " emit signal " + signal); if (signalSlotMap == null) return; List<ReceiverSlot> slotList = signalSlotMap.get(signal); if (slotList == null || slotList.isEmpty()) return; for (ReceiverSlot objSlot : slotList) { try { if (objSlot.slot == emitMethod) objSlot.slot.invoke(objSlot.receiver, objSlot.args[0], args); else objSlot.slot.invoke(objSlot.receiver, args); } catch (Exception e) { e.printStackTrace(); } } }
public class QWidget<T extends JComponent> extends QObject implements QSwing<T> { protected T widget; public QWidget(Class<T> clazz) { try { widget = clazz.newInstance(); } catch (Exception e) { e.printStackTrace(); } } @Override public T getSwingWidget() { return this.widget; } }
public class QPushButton extends QWidget<JButton> { public static final String CLICKED = "clicked"; public QPushButton(String text) { super(JButton.class); widget.setText(text); widget.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { emit(CLICKED); } }); } }
public class QLineEdit extends QWidget<JTextField> { public static final String RETURN_PRESSED = "returnPressed"; public QLineEdit() { super(JTextField.class); widget.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { emit(RETURN_PRESSED); } }); } }
public class AddressBar extends QWidget<JPanel> { /** * SIGNAL */ public static final String NEW_BUTTON_CLICKED = "newButtonClicked"; public static final String GO_TO_ADDRESS = "goToAddress(String,String)"; /** * SLOT */ public static final String HANDLE_GO_TO_ADDRESS = "handleGoToAddress()"; private QPushButton newButton; private QLineEdit addressEdit; private QPushButton goButton; public AddressBar() { super(JPanel.class); // 1.Create widget newButton = new QPushButton("New"); addressEdit = new QLineEdit(); goButton = new QPushButton("Go"); // 2.Set property addressEdit.getSwingWidget().setColumns(10); // 3.Connect signal-slot connect(newButton, QPushButton.CLICKED, this, NEW_BUTTON_CLICKED); connect(addressEdit, QLineEdit.RETURN_PRESSED, this, HANDLE_GO_TO_ADDRESS); connect(goButton, QPushButton.CLICKED, this, HANDLE_GO_TO_ADDRESS); // 4.Add to layout getSwingWidget().add(newButton.getSwingWidget()); getSwingWidget().add(addressEdit.getSwingWidget()); getSwingWidget().add(goButton.getSwingWidget()); } public void handleGoToAddress() { emit(GO_TO_ADDRESS, addressEdit.getSwingWidget().getText(), "test string"); } }
public class TabBar extends JTabbedPane { /** * SLOT */ public static final String HANDLE_NEW_TAB = "handleNewTab()"; public static final String HANDLE_GO_TO_SITE = "goToSite(String,String)"; public TabBar() { handleNewTab(); } public void handleNewTab() { WebView tab = new WebView(); add("blank", tab); } public void goToSite(String url, String testStr) { System.out.println("Receive url: " + url + ", " + testStr); WebView tab = (WebView) getSelectedComponent(); tab.load(url); } }
public class MainWindow extends JFrame { public static void main(String[] args) { JFrame window = new MainWindow(); window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); window.setSize(320, 340); window.setVisible(true); } public MainWindow() { // 1.Create widget AddressBar addressBar = new AddressBar(); TabBar tabBar = new TabBar(); // 2.Set property // 3.Connect signal-slot QObject.connect(addressBar, AddressBar.NEW_BUTTON_CLICKED, tabBar, TabBar.HANDLE_NEW_TAB); QObject.connect(addressBar, AddressBar.GO_TO_ADDRESS, tabBar, TabBar.HANDLE_GO_TO_SITE); // 4.Add to layout GridBagLayout layout = new GridBagLayout(); setLayout(layout); GridBagConstraints grid = new GridBagConstraints(); grid.fill = GridBagConstraints.BOTH; grid.gridx = grid.gridy = 0; grid.weightx = 1.0; grid.weighty = 0.1; add(addressBar.getSwingWidget(), grid); grid.fill = GridBagConstraints.BOTH; grid.gridx = 0; grid.gridy = 1; grid.weightx = 1.0; grid.weighty = 0.9; add(tabBar, grid); } } @SuppressWarnings("serial") class WebView extends JEditorPane { public WebView() { setEditable(false); } public void load(final String url) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { try { WebView.this.setPage(url); } catch (IOException e) { e.printStackTrace(); } } }); } }