原文链接为:https://www.peter.hartmann.tk/single-post/2018/09/18/Meet-QSkinny-a-lightweight-Qt-UI-library
September 18, 2018
Peter Hartmann
TL;DR: QSkinny offers a QWidget-like library built on top of the modern Qt graphic stack. It is using the Qt scene graph and is written fully in C++, thus making QML optional.
QSkinny offers a Qt API to write user interfaces in C++. It is inspired by the class design of QtWidgets, but runs on top of QtQuick. This means that QSkinny is hardware accelerated and can make use of e.g. animations and shaders. Below is a screenshot of a sample UI written with QSkinny:
1. How does it work?
Check out a simple "hello world" program written with QSkinny:
int main(int argc, char** argv)
{
QGuiApplication app(argc, argv);
QskWindow window;
window.resize(200, 200);
auto box = new QskLinearBox(Qt::Vertical);
auto button = new QskPushButton("push me", box);
auto label = new QskTextLabel("label", box);
window.addItem(box);
window.show();
return app.exec();
}
Looks familiar? Users of QtWidgets code will feel right at home when using QSkinny's API: There are similar controls in both worlds like text labels, push buttons, layouts, dialogs etc.
This diagram shows how QSkinny, QML and QtWidgets relate:
The layers in the diagram above are:
QSkinny: C++ UI controls
QML engine: declarative / JavaScript engine to parse UI files
QtQuick: basic layer of UI controls (containing e.g. x/y positioning and focus handling)
Qt scene graph: low level drawing primitives to make use of hardware acceleration
OpenGL: API to support hardware accelerated drawing
QtWidgets: C++ UI controls designed for desktop use
Qt raster paint engine: software (i.e. not hardware accelerated) drawing engine
QPainter API: interface for drawing images, text, shapes etc.
Since both QSkinny and QML elements are instances of QQuickItem, both technologies can be mixed: The QSkinny "buttons" example for instance is using a QskPushButton from QML.
2. Where is the code?
The code lives on github and is licensed under LGPLv2:
https://github.com/uwerat/qskinny
Its original authors are Uwe Rathmann and Andrew Knight, the author of this blog post started contributing later.
3. Why is it called QSkinny?
It is slim. The sample screenshot above shows 3 speedometers, each one of them consists of one QQuickItem, which itself contains several scene graph nodes: There is one node for the background one for the needle, one for the labels etc. In QML, each subcontrol is a QQuickItem and therefor a QObject.
It separates the functionality of controls from their appearance; the latter being handled by so called Skinlets. Those Skinlets live on the scene graph thread and handle the actual drawing. How exactly they are drawn is determined by a so called Skin, and can be changed at runtime. This makes it easy to implement e.g. a daylight vs. nighttime theme or different brand schemes:
As an example, here is a skin setting all push buttons to have blue text on green background with a 10 pixel padding:
setGradient( QskPushButton::Panel, Qt::green );
setColor( QskPushButton::Text, Qt::blue );
setMargins( QskPushButton::Panel | QskAspect::Padding, 10 );
Those skin properties are similar to properties in QML.
Upon a skin change, a programmer would just replace the colors, padding etc. with different values and then trigger a repaint; the according animation can even interpolate between colors, as seen above (reduced frame rate due to GIF compression).
4. How mature is it?
QSkinny is currently used in a major automotive project which unfortunately cannot be shown in public yet. This means it is being stress tested for production, but still lacking in areas like documentation; moreover the controls currently implemented are aligned to the project needs so far.
Mixing QSkinny and QML is in a proof-of-concept state, because the application mentioned above is purely written in C++ and not using QML in any way.
This project is showing very good performance numbers, especially a fast startup time and low memory usage. Considering that there are lots of controls loaded right at application startup, those things do not seem to be an issue with QSkinny (at least for this project).
Also, since the developers working on it came from a QtWidgets background, they were familiar with the underlying concepts and productive right away with QSkinny.
Do you want to try it out? Just clone the repository above and let us know how it goes!
Contributions (source code changes, documentation etc.) will of course also be appreciated.