A wizard is a graphical user interface that includes a series of dialogs to direct a user to complete a mission step by step. A wizard makes a complex task easier to perform. Origin provides several classes in Origin C for users to develop a wizard. The dialog for each step in the wizard can be created using an X-Function.
In this example, the wizard will perform a normality test and then a one-sample t-test for data in a column. The normality test's result can be shared in one-sample t-test.
| Note: This tutorial requires the Develop Kit. |
Minimum Origin Version Required: Origin 8.1SR0




Note that the X-Functions NormalityTest and OnetTest have the same variable "prob", which is a shared variable and will be declared in the source file.

Add the following script to StatTestWiz.h file.
#ifndef __STAT_TEST_WIZ_H__ #define __STAT_TEST_WIZ_H__ #include <..\OriginLab\XFWiz.h> #include <..\OriginLab\XFCore.h> #include <..\OriginLab\XFWizard_utils.h> class StatTestWizCore : public XFCore { public: StatTestWizCore(); public: void ChangeGoal(int nGoal); DataRange GetRange(); int nStep; protected: }; int stat_test_run_wiz_nodlg(LPCSTR lpcszThemeName = NULL, const XFWizTheme *pXFWizTheme = NULL, const XFWizInputOutputRange *pXFWizIO = NULL, DWORD dwOPUID = 0); int stat_test_open_wiz_dlg(LPCSTR lpcszThemeName = NULL, const XFWizTheme *pXFWizTheme = NULL, const XFWizInputOutputRange *pXFWizIO = NULL, DWORD dwOPUID = 0); #endif //__STAT_TEST_WIZ_H__
Click the Save button to save StatTestWiz.h file.
Add the following script to StatTestWiz.c file.
/////////////////////////////////////////////////////////////////////////////////// #include <..\OriginLab\XFWizManager.h> #include <..\OriginLab\WizOperation.h> #include <..\OriginLab\XFWizNavigation.h> #include <..\OriginLab\XFWizScript.h> #include <..\OriginLab\XFWizDlg.h> /////////////////////////////////////////////////////////////////////////////////// // Include your own header files here. #include "StatTestWiz.h" enum { GOAL_ALL = 0, GOAL_SIMPLE, }; //Names of three X-Functions #define STR_STEP_GOAL "StatTestWizGoal" #define STR_STEP_Normal "NormalityTest" #define STR_STEP_TTest "OnetTest" //Names of steps shown in the wizard. #define STR_LABEL_STEP_GOAL "Goal" #define STR_LABEL_STEP_Normal "Normality Test" #define STR_LABEL_STEP_TTest "One-Sample t-test" /////////////////////////////////////////////////////////////////////////////////// //Class StatTestWizTheme class StatTestWizTheme : public XFWizTheme { public: StatTestWizTheme(); }; //Name of the variable prob shared by X-Functions NormalityTest and OnetTest #define STR_GETN_VAR_SHARED_NProb "prob" StatTestWizTheme::StatTestWizTheme() :XFWizTheme() { m_saSharedList.Add(STR_GETN_VAR_SHARED_NProb); //Add the shared variable } /////////////////////////////////////////////////////////////////////////////////// class StatTestWizInputOutputRange : public XFWizInputOutputRange { }; /////////////////////////////////////////////////////////////////////////////////// //Class StatTestWizManager #define STR_CLASS_NAME_TEST "StatTestWiz" #define TEST_VERSION_NUMBER 1.0 class StatTestWizManager : public XFWizManager { public: StatTestWizManager(LPCSTR lpcszThemeName = NULL, const XFWizTheme *pXFWizTheme = NULL, const XFWizInputOutputRange *pXFWizIO = NULL, DWORD dwUIDOp = 0); protected: virtual double GetVersion() { return TEST_VERSION_NUMBER; } virtual XFCore* CreateXFCore() { return new StatTestWizCore; } virtual XFWizTheme* CreateXFWizTheme() { return new StatTestWizTheme; } virtual XFWizInputOutputRange* CreateXFWizInputOutputRange() { return new StatTestWizInputOutputRange; } virtual string GetClassName() { return STR_CLASS_NAME_TEST; } }; StatTestWizManager::StatTestWizManager(LPCSTR lpcszThemeName, const XFWizTheme *pXFWizTheme, const XFWizInputOutputRange *pXFWizIO, DWORD dwUIDOp) : XFWizManager(lpcszThemeName, pXFWizTheme, pXFWizIO, dwUIDOp) { StringArray saMapXFNames = {STR_STEP_GOAL, STR_STEP_Normal, STR_STEP_TTest}; StringArray saMapXFLabels = {STR_LABEL_STEP_GOAL, STR_LABEL_STEP_Normal, STR_LABEL_STEP_TTest}; m_saMapXFNames = saMapXFNames; m_saMapXFLabels = saMapXFLabels; ASSERT( m_saMapXFNames.GetSize() == m_saMapXFLabels.GetSize() ); StringArray saDefaultXFNames = {STR_STEP_GOAL, STR_STEP_Normal, STR_STEP_TTest}; m_saDefaultXFNames = saDefaultXFNames; m_strRunDlgName = _L("Stat Test"); } /////////////////////////////////////////////////////////////////////////////////// //Class StatTestWizCore StatTestWizCore::StatTestWizCore() :XFCore() { StringArray vsXFsRecalculateShown = {STR_STEP_GOAL}; m_vsXFsRecalculateShown = vsXFsRecalculateShown; nStep = GOAL_ALL; } //Select steps in the Goal Step void StatTestWizCore::ChangeGoal(int nGoal) { XFWizNavigation *pXFWizNavg = (XFWizNavigation *)GetXFWizNavigation(); ASSERT(pXFWizNavg); nStep = nGoal; if ( pXFWizNavg ) { StringArray saXFNames; saXFNames.Add(STR_STEP_GOAL); switch (nGoal) { case GOAL_ALL: saXFNames.Add(STR_STEP_Normal); saXFNames.Add(STR_STEP_TTest); break; case GOAL_SIMPLE: saXFNames.Add(STR_STEP_TTest); break; } pXFWizNavg->SetSteps(saXFNames); } } //Get input DataRange in the Goal Step. DataRange StatTestWizCore::GetRange() { XFWizNavigation *pXFWizNavg = (XFWizNavigation*)GetXFWizNavigation(); XFWizInputOutputRange* pIORange = pXFWizNavg->GetXFWizInputOutputRange(); DataRange drInput; if(!pIORange) { error_report("Fail to get io ranges!"); return drInput; } Array<DataRange&> drs; //Get input DataRange. if(!pIORange->Get(&drs, STR_STEP_GOAL, true)) { error_report("Fail to get range from WizCore!"); return drInput; } drInput = drs.GetAt(0); return drInput; } /////////////////////////////////////////////////////////////////////////////////// int stat_test_run_wiz_nodlg(LPCSTR lpcszThemeName, const XFWizTheme *pXFWizTheme, const XFWizInputOutputRange *pXFWizIO, DWORD dwOPUID) { TEMPLATE_run_wiz_nodlg(StatTestWizManager, lpcszThemeName, pXFWizTheme, pXFWizIO, dwOPUID) } int stat_test_open_wiz_dlg(LPCSTR lpcszThemeName, const XFWizTheme *pXFWizTheme, const XFWizInputOutputRange *pXFWizIO, DWORD dwOPUID) { TEMPLATE_open_wiz_dlg(StatTestWizManager, lpcszThemeName, pXFWizTheme, pXFWizIO, dwOPUID) } int stat_test_run_wiz(UINT msg, const XFWizTheme *pXFWizTheme, const XFWizInputOutputRange *pXFWizIO, DWORD dwOPUID, int nExeMode) { TEMPLATE_run_wiz(StatTestWizManager, msg, pXFWizTheme, pXFWizIO, dwOPUID, nExeMode) }
Click the Save button to save StatTestWiz.c file.
Note that StatTestWiz.c should be compiled after the X-Function StatTest is compiled, since the included files in StatTestWiz.c are not yet in the workspace until the X-Function StatTest is compiled. In fact StatTestWiz.h is included in X-Function StatTest, so StatTestWiz.c will be compiled automatically when X-Function StatTest is compiled.
In the X-Function Builder, click the Open button and open the X-Function StatTest. Click the Edit X-Function in Code Builder and add the following script.
#include <..\OriginLab\XFWiz.h> #include <..\OriginLab\WizOperation.h> #include <..\OriginLab\XFCore.h> #include <..\OriginLab\XFWizNavigation.h> #include <..\OriginLab\XFWizManager.h> #include <..\OriginLab\XFWizScript.h> #include <..\OriginLab\XFWizDlg.h> #include <..\OriginLab\XFWizard_utils.h> #include <..\OriginLab\WksOperation.h> #include <event_utils.h> #include "StatTestWiz.h"
Add the function body, which specifies the dialog mode.
if( script ) stat_test_run_wiz_nodlg(tn); else stat_test_open_wiz_dlg(tn);
Add the function body, which determines not to show this dialog before the wizard is opened.
nRet = XFEVT_PROCEED_NO_DLG;
Click Compile button to compile the file. Then click Return to Dialog button to return to X-Function Builder. In the X-Function Builder, click Save OXF file button to save the X-Function.
Open the X-Function StatTestWizGoal. Click Edit X-Function in Code Builder button, add the following script.
#include "StatTestWiz.h"
This function is used to check whether the input DataRange is a single column.
static bool _check_input(const TreeNode trGetN, string& strErr) { TreeNode trRange = trGetN.input; DataRange drInput; drInput.Create(trRange.strVal); if( drInput.GetNumRanges() == 0 ) { strErr = "Input can't be empty, and it should be a valid column."; return false; } else { if( drInput.GetNumRanges() == 1) { Worksheet wksInput; int nC1, nC2; drInput.GetRange(wksInput, nC1, nC2); if( nC1 == nC2 ) return true; } strErr = "Please select one column."; return false; } }
Add the function body, which updates the dialog.
StatTestWizCore* pstatwc = (StatTestWizCore*)get_xf_core_handler(trGetN); ASSERT(pstatwc); //Update the Wizard page. if ( 0 == lstrcmp(lpcszNodeName, "goal") ) pstatwc->ChangeGoal(trGetN.goal.nVal); //Error message is shown at the bottom of the dialog, //and OK button is disenabled for incorrect choice of DataRange. bOKEnable = _check_input(trGetN, strErrMsg); return false;
Click Compile button to compile the file. Then click Return to Dialog button to return to X-Function Builder, and click Save OXF file button to save the X-Function.
Open the X-Function NormalityTest. Click the Edit X-Function in Code Builder button and add the following script.
#include "StatTestWiz.h" #include <XFbase.h>
This function is used to update the dialog's edit boxes for normality test result.
static void _update_GUI(TreeNode& trGetN) { vector vRes; vRes = _norm_test(trGetN.nXFCorePointer.nVal, trGetN.type.nVal); trGetN.stat.dVal = vRes[0]; trGetN.df.dVal = vRes[1]; trGetN.prob.dVal = vRes[2]; }
This function is used to update the string shown at the bottom of the dialog.
static void _update_strErr(const TreeNode tr, string& strErr) { if(tr.prob.dVal >= 0.05 && tr.prob.dVal <= 1) strErr = "At the 0.05 level, the data was significantly drawn from a normally distributed population."; else if(tr.prob.dVal < 0.05 && tr.prob.dVal >= 0) strErr = "At the 0.05 level, the data was not significantly drawn from a normally distributed population."; else strErr = "There is not enough information to draw a conclusion."; }
Note that the string is divided into two lines shown in the page. It should be a command of one line in the script.
This function is used to perform Normality Test using related X-Functions.
static vector _norm_test(const int nXFCorePointer, const int nType) { StatTestWizCore* pstatwc = (StatTestWizCore*)get_xf_core_handler(nXFCorePointer); ASSERT(pstatwc); vector vRes(3); vRes[2] = -1; DataRange drInput; drInput = pstatwc->GetRange(); if( !drInput ) return vRes; vector<string> vsXFName = {"swtest","kstest","lillietest"}; XFBase xfNorm(vsXFName[nType]); if( !xfNorm.SetArg("irng", drInput) ) { error_report("Failed to set argument image type"); return vRes; } if( !xfNorm.SetArg("stat", vRes[0]) ) { error_report("Failed to set argument image type"); return vRes; } if( !xfNorm.SetArg("df", vRes[1]) ) { error_report("Failed to set argument image type"); return vRes; } if( !xfNorm.SetArg("prob", vRes[2]) ) { error_report("Failed to set argument image type"); return vRes; } if( !xfNorm.Evaluate() ) { error_report("Failed to evaluate the stats X-Function."); return vRes; } return vRes; }
Update the function body, which exports the result into a worksheet when the Next button is pressed.
DataRange drInput; StatTestWizCore* pstatwc = (StatTestWizCore*)get_xf_core_handler(nXFCorePointer); ASSERT(pstatwc); drInput = pstatwc->GetRange(); if( !drInput ) return; string strBook, strSheet; if(!drInput.GetBookSheet(strBook, strSheet)) { error_report("Workbook and worksheet names can't be obtained."); return; } WorksheetPage wpData(strBook); int nLayer = wpData.AddLayer("Normality Test"); if(nLayer >= 0) { Worksheet wksRes = wpData.Layers(nLayer); vector<string> vsTypeName = {"Shapiro-Wilk","Kolmogorov-Smirnov","Lilliefors"}; vector<string> vsNProb = {"Prob<W", "Prob>D", "Prob>D"}; vector<string> vsParaName = {"Statistic", "DF", ""}; vsParaName[2] = vsNProb[type]; vector vRes; vRes = _norm_test(nXFCorePointer, type); wksRes.Columns(1).SetLongName(vsTypeName[type]); for(int ii=0; ii<3; ii++) { wksRes.SetCell(ii, 0, vsParaName[ii], false); wksRes.SetCell(ii, 1, vRes[ii]); } } else { error_report("New worksheet can't be created."); }
Update the function body, which will update the results in the dialog as the method of normality test changes. Strings shown at the bottom of the dialog will also be updated.
_update_GUI(trGetN); _update_strErr(trGetN, strErrMsg); return true;
Update the function body, which will make the edit boxes for results grayed out, and show the result in the dialog.
trGetN.stat.Enable = false; trGetN.df.Enable = false; trGetN.prob.Enable = false;
Click the Compile button to compile the file. Then click the Return to Dialog button to return to X-Function Builder, and click the Save OXF file button to save the X-Function.
Open the X-Function OnetTest. Click the Edit X-Function in Code Builder button and add the following script.
#include "StatTestWiz.h" #include <XFbase.h>
const vector<string> vsNull = {"Mean = ","Mean <= ","Mean >= "}; const vector<string> vsAlter = {"Mean <> ","Mean > ","Mean < "}; const vector<string> vsAcceptNull = {"Not significantly different from","Not significantly greater than","Not significantly less than"}; const vector<string> vsRejectNull = {"significantly different from","significantly greater than","significantly less than"}; const vector<string> vsProb = {"Prob>|t|", "Prob>t", "Prob<t"};
This function is used to update the Null edit box.
static void _update_null(TreeNode& trGetN, bool bMean = false) { string strNull; strNull = vsNull[trGetN.tail.nVal] + ftoa(trGetN.mean.dVal); trGetN.null.strVal = strNull; if(bMean) { string strAlter = vsAlter[0] + ftoa(trGetN.mean.dVal) + "|"; strAlter = strAlter + vsAlter[1] + ftoa(trGetN.mean.dVal) + "|"; strAlter = strAlter + vsAlter[1] + ftoa(trGetN.mean.dVal); trGetN.tail.SetAttribute(STR_COMBO_ATTRIB, strAlter); } }
This function is used to check the Significance Level edit box value.
static bool _check_sig_level(TreeNode& trGetN, string& strErr) { if( trGetN.siglevel.dVal > 0 && trGetN.siglevel.dVal < 1 ) { return true; } else { strErr = "Significance Level should be between 0 and 1."; return false; } }
This function is used to define the string for the conclusion of t-test at the bottom based on P-value.
static void _update_strErr(const TreeNode tr, string& strErr) { if(tr.tprob.dVal >= tr.siglevel.dVal && tr.tprob.dVal <= 1) strErr.Format("Null Hypothesis is %s%s.\r\nAlternative Hypothesis is %s%s. At the %s level, the population mean is %s the test mean(%s).", vsNull[tr.tail.nVal], ftoa(tr.mean.dVal), vsAlter[tr.tail.nVal], ftoa(tr.mean.dVal), ftoa(tr.siglevel.dVal), vsAcceptNull[tr.tail.nVal], ftoa(tr.mean.dVal) ); else if(tr.tprob.dVal < tr.siglevel.dVal && tr.tprob.dVal >= 0) strErr.Format("Null Hypothesis is %s%s.\r\nAlternative Hypothesis is %s%s. At the %s level, the population mean is %s the test mean(%s).", vsNull[tr.tail.nVal], ftoa(tr.mean.dVal), vsAlter[tr.tail.nVal], ftoa(tr.mean.dVal), ftoa(tr.siglevel.dVal), vsRejectNull[tr.tail.nVal], ftoa(tr.mean.dVal) ); else strErr = "There is not enough information to draw a conclusion."; }
Note that the command is divided into several lines shown in the page. It should be a command of one line in the script.
This function is used to update edit boxes for results in the dialog.
static void _update_GUI(TreeNode& trGetN) { vector vRes; vRes = _one_sample_t_test(trGetN.nXFCorePointer.nVal, trGetN.mean.dVal, trGetN.tail.dVal, trGetN.siglevel.dVal); trGetN.stat.dVal = vRes[0]; trGetN.df.dVal = vRes[1]; trGetN.tprob.dVal = vRes[2]; trGetN.lcl.dVal = vRes[4]; trGetN.ucl.dVal = vRes[5]; }
This function is used to perform One-Sample t-Test using an X-Function.
static vector _one_sample_t_test(const int nXFCorePointer, const double dMean, const int nTail, const double dSiglevel) { DataRange drInput; StatTestWizCore* pstatwc = (StatTestWizCore*)get_xf_core_handler(nXFCorePointer); ASSERT(pstatwc); vector vRes(6); vRes[2] = -1; drInput = pstatwc->GetRange(); if( !drInput ) return vRes; vRes[3] = 100 - 100*dSiglevel; XFBase xfTTest("ttest1"); if( !xfTTest.SetArg("irng", drInput) ) { error_report("Failed to set argument irng"); return vRes; } if( !xfTTest.SetArg("mean", dMean) ) { error_report("Failed to set argument mean"); return vRes; } if( !xfTTest.SetArg("tail", nTail) ) { error_report("Failed to set argument tail"); return vRes; } if( !xfTTest.SetArg("alpha", dSiglevel) ) { error_report("Failed to set argument alpha"); return vRes; } if( !xfTTest.SetArg("stat", vRes[0]) ) { error_report("Failed to set argument stat"); return vRes; } if( !xfTTest.SetArg("df", vRes[1]) ) { error_report("Failed to set argument df"); return vRes; } if( !xfTTest.SetArg("prob", vRes[2]) ) { error_report("Failed to set argument prob"); return vRes; } if( !xfTTest.SetArg("lcl", vRes[4]) ) { error_report("Failed to set argument lcl"); return vRes; } if( !xfTTest.SetArg("ucl", vRes[5]) ) { error_report("Failed to set argument ucl"); return vRes; } if( !xfTTest.Evaluate() ) { error_report("Failed to evaluate the ttest1 X-Function."); return vRes; } return vRes; }
Update the function body, which exports the result into a worksheet when the Finish button is pressed.
DataRange drInput; StatTestWizCore* pstatwc = (StatTestWizCore*)get_xf_core_handler(nXFCorePointer); ASSERT(pstatwc); drInput = pstatwc->GetRange(); if( !drInput ) return ; string strBook, strSheet; if(!drInput.GetBookSheet(strBook, strSheet)) { error_report("Workbook and worksheet names can't be obtained."); return; } WorksheetPage wpData(strBook); int nLayer = wpData.AddLayer("One-Sample t-test"); if(nLayer >= 0) { Worksheet wksRes = wpData.Layers(nLayer); vector<string> vsParaName = {"t Statistic", "DF","", "Conf. Levels in %", "Lower Limits", "Lower Limits"}; vsParaName[2] = vsProb[tail]; vector vRes; vRes = _one_sample_t_test(nXFCorePointer, mean, tail, siglevel); wksRes.SetSize(-1, 4); wksRes.Columns(0).SetLongName("Test Statistics"); string strNull = "Null Hypothesis is " + vsNull[tail] + ftoa(mean); wksRes.Columns(1).SetLongName(strNull); wksRes.Columns(3).SetLongName("Confidence Intervals for Mean"); for(int ii=0; ii<3; ii++) { wksRes.SetCell(ii, 0, vsParaName[ii], false); wksRes.SetCell(ii, 1, vRes[ii]); wksRes.SetCell(ii, 2, vsParaName[ii + 3], false); wksRes.SetCell(ii, 3, vRes[ii + 3]); } } else { error_report("New worksheet can't be created."); }
Update the function body, which will update results and show a conclusion at the bottom of the dialog according to the result. As settings change in the dialog, the Null edit box will be updated as the mean and hypothesis change, and the Significance Level edit box's value is checked.
if( 0 == lstrcmp(lpcszNodeName, "mean") ) _update_null(trGetN, true); if( 0 == lstrcmp(lpcszNodeName, "tail") ) _update_null(trGetN); if( 0 == lstrcmp(lpcszNodeName, "siglevel") ) bOKEnable = _check_sig_level(trGetN, strErrMsg); _update_GUI(trGetN); _update_strErr(trGetN, strErrMsg); return false;
Update the function body, to show/hide or disable the controls in the dialog.
StatTestWizCore* pstatwc = (StatTestWizCore*)get_xf_core_handler(trGetN.nXFCorePointer.nVal); ASSERT(pstatwc); trGetN.prob.Show = 1 - pstatwc->nStep; trGetN.prob.Enable = false; trGetN.null.Enable = false; trGetN.stat.Enable = false; trGetN.df.Enable = false; trGetN.tprob.Enable = false; trGetN.lcl.Enable = false; trGetN.ucl.Enable = false;
Click the Compile button to compile the file. Then click the Return to Dialog button to return to the X-Function Builder. Click the Save OXF file button to save the X-Function.
Close Origin. Then start Origin and you will notice that a new item Stat Test is added to Origin's menu Statistics: Hypothesis Testing.
The following example shows how to use the wizard.





