KDWinUtils
Helper library for MFC to Qt migration
|
The KDWinUtils library provides classes with an API close the the MFC API, but using Qt underneath. Some high-level concepts like message maps or data exchange are migrated to a Qt equivalent.
The library is split between:
corelib
: aimed at replacing data primitiveswidgets
: aimed at replacing everything UI-relatedAny methods starting with an uppercase letter (DoSomething
) are coming from the MFC API. If a method starts with a lowercase letter (doSomething
), it's a new API.
To match the Qt API, new APIs that are handling callbacks (for message handling, command updating...) start with connect
.
Make sure to read the documentation of the different classes as there may be some small differences and API changes.
Note: Some API may be missing in those classes.
MFC class | KDWinUtils class | Comment |
---|---|---|
CImageList | KImageList | |
CPoint | KPoint | |
CRect | KRect | |
CSize | KSize | |
CString | KString | No separation between unicode and non-unicode |
CStringArray | KStringArray |
All those primitive classes have implicit and explicit conversion methods from/to their MFC and Qt equivalent.
MFC class | KDWinUtils class | Comment |
---|---|---|
CBrush | KBrush | |
KButtonGroup | Use for DDX_Radio | |
CCheck | KCheck | |
CComboBox | KComboBox | |
KCommandManager | Use for handling menubars and toolbars | |
CDialog | KDialog | |
CEdit | KEdit | For single line edit |
CEdit | KMultiEdit | For multi-line edit |
CFont | KFont | |
CHeaderCtrl | KHeaderCtrl | |
CListCtrl | KListModel | |
CMenu | KMenu | |
KNumber | Use for DDX_Text linked to an integral or floating point member | |
CDC | KPainter | The class using for drawing in Qt is QPainter |
CPen | KPen | |
CPropertyPage | KPropertyPage | |
CPropertySheet | KPropertySheet | |
CSliderCtrl | KSliderCtrl | |
KSortFilterProxyModel | Use internally by KListModel for sorting and filtering | |
CTabCtrl | KTabCtrl | |
KText | Use for DDX_Text linked to a string | |
CWnd | KWidget | QWidget is the base class for all graphical items |
CSpinButtonCtrl | KSpinButtonCtrl |
The RC file contains different items:
All those items are migrated in different ways, using Qt paradigms.
All dialogs have a description in the RC file, with the position and size of all the controls in the dialog. Qt also has a way to describe a dialog, using UI files, which can easily be edited graphically using the Qt Designer.
The migration will transform the description from the RC file into a Qt UI file. Note that contrary to MFC, there's one UI file per dialog.
In MFC RC files, strings are typically handled by referencing them through resource IDs. These IDs are assigned to strings within the RC file and then used to access the corresponding string resources programmatically.
However, in Qt, there's no direct equivalent to MFC RC files for managing string resources. Instead, Qt relies on other mechanisms like internationalization (i18n) and Qt Linguist for handling translations and managing strings.
To bridge this gap and enable similar functionality, a ResourceHelper
file is created with a Helper::GetString
method returning the string from its ID. This will typically allow using the Qt translation system, while still using ID-based string access.
Qt already has a system in place to deal with assets (icons, images...): the Qt Resource System. The major difference with MFC is that MFC uses resource IDs to reference and access resources.
To bridge this gap and enable similar functionality, a ResourceHelper
file is created with a Helper::GetAsset
method returning the asset from its ID.
Note: the qrc file is quite easy to create from a script, it is however advised to convert the BMP images to PNG images, as well as making them semi-transparent if needed (instead of relying on specific colors like Magenta).
TODO
The CWnd
is the base class for all graphical objects in MFC, particularly it's the base class for CDialog
. Within KDWinUtils, their equivalent are respectively KWidget
and KDialog
: in Qt, the base class for any graphical objects is a QWidget
.
But contrary to MFC, those classes are not directly inherited from one to another, as we already have a Qt inheritance hierarchy in place:
This has an impact on the GetParent
method, you now have 3 methods:
GetParent<T>
: template method to retrieve the direct parent,GetParentWidget
: returns the parent as a KWidget
, or nullptr
if it's not one,GetParentDialog
: goes up in the parent-child tree to find the first KDialog
and returns it, or nullptr
if none.Message map is a corner feature for any control or window (CWnd
, CDialog
...) in MFC. They are used mainly for 2 different scenarios:
Those 2 scenarios map perfectly to Qt own paradigms:
So a simple message map:
will be migrated in the code below:
It is possible to send (synchronous) and post (asynchronous) messages to a specific control or window. This is done with the SendMessage
and PostMessage
APIs. Those messages are either handled from the message map (with ON_MESSAGE
) or using PreTranslateMessage
.
In KDWinUtils, the SendMessage
and PostMessage
APIs still exist (see in KWidgetBase
). The way it's done is that it's going to create a Qt event, and either send it directly to the widget (synchronous) or post it on the event loop (asynchronous). The handling is done:
ConnectMessage
to replace the ON_MESSAGE
QObject::event
to replace PreTranslateMessage
Warning: There are no
PeekMessage
, as it's not possible with Qt. Such a code need to be changed.
Data exchange is a concept only available in MFC, there are no such things in Qt. MFC has some kind of model-view architecture, with explicit synchronization points. In Qt, there are no such things, developers are using directly the widgets and their API in the code.
To simplify the migration, we keep the members (a CComboBox
becomes a KComboBox
), but those are just proxies on top of existing widgets. The DoDataExcahnge
method is used to link those proxies with their widgets.
For example, if we have a combo box in a dialog, we will have something like this:
Then, when the developer calls m_comboCtrl.AddString("foo")
, he is calling the corresponding Qt API on the combo box, in this case m_ui->comboBox->addItem("foo")
.
It would be possible to remove all members and only use the Qt API, but that would require changing a lot of code. The code is still readable as is, and you can always manipulate the widget directly, without any harm.
Drawing is done using primitives close to the MFC primitives:
MFC class | KDWinUtils class |
---|---|
CBrush | KBrush |
CFont | KFont |
CDC / CPaintDC | KPainter |
CPen | KPen |
Contrary to the MFC API, the preferred way to work in Qt is to create objects (pen, brush...) on the stack, beyond that the general usage is the same.
The biggest difference is that Qt is using a double buffer by default, and there's no immediate painting possible. All paint operations need to be done in the paintEvent
method of widgets.