Eclipse外掛RCP桌面應用開發的點點滴滴

2022-10-24 18:00:24

Eclipse外掛開發的點點滴滴

新公司做的是桌面應用程式, 與之前一直在做的web頁面 ,相差甚大 。
這篇文章是寫於2022年10月底,這時在新公司已經入職了快三月。寫作目的是:國內對於eclipse外掛開發相關的檔案是少之又少,這三個月我們小組翻遍了國外檔案,勉強將軟體拼湊出並release出測試版本,為了方便同行以及自我學習,所以想把這幾個月學到的eclipse rcp外掛相關知識寫下來。

一、 Wizard部分

Wizard 一般用於嚮導式對話方塊 ,eclipse的新建專案就是一個典型的wizard 。wizard一般由幾個wizard page 組成 ,通過按鈕控制 上一頁下一頁完成取消 。

1.wizardpages

wizardpage1
package de.vogella.rcp.intro.wizards.wizard;

import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;

public class MyPageOne extends WizardPage {
    private Text text1;
    private Composite container;

    public MyPageOne() {
        super("First Page");
        setTitle("First Page");
        setDescription("Fake Wizard: First page");
    }

    @Override
    public void createControl(Composite parent) {
        container = new Composite(parent, SWT.NONE);
        GridLayout layout = new GridLayout();
        container.setLayout(layout);
        layout.numColumns = 2;
        Label label1 = new Label(container, SWT.NONE);
        label1.setText("Put a value here.");

        text1 = new Text(container, SWT.BORDER | SWT.SINGLE);
        text1.setText("");
        text1.addKeyListener(new KeyListener() {

            @Override
            public void keyPressed(KeyEvent e) {
            }

            @Override
            public void keyReleased(KeyEvent e) {
                if (!text1.getText().isEmpty()) {
                    setPageComplete(true);

                }
            }

        });
        GridData gd = new GridData(GridData.FILL_HORIZONTAL);
        text1.setLayoutData(gd);
        // required to avoid an error in the system
        setControl(container);
        setPageComplete(false);

    }

    public String getText1() {
        return text1.getText();
    }
}
wizardpage2
package de.vogella.rcp.intro.wizards.wizard;

import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;

public class MyPageTwo extends WizardPage {
    private Text text1;
    private Composite container;

    public MyPageTwo() {
        super("Second Page");
        setTitle("Second Page");
        setDescription("Now this is the second page");
        setControl(text1);
    }

    @Override
    public void createControl(Composite parent) {
        container = new Composite(parent, SWT.NONE);
        GridLayout layout = new GridLayout();
        container.setLayout(layout);
        layout.numColumns = 2;
        Label label1 = new Label(container, SWT.NONE);
        label1.setText("Say hello to Fred");

        text1 = new Text(container, SWT.BORDER | SWT.SINGLE);
        text1.setText("");
        text1.addKeyListener(new KeyListener() {

            @Override
            public void keyPressed(KeyEvent e) {
                // TODO Auto-generated method stub
            }

            @Override
            public void keyReleased(KeyEvent e) {
                if (!text1.getText().isEmpty()) {
                    setPageComplete(true);
                }
            }

        });
        GridData gd = new GridData(GridData.FILL_HORIZONTAL);
        text1.setLayoutData(gd);
        Label labelCheck = new Label(container, SWT.NONE);
        labelCheck.setText("This is a check");
        Button check = new Button(container, SWT.CHECK);
        check.setSelection(true);
        // required to avoid an error in the system
        setControl(container);
        setPageComplete(false);
    }

    public String getText1() {
        return text1.getText();
    }
}

①自定義wizardpage主要是繼承JFACE 的 WizardPage ,並重寫 createControl方法。 在createControl方法中,可以對你的嚮導頁面元件進行佈局、新增監聽等動作。
②對於當前頁面的標題、描述等資訊,可以在建構函式中通過setTitle 和 setDescription方法來設定

2.wizard

wizardpage新增好後,需要新建一個wizard類來管理它們 。

MyWizard
package de.vogella.rcp.intro.wizards.wizard;

import org.eclipse.jface.wizard.Wizard;


public class MyWizard extends Wizard {

    protected MyPageOne one;
    protected MyPageTwo two;

    public MyWizard() {
        super();
        setNeedsProgressMonitor(true);
    }

    @Override
    public String getWindowTitle() {
        return "Export My Data";
    }

    @Override
    public void addPages() {
        one = new MyPageOne();
        two = new MyPageTwo();
        addPage(one);
        addPage(two);
    }

    @Override
    public boolean performFinish() {
        // Print the result to the console
        System.out.println(one.getText1());
        System.out.println(two.getText1());

        return true;
    }
}

① 自定義wizard繼承 JFACE的 Wizard類 。
重寫addPage()方法,為嚮導新增嚮導頁。
重寫performFinish()方法,指定點選finish按鈕後完成的動作.
重寫canFinish()方法,FINISH按鈕是否可以點選,

可以通過這個方法,來判斷是否是最後一頁,最後一頁才可以點FINISH按鈕
@Override
	public boolean canFinish() {
		if (this.getContainer().getCurrentPage() instanceof FilePreprocessingWizardPage) // FilePreprocessingWizardPage為最後一個頁面
			return true;
		else
			return false;
	}

重寫getNextPage()方法, 下一頁

3.WizardDialog

wizardDialog 一般用於管理嚮導頁的按鈕,如果你想將原有的next/finish/cancel等按鈕重寫,就需要新建這個類。
下面是我專案中遇到的程式碼,需求是:最後一個頁不再顯示next按鈕,而是改為start,並執行相關功能。
第一頁finish不可點 (這個由wizard類的canfinish方法控制):

第二頁finish可以點、next 變為start

點選檢視程式碼


import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.wizard.IWizard;
import org.eclipse.jface.wizard.IWizardPage;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Button;

public class InputFileWizardDialog extends WizardDialog {
	private Button startBtn;
	private Button nextButton;

	public InputFileWizardDialog(Shell parentShell, IWizard newWizard) {
		super(parentShell, newWizard);
	}

	@Override
	protected void buttonPressed(int buttonId) {
		switch (buttonId) {
		case IDialogConstants.HELP_ID: {
			helpPressed();
			break;
		}
		case IDialogConstants.BACK_ID: {
			backPressed();
			break;
		}
		case IDialogConstants.NEXT_ID: {
			nextPressed();
			break;
		}
		case IDialogConstants.FINISH_ID: {
			finishPressed();
			break;
		}
		}
	}

	@Override
	protected void nextPressed() {
		IWizardPage currentPage = getCurrentPage();
		IWizardPage nextPage = currentPage.getNextPage();
		if (currentPage instanceof FilePreprocessingWizardPage) {
			((FilePreprocessingWizardPage) currentPage).startButtonClick();
		}
		if (nextPage instanceof FilePreprocessingWizardPage) { // last page
			if (nextPage.getControl() != null)
				nextPage.dispose();
			showPage(nextPage);
			startBtn = this.getButton(IDialogConstants.NEXT_ID);
			startBtn.setText("Start");
			startBtn.setEnabled(true);
		}
	}

	/**
	 * The Back button has been pressed.
	 */
	@Override
	protected void backPressed() {
		IWizardPage page = getCurrentPage().getPreviousPage();
		super.backPressed();
		if (!(page instanceof FilePreprocessingWizardPage)) { // last page
			nextButton = this.getButton(IDialogConstants.NEXT_ID);
			nextButton.setText(IDialogConstants.NEXT_LABEL);
		}
	}
}

①buttonPressed()方法監聽按鈕被點選後執行的方法
②nextPressed()方法,下一頁 。 這裡判斷當前頁面的下一頁是否為最後一頁,如果是則通過setTest方法將按鈕改為start按鈕,並將其設為可用狀態 。 如果當前頁面已經是最後一頁,則執行在最後一頁中定義的startbuttonclick方法 。
③ backPressed()方法,點上一頁時,將上個方法中被改變的next按鈕復原

4.最後,開啟一個wizard

一般寫在一個按鈕監聽中 , 或者選單功能裡 。

按鈕監聽:
Button button = new Button(parent, SWT.PUSH);
button.setText("Open Wizard");
button.addSelectionListener(new SelectionAdapter() {
    @Override
    public void widgetSelected(SelectionEvent e) {
        WizardDialog wizardDialog = new WizardDialog(parent.getShell(),
            new MyWizard());
        if (wizardDialog.open() == Window.OK) {
            System.out.println("Ok pressed");
        } else {
            System.out.println("Cancel pressed");
        }
    }
});
選單功能,這裡用的是E4的handle機制

import org.eclipse.e4.core.di.annotations.Execute;
import org.eclipse.ui.IWorkbench;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.widgets.Shell;

public class InputFilesAssistantHandle {
	@Execute
	public void execute(IWorkbench iWorkbench, Shell shell) {
		WizardDialog wizardDialog = new WizardDialog(shell, new MyWizard());
		WizardDialog.setDefaultImage(ApplicationContext.getImage(Constant.PLUGIN_ID, "icons/module/cn_icon.png"));
		if (wizardDialog.open() == Window.OK) {
		} else {
		}
	}
}

①可以通過setDefaultImage來設定向導的圖示

效果:

進階:

① wizardpage 的動態重新整理 、 聯動

你的wizardpages 初始化是在wizard開啟的時候, 而不是點next或back時再初始化 。 所以,如果你想將兩個wizardpage進行聯動,通過上面的程式碼難以實現 。
閱讀原始碼會發現,

原始碼

    private void updateForPage(IWizardPage page) {   
     // ensure this page belongs to the current wizard   
     if (wizard != page.getWizard()) {   
       setWizard(page.getWizard());   
      }   
     // ensure that page control has been created   
     // (this allows lazy page control creation)   
     if (page.getControl() == null) {   
         page.createControl(pageContainer);   
         // the page is responsible for ensuring the created control is accessable   
         // via getControl.   
         Assert.isNotNull(page.getControl());   
         // ensure the dialog is large enough for this page   
         updateSize(page);   
     }   
     // make the new page visible   
     IWizardPage oldPage = currentPage;   
     currentPage = page;   
     currentPage.setVisible(true);   
     if (oldPage != null) {   
       oldPage.setVisible(false);   
      }   
     // update the dialog controls   
     update();   
    }    


點next或back按鈕後,頁面之所以不會再初始化,是因為他會有個判斷page.getControl() == null,因此我們只要將想辦法在調轉到某個WizardPage的時候,將其control設定為null就可以了.
所以,在點next 或 back 按鈕時 ,可以加如下程式碼:
// 對引數頁必須重繪
IWizardPage page = getNextPage();
if (page.getControl() != null)
page.dispose();

並在你想要重新整理的頁面中重寫dispose方法:
public void dispose() {
super.dispose();

setControl(null);
}

二、未完待續。。。