C++ MFC MDI change child window variables on creation - c++

I have an MDI application where a dialog is called when the OnFileNew() function (processed by the theApp object) is called. This dialog allows the user to set values to some variables that then need to be passed on to the CChildFrame object that is created when the ->CreateNewChild() function is called.
How do I pass these variables onto the CChildFrame object that is created by the ->CreateNewChild() function?
EDIT:
In response to an answer I got, here are the results for using ->Create() vs ->CreateNewChild().
Link: CMainFrame *pFrame; - pFrame->CreateNewChild()
Link: CChildFrame *childFrame; - childFrame->Create()
How do I get the tabbed windows shown in the first link with the function declarations described in the second link?

You can pass the data via a customized document template. Derive a class from CMultiDocTemplate to add additional data members, then add a pointer to your derived document template class to your CWinApp-derived app class. Initialize your document template in the usual way, except when you finish, save the new document template object to the pointer in your app class.
Now in your CreateNewChild function, instead of calling CWinApp::OnFileNew, you can just get the data from the current frame, then assign to the data member in the document template saved in the app class, before calling OpenDocumentFile(NULL). You can clear the data members when OpenDocumentFile returns.
The document template will in turn create the child frame and pass the doc template in the create context. To get the create context in the child frame, you can either override CChildFrame::OnCreateClient, or read the create structure in OnCreate:
MDICREATESTRUCT * pMDICreateStruct=(MDICREATESTRUCT * )lpCreateStruct->lpCreateParams;
CCreateContext *pCreateContext=(CCreateContext *)pMDICreateStruct->lParam;
Instead of passing the initialization data in the document template, you could also pass data to the new document. You will basically copy the code from CMultiDocTemplate::OpenDocumentFile and add the code to get the initialization data from the active document of the main frame.

Related

Creating C++ objects from QML

In C++ model, I have QAbstractListModelderived class called Cart which contains QList<void*>container.
In QML, I show a list of objects. When user clicks on any of them, it should create that object in C++ and add it to the cart. It will also set some properties of that object.
My question is how do I really do that in the best way?
Here is the code how this will look like in C++ alone:
Cart * cart = new Cart; // we have this object already created
// which we have exposed to QML as 'qmlcart'.
// When user clicks apple
Apple * apple = new Apple(1.99) ; // 1.99 is price
apple->setType("Red Delicious");
cart.add( apple );
// When user clicks orange
Orange * orange = new Orange(0.99) // price
orange->setType("Valencia")
cart.add( orange );
The above is straight forward in C++ world but it gets somewhat complex if the click is in QML. What is the best way create and pass information about the object? Assume we have more attributes about the object other than price and type.
Does there has to be a corresponding slot function for every type of object we create? If the object has more complex properties, say 5 various attributes, do we pass all 5 of them to slot function or there is a better way? Should we use some of factory design pattern to create object?
One particular ability I am missing with QML in use is the freedom to work with C++ created object. For example if it was all C++, I could have created the C++ object and than set all its attributes no matter how many are there and than simply add the object to the container.
But with QML, even though I have all information about the object but I can't really create and configure the class and than pass to C++ because it has to be created in C++ which can't return it and so all configuration parameters will have to be passed to the slot function as well! Is there any better way?
Unless the C++ object happens to be a QObject derived type, registered as a type to QML, you can't do that directly. Even in QML, you either need a component or a QML source wrapper to create a C++ object, for example:
\\Object.qml
import Object 1.0
Object {}
So now you have a QML source wrapper to create the C++ Object dynamically. Or using a component:
Component {
id: component
Object {}
}
You can however, have a C++ slot or Q_INVOKABLE you can call from QML, and do the object creation there.
You can set object properties in QML as well, for example:
Object {
someProperty: someValue
}
or
component.createObject(parentObj, {"someProperty" : somevalue})
it will work similarly to a constructor - all properties will be set before the object is considered to be "competed" by the runtime.
From your question it looks like you are getting ahead of yourself - do a little more learning before you rush into using QML... and there is really no need for those void * - this is not C. Don't make your life any harder.

Using QtScript outside my main form

I am using Qt5, and trying to learn on how to make an application scriptable.
For this I created a main window that contains some text edits, labels, etc. I then added an option called "script console" to that forms' menu in order for me to open a second form containing just a text edit and a button called "Evaluate".
What I was aiming at was being able to use that second form and through Qt script engine be able to set or get values from my main form, and generally be able to script various functions.
What I tried doing was set up the engine like this
scriptingconsole::scriptingconsole(QWidget *parent) :
QDialog(parent),
ui(new Ui::scriptingconsole)
{
ui->setupUi(this);
QScriptValue appContext = myScriptEngine.newQObject(parent);
myScriptEngine.globalObject().setProperty("app", appContext);
}
I don't get what I was expecting though.
If I try to evaluate the expression "app" I get null as an output.
This works fine if I use myScriptEngine.newQObject(parent) with an object inside the current class (if instead of parent I enter this), but I want to be able to access object in other classes too (hopefully all public slots that are used by my app in general).
Does anyone know what I am doing wrong here and how can I use my scripting console
class to access public slots from my main window?
What's wrong?
I guess it's because you didn't explicitly pass the pointer, which points to your main form, to the constructor of your scriptingconsole. That's why you got NULL as a result. (NULL is default, as you can see QWidget *parent = 0 in every QWidget constructor)
This happens if your object is not dynamically instantiated.
Solution
Dynamically allocate scriptingconsole in your main form:
scriptingconsole* myScriptConsole;
//...
myScriptConsole = new scriptingconsole(this);
// ^^^^
// pass the pointer which points to parent widget
The Qt documentation of QScriptEngine::newQObject says:
Creates a QtScript object that wraps the given QObject object, using the given ownership. The given options control various aspects of the interaction with the resulting script object.
http://qt-project.org/doc/qt-4.8/qscriptengine.html#newQObject
i.e. it wraps a QObject.. You are probably passing NULL to newQObject, for whatever reason. Try setting a breakpoint and evaluating the value of 'parent'.

Access QCustomPlot Variable

In Short:
I need to access a QCustomPlot variable in a WidgetClass. But I can't because it's private in that context. How is this accomplished?
What I have done:
I have a class that creates a graph and a widget class that calls the graph class
My QCustomPlot variable is a private variable that is being declared in the graph class, but I need to access this variable in the Widget Class.
I have a generate_graphs slot that gets data and passes the data to the graph class and from there the graph is created. I am saving to a png, so I need to access the graph variable with this data, the variable is passed data in the WidgetClass which then calls the graph class, which generates the graph.
The graph class variable (in the Widget Class) is declared like so: GraphWidget* graph
I try to access the QCustomPlot variable in that slot like this: graph->QCustomPlot->savePNG()
I keep getting an error that says QCustomPlot* GraphWidget::QCustomPlot is private within this context
I then created a function to just return the GraphWidget* graph so I could access that variable in the graph class. Instead so I could access the QCustomPlot variable without it being private in that context. This caused errors because it needs to be passed a GraphWidget* graph which is not declared in the GraphWidget class.
This was an isolated problem, but if anyone else has a similar problem my suggestion is this:
Make sure that your GraphWidget variable (the variable to creates a QCustomPlot) is accessible by the entire class that is trying to use it. I was trying to pass it a bunch of different ways, and none of them were correct.
Also make sure you create a public function in the GraphWidget class in order to access the savePng() function that QCustomPlot uses.

MFC :: passing data using structure

So I have this MFC dialog program I am working with. The dialogs are written but now I am having difficulty passing data around from dialog to dialog. I have the following structure _dlgDataHandler set up in a class derived from CWinApp and have have created a "new" statement for a pointer to this type.
//.......SRK.h file
class CSRK_App : public CWinApp
{
public:
CFSB_App();
// added the following data structure for data passing withing the program
typedef struct _dlgDataHandler {
char RepetitionRadio[24];
// another member
// yet another member and so on as necessary
} *dlgDataHandlerPtr;
// extern dlgDataHandlerPtr dlgDataHandler;
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CSRK_App)
public:
virtual BOOL InitInstance();
//}}AFX_VIRTUAL
// Implementation
//{{AFX_MSG(CSRK_App)
// NOTE - the ClassWizard will add and remove member functions here.
// DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
//....... SRK.cpp A pointer to a new dataHandler created in this block about 2/3 the way down
// CSRK_App initialization
BOOL CSRK_App::InitInstance()
{
AfxEnableControlContainer();
// Standard initialization
// If you are not using these features and wish to reduce the size
// of your final executable, you should remove from the following
// the specific initialization routines you do not need.
//SetRegistryKey(_T("Local AppWizard-Generated Aplications"));
#ifdef _AFXDLL
Enable3dControls(); // Call this when using MFC in a shared DLL
#else
Enable3dControlsStatic(); // Call this when linking to MFC statically
#endif
//CSRK_Dlg dlg;
CDialogMain dlg("SRK - Beta"); // added 12/27 **
m_pMainWnd = &dlg;
//const char* m_pszHelpFilePath = NULL;
//free((void*)m_pszHelpFilePath);
//m_pszHelpFilePath=_tcsdup(_T("c:\SRKHelp.rtf"));
// the following line added to allocate memory for the structure
dlgDataHandlerPtr dlgDataHandler = new _dlgDataHandler;
dlg.SetWizardMode(); // added 12/27 **
int nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here to handle when the dialog is
// dismissed with OK
}
else if (nResponse == IDCANCEL)
{
// TODO: Place code here to handle when the dialog is
// dismissed with Cancel
}
// Since the dialog has been closed, return FALSE so that we exit the
// application, rather than start the application's message pump.
return FALSE;
}
In the dialog .cpp files, there are five, I need to be able to get data from the AFX variables "m_" and load them into this dataHandler Structure (or another one like it) so that they can be used in other dialogs and parts of the program, specifically my actual code when all the dialog data collection is done. Someone said to use AfxGetApp() so that I could have a handle on the current instance but I do not understand what they are talking about. And yes I have read about it in many forums, I just don't get it. I further realize this is probably not the best way to do it. I am trying to learn MFC/OOP with what time I have available, but for now, I am just trying to get a handle on the basic process as I can tune it later once I understand how to collect and pass simple data around.
I further don't understand how calling AfxGetApp() will help me get a handle on the members of CSRK_App. It inherited CWinApps public members but AfxGetapp() can't see what CSRK_App has... can it?
First, to explain the AfxGetApp advice you have received. There is some extra hand-waving using 'new' and a pointer, but this is basically using a global variable for the structure that holds your data. This is not the best way to do what you are trying to do. There are many pitfalls.
AfxGetApp() is an MFC call that returns a pointer to your main App class derived from CWinApp.
If you want to use that returned pointer, you need to cast it as a CSRK_App* pointer with:
CSRK_App* pApp = static_cast <CSRK_App*> ( AfxGetApp());
Then you can use pApp->dlgDataHandlerPtr->... to access the variables you need.
Now, for the pitfalls. Someone else may chime in with a reason why the 'new' and the pointer are helpful, but I do not see any advantage to this approach as compared to just having a local variable dlgDataHandler inside your CSRK_App class. That would simplify the code.
The next issue is that all your data is public in a struct. Any dialog class that can call AfxGetApp can read or write any data in that struct. You have no way to control access.
Also, all of your dialog classes must now include SRK_App.h so they know the structure, and have access to all other variables in that App class.
A cleaner, object-oriented approach would be to declare the struct (class) for the data in a separate .h file that could be included in the dialog classes. Then, you would pass a pointer/reference to this data into the constructor of the dialog classes. The dialog class would have no need to know anything about the App class.
For an even higher level of segregation, the dialog classes can be written so they only get a copy of the dlgDataHandler class passed in before calling .DoModal(), and then after the DoModal call returns with IDOK, the App class can have control over which data from the dialog gets updated into the dlgDataHandler class. The advantage of this approach is that it insures that no matter how the dialog class is programed, the user can always "Cancel" the dialog without modifying any data.

How do i update a control outside of a dialog?

For example, in an MFC program, I have my main application and a 'class'. What should I do if I want to update a control (say, a listbox) that is situated on my main application from that 'class'?
heres an example that worked for me
theApp.m_pMainWnd->GetDlgItem(IDC_BUTTON6)->SetWindowTextW(L"Run Auto Test");
Your class can be designed to trigger an event which your main application can listen for. Then, a listener/event handler/delegate can be called to handle the event and update the listbox. Typically, most event formats pass a reference of the sender, in this case your 'class', as well as an object containing event arguments. These arguments can be used to pass the list of items you want to add to your listbox.
If you have the handle to dialog object in your class, then you can use GetDlgItem(ResourceID) to get list control object.
The easiest approach is to expose the listview from your application form/window to the classes that use it. You can do this either by passing the listview object (or parent window) to the class constructor, or storing it in a static variable that is accessible by the class.
For better encapsulation, you can put a method in the application that the class can call, e.g. "AddItemToListBox()". This allows the application object to remain in control of how you access the listbox. Again you can do this as a static method, or pass the main program object's 'this' pointer into the class constructor.
i.e.
class CApplication
{
CListBox m_ListBox;
public:
static void CApplication::AddItemToListBox(CString itemText)
{
// Add the item as you wish here
}
}
class CMyClass
{
afx_msg void CMyClass::OnMouseDown(...)
{
CApplication::AddItemToListBox("This is a test");
}
}