Adding+tabs+in+the+Explorer

toc

= Description = This article explains how to add extra tabs in the Explorer in order to add new functionality without the hassle of having to dig into the Explorer code oneself. With the new plugin-architecture of the Explorer it is fairly easy making your extensions available in the GUI.


 * Note:** This is also covered in chapter //Extending WEKA// of the WEKA manual in versions later than 3.6.1/3.7.0 or snapshots of the stable-3.6/developer version later than 10/01/2010.

= Version = >3.5.5 or Snapshot

= Requirements = Here is roughly what is required in order to add a new tab (the examples go into more detail): >> in case you want to take advantage of the logging in the Explorer >> in case your class needs to be notified of changes in the Capabilities, e.g., if new data is loaded into the Explorer
 * your class must be derived from
 * your class must implemented the interface
 * optional interfaces
 * adding the classname of your class to the //Tabs// property in the file

= Examples = The following examples demonstrate the new plugin architecture (a bold term for such a simple extension mechanism). Only the necessary details are discussed, as the full source code is available for download as well.

Purpose
Displaying the **SqlViewer** as a tab in the Explorer instead of using it either via the //Open DB...// button or as standalone application. Uses the existing components already available in Weka and just assembles them in a. Since this tab does not rely on a dataset being loaded into the Explorer, it will be used as a //standalone// one.

Useful for people who are working a lot with databases and would like to have an SQL worksheet available all the time instead of clicking on a button every time to open up a database dialog.

Implementation
code format="java" public class SqlPanel extends JPanel implements ExplorerPanel { code code format="java" /** the parent frame */ protected Explorer m_Explorer = null; /** sends notifications when the set of working instances gets changed*/ protected PropertyChangeSupport m_Support = new PropertyChangeSupport(this); code code format="java" /** Sets the Explorer to use as parent frame */ public void setExplorer(Explorer parent) { m_Explorer = parent; } /** returns the parent Explorer frame */ public Explorer getExplorer { return m_Explorer; } /** Returns the title for the tab in the Explorer */ public String getTabTitle { return "SQL"; // what's displayed as tab-title, e.g., //Classify// } /** Returns the tooltip for the tab in the Explorer */ public String getTabTitleToolTip { return "Retrieving data from databases"; // the tooltip of the tab } /** ignored, since we //"generate"// data and not receive it */ public void setInstances(Instances inst) { } /** PropertyChangeListener who will be notified of value changes. */ public void addPropertyChangeListener(PropertyChangeListener l) { m_Support.addPropertyChangeListener(l); } /** Removes a PropertyChangeListener. */ public void removePropertyChangeListener(PropertyChangeListener l) { m_Support.removePropertyChangeListener(l); } code code format="java" /** the actual SQL worksheet */ protected SqlViewer m_Viewer; /** the panel for the buttons */ protected JPanel m_PanelButtons; /** the Load button - makes the data available in the Explorer */ protected JButton m_ButtonLoad = new JButton("Load data"); /** displays the current query */ protected JLabel m_LabelQuery = new JLabel(""); code code format="java" m_ButtonLoad.addActionListener(new ActionListener {     public void actionPerformed(ActionEvent evt){        m_Support.firePropertyChange("", null, null);      }    }); code code format="java" addPropertyChangeListener(new PropertyChangeListener {   public void propertyChange(PropertyChangeEvent e) {      try {        // load data        InstanceQuery query = new InstanceQuery;        query.setDatabaseURL(m_Viewer.getURL);        query.setUsername(m_Viewer.getUser);        query.setPassword(m_Viewer.getPassword);        Instances data = query.retrieveInstances(m_Viewer.getQuery);        // set data in preprocess panel (will also notify of capabilties changes)        getExplorer.getPreprocessPanel.setInstances(data);      }      catch (Exception ex) {        ex.printStackTrace;      }    }  }); code code format="text" Tabs=weka.gui.explorer.SqlPanel,\ weka.gui.explorer.ClassifierPanel,\ weka.gui.explorer.ClustererPanel,\ weka.gui.explorer.AssociationsPanel,\ weka.gui.explorer.AttributeSelectionPanel,\ weka.gui.explorer.VisualizePanel code
 * class is derived from and implements the  interface (the full source code also imports the  interface, but that is only additional functionality):
 * some basic members that we need to have
 * methods we need to implement due to the used interfaces
 * additional GUI elements
 * loading the data into the Explorer by clicking on the //Load// button will fire a property change:
 * the //propertyChange// event will perform the actual loading of the data, hence we add an anonymous property change listener to our panel:
 * In order to add our to the list of tabs displayed in the Explorer, we need to modify the  file (just extract it from the  and place it in your home directory). The //Tabs// property must look like this:

Source

 * [[file:SqlPanel.java]] ([|stable-3.6], [|developer])

Purpose
Instead of only having a //Generate...// button in the PreprocessPanel or using it from commandline, this example creates a new panel to be displayed as extra tab in the Explorer. This tab will be available regardless whether a dataset is already loaded or not (= //standalone//).

Implementation
code format="java" public class GeneratorPanel extends JPanel implements ExplorerPanel { code code format="java" /** the parent frame */ protected Explorer m_Explorer = null; /** sends notifications when the set of working instances gets changed*/ protected PropertyChangeSupport m_Support = new PropertyChangeSupport(this); code code format="java" /** Sets the Explorer to use as parent frame */ public void setExplorer(Explorer parent) { m_Explorer = parent; } /** returns the parent Explorer frame */ public Explorer getExplorer { return m_Explorer; } /** Returns the title for the tab in the Explorer */ public String getTabTitle { return "DataGeneration"; // what's displayed as tab-title, e.g., Classify } /** Returns the tooltip for the tab in the Explorer */ public String getTabTitleToolTip { return "Generating artificial datasets"; // the tooltip of the tab } /** ignored, since we "generate" data and not receive it */ public void setInstances(Instances inst) { } /** PropertyChangeListener who will be notified of value changes. */ public void addPropertyChangeListener(PropertyChangeListener l) { m_Support.addPropertyChangeListener(l); } /** Removes a PropertyChangeListener. */ public void removePropertyChangeListener(PropertyChangeListener l) { m_Support.removePropertyChangeListener(l); } code code format="java" /** the GOE for the generators */ protected GenericObjectEditor m_GeneratorEditor = new GenericObjectEditor; /** the text area for the output of the generated data */ protected JTextArea m_Output = new JTextArea; /** the Generate button */ protected JButton m_ButtonGenerate = new JButton("Generate"); /** the Use button */ protected JButton m_ButtonUse = new JButton("Use"); code code format="java" m_ButtonGenerate.addActionListener(new ActionListener{   public void actionPerformed(ActionEvent evt){      DataGenerator generator = (DataGenerator) m_GeneratorEditor.getValue;      String relName = generator.getRelationName;      String cname = generator.getClass.getName.replaceAll(".*\\.", "");      String cmd = generator.getClass.getName;      if (generator instanceof OptionHandler)        cmd += " " + Utils.joinOptions(((OptionHandler) generator).getOptions);      try {        // generate data        StringWriter output = new StringWriter;        generator.setOutput(new PrintWriter(output));        DataGenerator.makeData(generator, generator.getOptions);        m_Output.setText(output.toString);      }      catch (Exception ex) {        ex.printStackTrace;        JOptionPane.showMessageDialog( getExplorer, "Error generating data:\n" + ex.getMessage, "Error", JOptionPane.ERROR_MESSAGE);     }      generator.setRelationName(relName);    }  }); code code format="java" m_ButtonUse.addActionListener(new ActionListener{     public void actionPerformed(ActionEvent evt){        m_Support.firePropertyChange("", null, null);      }    }); code code format="java" addPropertyChangeListener(new PropertyChangeListener {   public void propertyChange(PropertyChangeEvent e) {      try {        Instances data = new Instances(new StringReader(m_Output.getText));        // set data in preprocess panel (will also notify of capabilties changes)        getExplorer.getPreprocessPanel.setInstances(data);      }      catch (Exception ex) {        ex.printStackTrace;        JOptionPane.showMessageDialog( getExplorer, "Error generating data:\n" + ex.getMessage, "Error", JOptionPane.ERROR_MESSAGE);     }    }  }); code code format="text" Tabs=weka.gui.explorer.GeneratorPanel:standalone,\ weka.gui.explorer.ClassifierPanel,\ weka.gui.explorer.ClustererPanel,\ weka.gui.explorer.AssociationsPanel,\ weka.gui.explorer.AttributeSelectionPanel,\ weka.gui.explorer.VisualizePanel code > **Note:** the //standalone// option is used to make the tab available without requiring the preprocess panel to load a dataset first.
 * class is derived from and implements the  interface (the full source code also imports the  interface, but that is only additional functionality):
 * some basic members that we need to have (the same as for the class):
 * methods we need to implement due to the used interfaces (almost identical to ):
 * additional GUI elements:
 * the //Generate// button doesn't load the generated data directly into the Explorer, but only outputs in the (this is done with the //Use// button - see further down):
 * the //Use// button finally fires a //property change// event that will load the data into the Explorer:
 * the //propertyChange// event will perform the actual loading of the data, hence we add an anonymous property change listener to our panel:
 * In order to add our to the list of tabs displayed in the Explorer, we need to modify the  file (just extract it from the  and place it in your home directory). The //Tabs// property must look like this:

Source

 * [[file:GeneratorPanel.java]] ([|stable-3.6], [|developer])

Purpose
By default the //Classify// panel only performs 1 run of 10-fold cross-validation. Since most classifiers are rather sensitive to the order of the data being presented to them, those results can be too optimistic or pessimistic. Averaging the results over 10 runs with differently randomized train/test pairs returns more reliable results. And this is where this plugin comes in: it can be used to obtain statistical sound results for a specific classifier/dataset combination, without having to setup a whole experiment in the Experimenter.

Implementation
code format="text" Tabs=weka.gui.explorer.ClassifierPanel,\ weka.gui.explorer.ExperimentPanel,\ weka.gui.explorer.ClustererPanel,\ weka.gui.explorer.AssociationsPanel,\ weka.gui.explorer.AttributeSelectionPanel,\ weka.gui.explorer.VisualizePanel code
 * Since this plugin is rather bulky, we omit the implementation details, but the following can be said:
 * based on the
 * the //actual// code doing the work follows the example in Using the Experiment API article
 * In order to add our to the list of tabs displayed in the Explorer, we need to modify the  file (just extract it from the  and place it in your home directory). The //Tabs// property must look like this:

Source

 * [[file:ExperimentPanel.java]] ([|stable-3.6], [|developer])