I'm working through "Programming Principles and Practice", and I don't understand why this Widget class uses pointers as data members.
The book's explanations is this:
Note that our Widget keeps track of its FLTK widget and the Window with which it is associated. Note that we need pointers for that because a Widget can be associated with different Windows during its life. A reference or a named object wouldn’t suffice. (Why not?)
So, I still don't understand why the Widget can't have a named object Window win as a data member, which can take a different value when it's associated with a different Window. Could someone explain this a bit?
class Widget {
// Widget is a handle to a Fl_widget — it is *not* a Fl_widget
// we try to keep our interface classes at arm’s length from FLTK
public:
Widget(Point xy, int w, int h, const string& s, Callback cb)
:loc(xy), width(w), height(h), label(s), do_it(cb) { }
virtual ~Widget() { } // destructor
virtual void move(int dx,int dy)
{ hide(); pw–>position(loc.x+=dx, loc.y+=dy); show(); }
virtual void hide() { pw–>hide(); }
virtual void show() { pw–>show(); }
virtual void attach(Window&) = 0; // each Widget defines at least one action for a window
Point loc;
int width;
int height;
string label;
Callback do_it;
protected:
Window* own; // every Widget belongs to a Window
Fl_Widget* pw; // a Widget “knows” its Fl_Widget
};
Window own;
This will copy the whole Window object. If the real window changes, this widget will have an old and useless copy.
Window & own;
A reference. We meet two issues:
A) We can not know if the window has been deleted outside the widget code
B) The window must exists before this widget
Window * own;
A pointer can be NULL, as opposed to a reference. This avoids reference issues.
The book seems a bit old, just because it uses raw-pointers.
EDIT due to comments
It is absolutely true that issue "A)" is the same for references and pointers. But it can be easier handled with a pointer.
What I wanted to point to is that when a window is deleted, the widget must be informed about it, so as to not use the "own" object.
With a pointer, it can be resetted to NULL, so any further attemp to use "own" can be easily catched. But with a reference you need, at least, an extra bool in your code just to store if "own" is valid or not.
First, we need to work out the logical and implementation details. On the logical side, a window contains widgets: just like a mother has children. This is a one to many relationship. Each widget has an implementation entity, in this case an Fl_widget. It could equally be a MS windows Window, a QtWidget or and X-Windows Widget. It is one of those implementation dependent things
___________
|window |
| _______ | _________
| |widget-|-|--->|Fl_widget|
| _______ | _________
| |widget-|-|--->|Fl_widget|
|___________|
Within window itself, there will also be an implementation detail like another Fl_Widget or Fl_window.
The reason why we cannot have Window win instead of Window win* is because the window owns the widget and not the other way round. If we had Window win then whenever any window property changed, you'd have to modify all the widgets containing the same window. Taking, the mother child relationship, if the mother had a haircut, the mother member for all the children of the same mother would have to have a haircut. You'd have to have a method to keep track of and update all the mothers of all the children. This would just be a maintenance nightmare.
If we just hold a pointer to the window then we know that if the window is modified, all sibling widgets will be able to query the change in the parent window and get the same answer without a lot of effort.
The second part of your question about moving widgets between windows - I've never seen anyone do that. In all the implementations I have ever used, this is impossible. The parent window owns the widget for life. To "move it", you'd normally have to reincarnate it i.e. destroy it from the current window and re-create it in the new window.
Related
I am writing a C++ MFC application to control a machine in a manufacturing setting. This app also needs to analyze a lot of information in a very short cycle time.
For testing purposes and long term maintenance, I need to be able to graph data coming from a sensor on the console. I may have totally overlooked an option (feel free to propose other options) but my research has taken me to using a picture control.
I am successfully drawing in this control by use of OnPaint(). My issue is that I need to redraw a new image every few seconds and I cannot call OnPaint() repetitively or pass data to it.
How can I create a new function that can be used to draw on the picture control repetitively? Also, this is my first foray into an MFC app so please explain on an appropriate level. Thanks!
class CPicture : public CStatic
{
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnPaint();
};
BEGIN_MESSAGE_MAP(CPicture, CStatic)
ON_WM_PAINT()
END_MESSAGE_MAP()
void CPicture::OnPaint()
{
CPaintDC dc(this); // device context for painting
dc.SelectStockObject(BLACK_BRUSH);
dc.Rectangle(5, 50, 1000, 51);
}
I guess the question is how and where to access this
//Picture
class CPicture : public CStatic
{
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnPaint();
vector<Coordinates> GraphData;
};
void CPicture::OnPaint()
{
// device context for painting
CPaintDC dc(this);
// save current brush
CBrush *pOldBrush = (CBrush*)dc.SelectStockObject(BLACK_BRUSH);
int NumPoints = GraphData.size() - 1;
for (int N = 0; N <= NumPoints; N++) {
dc.Rectangle(GraphData[N].x, GraphData[N].y, GraphData[N].x, GraphData[N].y);
}
// select original brush into device contect
dc.SelectObject(pOldBrush);
}
You can call Invalidate() on your control when new data arrives, or use RedrawWindow() to force an immediate redraw:
CPicture myPicture;
myPicture.Invalidate();
or
myPicture.RedrawWindow();
I cannot call OnPaint() repetitively or pass data to it.
To pass data, a structure containg the data can be declared inside your CPicture class (or some place else in your program), and that data can then be accessed from within OnPaint():
struct myData {
int value1;
int value2; // or an array, or some other data structure
}
class CPicture : public CStatic
{
DECLARE_MESSAGE_MAP()
public:
myData m_data;
afx_msg void OnPaint();
};
In OnPaint() (you should also select the original brush back into the device context to avoid resource leaks):
void CPicture::OnPaint()
{
CPaintDC dc(this); // device context for painting
// save current brush
CBrush *pOldBrush = (CBrush*)dc.SelectStockObject(BLACK_BRUSH);
// check pOldBrush - could be NULL
// dc.Rectangle(5, 50, 1000, 51);
// access m_data here, for example
dc.Rectangle(m_data.value1, m_data.value2, 1000, 51);
// select original brush into device contect
dc.SelectObject(pOldBrush);
}
Update (working with threads):
Assuming the following (from the comments):
for the main thread you have a dialog CLongbowDlg.
for the graph, you have a PicControl derived from CStatic, and that control is placed on the dialog.
from the main thread, a worker thread is started to read the data.
PicControl and CLongbowDlg are defined in the same header, but are
independent of each other. I need to be able to call Invalidate() or
RedrawWindow() from inside CLongbowDlg's functions because they
represent the primary thread.
I'll try to give a short description of one of the possibilities here, because this should actually be a seperate question.
Firstly, an object of PicControl has to be a member of CLongbowDlg, which I assume is the case (let's call it m_PicControl) - So, in class CLongbowDlg:
PicControl m_PicControl;
For the data (I'll be using the above myData as example data): in your main thread (the Dialog), create a variable of type myData: m_data (for larger data you could allocate space on the heap, or use CArray or some other container):
myData m_data;
In PicControl create a member variable of type myData* and set it to NULL in the PicControl constructor.
myData *m_pData;
In OnInitDialog() (main dialog), provide m_picControl with a pointer to the data (or better create a function to do that in PicControl):
m_picControl.m_pData = &m_data;
When starting the worker thread, also provide it a pointer to m_data and/or a pointer to the dialog itself (this).
Make sure to protect the data with a critical section.
When data comes in, the worker thread can write to it via the provided pointer.
In PicControl::OnPaint(), the same data can be accessed through m_pData.
To initiate a redraw, there are several ways:
use a timer inside PicControl or in the main dialog, and call Invalidate() every time the timer fires.
to control the redrawing from the worker thread (when a certain amount of new data has arrived for example) a message can be posted, using PostMessage(), to the main dialog (using the pointer that was provided when starting the thread - the this pointer).
To receive the message you'll have to create a message handler in the main dialog, and from there call Invalidate() on m_picControl (you could also post a message directly to PicControl, but I prefer to do it via the main window).
The Code
I've been writing this c++ with Qt knowing it works but not really understanding why I sometimes do things other than "I just know I should be doing this".
This is my startup class which initialises my classes:
namespace GUI
{
Startup::Startup() :
QObject(nullptr),
m_setupTab(*new SetupTab(nullptr)),
m_regTab(*new CbcRegistersTab(nullptr)),
m_dataTab(*new DataTestTab(nullptr)),
m_mainView(*new MainView(nullptr,
m_setupTab,
m_regTab,
m_dataTab)),
m_systemController(*new SystemController(nullptr,
Provider::getSettingsAsSingleton())),
m_dataTest(*new DataTest(nullptr,
m_systemController)),
m_setupTabVm(new SetupTabViewManager(this,
m_setupTab,
m_systemController,
Provider::getSettingsAsSingleton() ))
{
}
Then in my header file the member variables are described as such:
private:
SetupTab& m_setupTab;
CbcRegistersTab& m_regTab;
DataTestTab& m_dataTab;
MainView& m_mainView;
Settings* m_settings;
SystemController& m_systemController;
DataTest& m_dataTest;
SetupTabViewManager* m_setupTabVm;
The main difference between the view manager and everything else is that view manager passes signals between the tab classes and everything else.
Then to start this in my main, all I do is this:
GUI::Startup startup;
startup.show();
SetupTabViewManager.cpp:
#include "setuptabviewmanager.h"
#include "View/setuptab.h"
#include "Model/systemcontroller.h"
#include "Model/settings.h"
#include <QDebug>
namespace GUI
{
SetupTabViewManager::SetupTabViewManager(QObject *parent,
SetupTab& tab,
SystemController& sysCtrl,
Settings& config) :
QObject(parent),
m_setupTab(tab),
m_systemController(sysCtrl)
{
WireMessages(config);
WireSetupTabButtons(config);
}
void SetupTabViewManager::WireMessages(Settings& config)
{
connect(&config, SIGNAL(notifyStatusMessage(QString)), //for QT4
&m_setupTab, SLOT(onStatusUpdate(QString)) );
connect(&m_systemController, SIGNAL(notifyStatusMessage(QString)),
&m_setupTab, SLOT(onStatusUpdate(QString)));
}
void SetupTabViewManager::WireSetupTabButtons(Settings& config)
{
connect(&m_setupTab, SIGNAL(onBtnLoadSettingsClicked(bool)),
&config, SLOT(onLoadButtonClicked(bool)) );
connect(&config, SIGNAL(setHwTree(QStandardItemModel*)),
&m_setupTab, SLOT(setHwTreeView(QStandardItemModel*)));
connect(&m_setupTab, SIGNAL(onBtnInitClicked()),
&m_systemController, SLOT(startInitialiseHw()));
connect(&m_setupTab, SIGNAL(onBtnCfgClicked()),
&m_systemController, SLOT(startConfigureHw()));
}
}
Questions
What is the advantage of initialising a class in a member variable as a pointer or a reference? I just know when I make a "view manager", I should initialise the member variable as a pointer. Though I'm not sure why?
Also what is the advantage of "this" or "nullptr" as the parent to the class?
Qt objects are organized in object trees. It allows a programmer not to care about destructing the created objects: they will be deleted automatically when their respective parents are deleted.
If you take a look at almost any Qt GUI application, then you will see that all of its widgets (buttons, checkboxes, panels, etc.) have one single ancestor - the main window. When you delete the main window, all your application widgets are automatically deleted.
You can set up this parent-child relation by passing a parent object to a newly created child:
QWidget *mainWidget = ...
QPushButton *btn = new QPushButton(mainWidget);
In this case, btn object becomes a child of the mainWidget object, and from this moment you may not delete that child manually.
If you create an object without a parent, you should always delete it by yourself.
As thuga mentioned in the comments, you can transfer object ownership later. Widgets can be places inside layouts and it will make them children of the layout owner. Or you can even explicitly set parent with QObject::setParent.
And as for declaring class members as pointers or references - it does not really matter. It's just a question of convenience.
In Qt world all child objects are getting deleted when their parent object is deleting. If you do not define class members (QObjects) as pointers, they will be deleted twice: once by their parent, and second time when your class object is destructed by itself, and this can lead to undefined behavior.
so my problem isn't as much about the code, but about the way to do it.
I'm working on a GUI and I want my buttons to know who there parent is. And of course, the window knows which buttons it has.
This creates a circular dependency, since both need to be able to access the others methods and attributes, at least that's what I would prefer.
I found a solution that works, but I'm not very happy with it:
I created a third object to which button writes, what it wants the window to do. And the window checks this third object for commands.
I wanted to ask you, if you know of a better way to this, since I couldn't find any other way, that works for me.
Thanks!
I'd suggest create a window interface. Provide a back pointer to the window interface in the constructor of the button. The window that owns the button depends on the button and the button depends on the window interface. No circular dependency.
struct IWindow {
};
struct Button {
IWindow* window_;
Button(IWindow* window) : window_(window){}
};
struct WindowWithButton : IWindow {
Button button_;
WindowWithButton() : button_(this) {}
};
Then add virtual methods to IWindow that are implemented by WindowWithButton so that Button can get the information it needs from the WindowWithButton.
It's a standard pattern:
struct Window; // Forward-declare the parent
struct Button {
void pingParent(); // Only declare members which need
// more than superficial knowledge of Window
Window* parent; // Ok, we know there is a Window, somewhere
};
struct Window {
unique_ptr<Button> child;
// Other functions using the button
void pingpong() {child->pingParent();}
void ping(){}
};
/*optional inline*/ void Button::pingParent() {
parent->ping();
}
I've been trying to program in C++ a sort of simple "window" system for use in a game, which draws windows that can have buttons, etc. in them in the game area (internal to the game's own graphics, i.e. not the OS's GUI windows). The window objects (call it "class Window" for here) have some methods for events like key press, and the ability to hook on a handler to be called upon receipt of that event.
Windows are (or will be) collected in a "window manager", and the window object will have "close()" member that would call the parent window manager's window-deletion routine to delete itself. An event handler hooked to, say, a button on the window might invoke this routine to close the window (think an "OK" box).
The trouble is this sounds like a "delete *this;" statement, which I've heard is a no-no. True, it doesn't do that directly, but the effect is the same: an object has a member function that brings about its own destruction (e.g. the "close()" function, or the event function that triggers the handler leading to the "close()" function being called.). If this is bad, then what is a better way to design this?
There is nothing wrong with an object deleting itself. You must simply tell the window manager to remove the window from it's collection and then delete. If you have the window manager delete the window object, that's even better.
If you really want to avoid this behavior, you can add a bool dead; to each window that initializes to false. When the window is to be closed, set this->dead = true;. Every frame, have the window manager iterate through it's windows and delete the ones that are dead.
Note that this solution still does not fix errors that arise from external systems that have a reference to the deleted window, but it does have the advantage of centralizing the deletion of windows.
I have designed many games' window systems, and in my experience, allowing windows to delete themselves is a very elegant solution, even if it is more error-prone.
A minimal example:
class Window
{
public:
void keyPressCallback(int c)
{
if (c == KEY_ESC)
{
manager.destroy(this);
return;
}
}
WindowManager& manager;
};
class WindowManager
{
public:
void destroy(Window* target)
{
delete target;
windows.erase(std::find(windows.begin(), windows.end(), target));
}
std::vector<Window*> windows;
};
As long as there are no remaining pointers to that window, this method is perfectly safe and semantically sane. When the window receives a signal to close, it closes itself.
The same example with the dead flag:
class Window
{
public:
Window() : dead(false) {}
void keyPressCallback(int c)
{
if (c == KEY_ESC)
{
dead = true;
return;
}
}
bool dead;
};
class WindowManager
{
public:
void cleanup()
{
for (auto iter = windows.begin(); iter != windows.end(); ++iter)
{
if (iter->dead) windows.erase(iter);
}
}
std::vector<Window*> windows;
};
I have some custom control inside of which i should create radiobuttons or checkboxes. The count of child controls is available only at runtime (it loads some file from which it gets this count). So i need to create variable number of controls. Which collection i should use for this purpose?
Solution 1: simply use std::vector<HWND> (or CArray<HWND>) - not suitable because i want use MFC (CButton). Of course i can Attach() and later Detach() handle to window each time i need this window, but it will give big overhead.
Solution 2: use std::vector<CButton*> or CArray<CButton*> or CList<CButton*> or... In this case i take care about making 'new' and appropriate 'delete' when control is unneeded. I am forgetful :)
MFC handle map contains pointer to CButton and i can't use simple CArray<CButton>, because it will move my objects each time when his size will grow.
... and the question is:
Which collection i should use for containing variable count of MFC control classes?
If you only want to read your file with the Count parameter, create your buttons, work with them and later delete them all then CArray<CButton*> is fine in my opinion. To make sure the buttons get deleted you could wrap the CArray into a helper like:
class CMyButtonArrayWrapper
{
public:
CMyButtonArrayWrapper();
virtual ~CMyButtonArrayWrapper();
void ClearArray();
void Add(CButton* theButton);
private:
CArray<CButton*> m_Array;
}
CMyButtonArrayWrapper::CMyButtonArrayWrapper()
{
}
CMyButtonArrayWrapper::~CMyButtonArrayWrapper()
{
ClearArray();
}
void CMyButtonArrayWrapper::ClearArray()
{
for (int i=0; i<m_Array.GetSize(); i++)
{
CButton* aButton=m_Array.GetAt(i);
if (aButton)
delete aButton;
}
m_Array.RemoveAll();
}
void CMyButtonArrayWrapper::Add(CButton* theButton)
{
m_Array.Add(theButton);
}
Then add an object of this class as a member to your custom control (m_MyButtonArrayWrapper) and add your buttons with:
CButton* aButton=new CButton;
aButton->Create( ... );
m_MyButtonArrayWrapper.Add(aButton);
If you need to add and remove buttons randomly a CList might be better suited for performance reasons. (But you won't probably notice a performance difference using InsertAt/RemoveAt of CArray, except your UI has several thousands of buttons. I guess this would be more an artwork than a user interface :))