Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qquickqmlgenerator.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5#include "qquicknodeinfo_p.h"
6#include "utils_p.h"
7
8#include <private/qsgcurveprocessor_p.h>
9#include <private/qquickshape_p.h>
10#include <private/qquadpath_p.h>
11#include <private/qquickitem_p.h>
12#include <private/qquickimagebase_p_p.h>
13
14#include <QtCore/qloggingcategory.h>
15#include <QtCore/qdir.h>
16
18
19Q_DECLARE_LOGGING_CATEGORY(lcQuickVectorImage)
20
22{
23public:
25 : m_array(new QByteArray())
26 , m_stream(new QTextStream(m_array, QIODeviceBase::ReadWrite))
27 , m_resultStream(result)
28 {}
29
31 {
32 if (m_stream) {
33 m_stream->flush();
34 delete m_stream;
35 }
36
37 if (m_resultStream && m_array && !m_array->isEmpty())
38 *m_resultStream << *m_array << Qt::endl;
39
40 delete m_array;
41 }
42
44 : m_array(std::exchange(other.m_array, nullptr))
45 , m_stream(std::exchange(other.m_stream, nullptr))
46 , m_resultStream(std::exchange(other.m_resultStream, nullptr))
47 {}
49 {
50 std::swap(m_resultStream, other.m_resultStream);
51 std::swap(m_stream, other.m_stream);
52 std::swap(m_array, other.m_array);
53
54 return *this;
55 }
56
57 Q_DISABLE_COPY(GeneratorStream)
58private:
59 template<typename T>
60 friend const GeneratorStream &operator<<(const GeneratorStream& str, T val);
61 QByteArray *m_array = nullptr;
62 QTextStream *m_stream = nullptr;
63 QTextStream *m_resultStream = nullptr;
64};
65
66template<typename T>
68{
69 *str.m_stream << val;
70 return str;
71}
72
73QQuickQmlGenerator::QQuickQmlGenerator(const QString fileName, QQuickVectorImageGenerator::GeneratorFlags flags, const QString &outFileName)
75 , outputFileName(outFileName)
76{
77 m_stream = new QTextStream(&result);
78}
79
81{
82 if (!outputFileName.isEmpty()) {
83 QFile outFile(outputFileName);
84 outFile.open(QIODevice::WriteOnly);
85 outFile.write(result);
86 outFile.close();
87 }
88
89 if (lcQuickVectorImage().isDebugEnabled()) {
90 result.truncate(300);
91 qCDebug(lcQuickVectorImage).noquote() << result;
92 }
93}
94
96{
97 m_shapeTypeName = name.toLatin1();
98}
99
101{
102 return QString::fromLatin1(m_shapeTypeName);
103}
104
106{
107 m_commentString = commentString;
108}
109
111{
112 return m_commentString;
113}
114
116{
117 m_indentLevel++;
118 if (!info.nodeId.isEmpty())
119 stream() << "objectName: \"" << info.nodeId << "\"";
120 if (!info.isDefaultTransform) {
121 auto sx = info.transform.m11();
122 auto sy = info.transform.m22();
123 auto x = info.transform.m31();
124 auto y = info.transform.m32();
125 if (info.transform.type() == QTransform::TxTranslate) {
126 stream() << "transform: Translate { " << "x: " << x << "; y: " << y << " }";
127 } else if (info.transform.type() == QTransform::TxScale && !x && !y) {
128 stream() << "transform: Scale { xScale: " << sx << "; yScale: " << sy << " }";
129 } else {
130 const QMatrix4x4 m(info.transform);
131 {
132 stream() << "transform: [ Matrix4x4 { matrix: Qt.matrix4x4 (";
133 m_indentLevel += 3;
134 const auto *data = m.data();
135 for (int i = 0; i < 4; i++) {
136 stream() << data[i] << ", " << data[i+4] << ", " << data[i+8] << ", " << data[i+12] << ", ";
137 }
138 stream() << ") } ]";
139 m_indentLevel -= 3;
140 }
141 }
142 }
143 if (!info.isDefaultOpacity) {
144 stream() << "opacity: " << info.opacity;
145 }
146 m_indentLevel--;
147}
148
150{
152
153 return false;
154}
155
157{
158 if (!isNodeVisible(info))
159 return;
160
161 const QFileInfo outputFileInfo(outputFileName);
162 const QDir outputDir(outputFileInfo.absolutePath());
163
164 QString filePath;
165
166 if (!m_retainFilePaths || info.externalFileReference.isEmpty()) {
167 filePath = m_assetFileDirectory;
168 if (filePath.isEmpty())
169 filePath = outputDir.absolutePath();
170
171 if (!filePath.isEmpty() && !filePath.endsWith(u'/'))
172 filePath += u'/';
173
174 QDir fileDir(filePath);
175 if (!fileDir.exists()) {
176 if (!fileDir.mkpath(QStringLiteral(".")))
177 qCWarning(lcQuickVectorImage) << "Failed to create image resource directory:" << filePath;
178 }
179
180 filePath += QStringLiteral("%1%2.png").arg(m_assetFilePrefix.isEmpty()
181 ? QStringLiteral("svg_asset_")
182 : m_assetFilePrefix)
183 .arg(info.image.cacheKey());
184
185 if (!info.image.save(filePath))
186 qCWarning(lcQuickVectorImage) << "Unabled to save image resource" << filePath;
187 qCDebug(lcQuickVectorImage) << "Saving copy of IMAGE" << filePath;
188 } else {
189 filePath = info.externalFileReference;
190 }
191
192 const QFileInfo assetFileInfo(filePath);
193
194 // TODO: this requires proper asset management.
195 stream() << "Image {";
196 m_indentLevel++;
197
199 stream() << "x: " << info.rect.x();
200 stream() << "y: " << info.rect.y();
201 stream() << "width: " << info.rect.width();
202 stream() << "height: " << info.rect.height();
203 stream() << "source: \"" << outputDir.relativeFilePath(assetFileInfo.absoluteFilePath()) <<"\"";
204
205 m_indentLevel--;
206
207 stream() << "}";
208}
209
211{
212 if (!isNodeVisible(info))
213 return;
214
215 if (m_inShapeItem) {
216 if (!info.isDefaultTransform)
217 qWarning() << "Skipped transform for node" << info.nodeId << "type" << info.typeName << "(this is not supposed to happen)";
219 } else {
220 m_inShapeItem = true;
221 stream() << shapeName() << " {";
222
223 // Check ??
225
226 m_indentLevel++;
228 stream() << "preferredRendererType: Shape.CurveRenderer";
230 //qCDebug(lcQuickVectorGraphics) << *node->qpath();
231 m_indentLevel--;
232 stream() << "}";
233 m_inShapeItem = false;
234 }
235}
236
237void QQuickQmlGenerator::generateGradient(const QGradient *grad, const QRectF &boundingRect)
238{
239 if (grad->type() == QGradient::LinearGradient) {
240 auto *linGrad = static_cast<const QLinearGradient *>(grad);
241 stream() << "fillGradient: LinearGradient {";
242 m_indentLevel++;
243
244 QRectF gradRect(linGrad->start(), linGrad->finalStop());
245 QRectF logRect = linGrad->coordinateMode() == QGradient::LogicalMode ? gradRect : QQuickVectorImageGenerator::Utils::mapToQtLogicalMode(gradRect, boundingRect);
246
247 stream() << "x1: " << logRect.left();
248 stream() << "y1: " << logRect.top();
249 stream() << "x2: " << logRect.right();
250 stream() << "y2: " << logRect.bottom();
251 for (auto &stop : linGrad->stops())
252 stream() << "GradientStop { position: " << stop.first << "; color: \"" << stop.second.name(QColor::HexArgb) << "\" }";
253 m_indentLevel--;
254 stream() << "}";
255 } else if (grad->type() == QGradient::RadialGradient) {
256 auto *radGrad = static_cast<const QRadialGradient*>(grad);
257 stream() << "fillGradient: RadialGradient {";
258 m_indentLevel++;
259
260 stream() << "centerX: " << radGrad->center().x();
261 stream() << "centerY: " << radGrad->center().y();
262 stream() << "centerRadius: " << radGrad->radius();
263 stream() << "focalX:" << radGrad->focalPoint().x();
264 stream() << "focalY:" << radGrad->focalPoint().y();
265 for (auto &stop : radGrad->stops())
266 stream() << "GradientStop { position: " << stop.first << "; color: \"" << stop.second.name(QColor::HexArgb) << "\" }";
267 m_indentLevel--;
268 stream() << "}";
269 }
270}
271
273{
274 Q_UNUSED(pathSelector)
275 Q_ASSERT(painterPath || quadPath);
276
277 const bool noPen = info.strokeStyle.color == QColorConstants::Transparent;
278 if (pathSelector == QQuickVectorImageGenerator::StrokePath && noPen)
279 return;
280
281 const bool noFill = info.grad.type() == QGradient::NoGradient && info.fillColor == QColorConstants::Transparent;
282
283 if (pathSelector == QQuickVectorImageGenerator::FillPath && noFill)
284 return;
285
286 auto fillRule = QQuickShapePath::FillRule(painterPath ? painterPath->fillRule() : quadPath->fillRule());
287 stream() << "ShapePath {";
288 m_indentLevel++;
289 if (!info.nodeId.isEmpty()) {
290 switch (pathSelector) {
292 stream() << "objectName: \"svg_fill_path:" << info.nodeId << "\"";
293 break;
295 stream() << "objectName: \"svg_stroke_path:" << info.nodeId << "\"";
296 break;
298 stream() << "objectName: \"svg_path:" << info.nodeId << "\"";
299 break;
300 }
301 }
302
303 if (noPen || !(pathSelector & QQuickVectorImageGenerator::StrokePath)) {
304 stream() << "strokeColor: \"transparent\"";
305 } else {
306 stream() << "strokeColor: \"" << info.strokeStyle.color.name(QColor::HexArgb) << "\"";
307 stream() << "strokeWidth: " << info.strokeStyle.width;
308 stream() << "capStyle: " << QQuickVectorImageGenerator::Utils::strokeCapStyleString(info.strokeStyle.lineCapStyle);
309 stream() << "joinStyle: " << QQuickVectorImageGenerator::Utils::strokeJoinStyleString(info.strokeStyle.lineJoinStyle);
310 stream() << "miterLimit: " << info.strokeStyle.miterLimit;
311 if (info.strokeStyle.dashArray.length() != 0) {
312 stream() << "strokeStyle: " << "ShapePath.DashLine";
313 stream() << "dashPattern: " << QQuickVectorImageGenerator::Utils::listString(info.strokeStyle.dashArray);
314 stream() << "dashOffset: " << info.strokeStyle.dashOffset;
315 }
316 }
317
318 if (!(pathSelector & QQuickVectorImageGenerator::FillPath)) {
319 stream() << "fillColor: \"transparent\"";
320 } else if (info.grad.type() != QGradient::NoGradient) {
321 generateGradient(&info.grad, boundingRect);
322 } else {
323 stream() << "fillColor: \"" << info.fillColor.name(QColor::HexArgb) << "\"";
324 }
325 if (fillRule == QQuickShapePath::WindingFill)
326 stream() << "fillRule: ShapePath.WindingFill";
327 else
328 stream() << "fillRule: ShapePath.OddEvenFill";
329
330 QString hintStr;
331 if (quadPath)
333 if (!hintStr.isEmpty())
334 stream() << hintStr;
335
336
338 stream() << "PathSvg { path: \"" << svgPathString << "\" }";
339
340 m_indentLevel--;
341 stream() << "}";
342}
343
345{
346 if (!isNodeVisible(info))
347 return;
348
349 stream() << "// Missing Implementation for SVG Node: " << info.typeName;
350 stream() << "// Adding an empty Item and skipping";
351 stream() << "Item {";
353 stream() << "}";
354}
355
357{
358 if (!isNodeVisible(info))
359 return;
360
361 static int counter = 0;
362 stream() << "Item {";
364 m_indentLevel++;
365
366 if (!info.isTextArea)
367 stream() << "Item { id: textAlignItem_" << counter << "; x: " << info.position.x() << "; y: " << info.position.y() << "}";
368
369 stream() << "Text {";
370
371 m_indentLevel++;
372
373 if (info.isTextArea) {
374 stream() << "x: " << info.position.x();
375 stream() << "y: " << info.position.y();
376 if (info.size.width() > 0)
377 stream() << "width: " << info.size.width();
378 if (info.size.height() > 0)
379 stream() << "height: " << info.size.height();
380 stream() << "wrapMode: Text.Wrap"; // ### WordWrap? verify with SVG standard
381 stream() << "clip: true"; //### Not exactly correct: should clip on the text level, not the pixel level
382 } else {
383 QString hAlign = QStringLiteral("left");
384 stream() << "anchors.baseline: textAlignItem_" << counter << ".top";
385 switch (info.alignment) {
386 case Qt::AlignHCenter:
387 hAlign = QStringLiteral("horizontalCenter");
388 break;
389 case Qt::AlignRight:
390 hAlign = QStringLiteral("right");
391 break;
392 default:
393 qCDebug(lcQuickVectorImage) << "Unexpected text alignment" << info.alignment;
395 case Qt::AlignLeft:
396 break;
397 }
398 stream() << "anchors." << hAlign << ": textAlignItem_" << counter << ".left";
399 }
400 counter++;
401
402 stream() << "color: \"" << info.fillColor.name(QColor::HexArgb) << "\"";
403 stream() << "textFormat:" << (info.needsRichText ? "Text.RichText" : "Text.StyledText");
404
405 QString s = info.text;
406 s.replace(QLatin1Char('"'), QLatin1String("\\\""));
407 stream() << "text: \"" << s << "\"";
408 stream() << "font.family: \"" << info.font.family() << "\"";
409 if (info.font.pixelSize() > 0)
410 stream() << "font.pixelSize:" << info.font.pixelSize();
411 else if (info.font.pointSize() > 0)
412 stream() << "font.pixelSize:" << info.font.pointSizeF();
413 if (info.font.underline())
414 stream() << "font.underline: true";
415 if (info.font.weight() != QFont::Normal)
416 stream() << "font.weight: " << int(info.font.weight());
417 if (info.font.italic())
418 stream() << "font.italic: true";
419
420 if (info.strokeColor != QColorConstants::Transparent) {
421 stream() << "styleColor: \"" << info.strokeColor.name(QColor::HexArgb) << "\"";
422 stream() << "style: Text.Outline";
423 }
424
425 m_indentLevel--;
426 stream() << "}";
427
428 m_indentLevel--;
429 stream() << "}";
430}
431
433{
434 if (!isNodeVisible(info))
435 return;
436
437 if (info.stage == StructureNodeStage::Start) {
438 stream() << "Item {";
440 m_indentLevel++;
441 stream() << "x: " << info.startPos.x();
442 stream() << "y: " << info.startPos.y();
443 } else {
444 m_indentLevel--;
445 stream() << "}";
446 }
447}
448
450{
451 if (!isNodeVisible(info))
452 return false;
453
454 if (info.stage == StructureNodeStage::Start) {
455 if (!info.forceSeparatePaths && info.isPathContainer) {
456 stream() << shapeName() <<" {";
457 m_indentLevel++;
459 stream() << "preferredRendererType: Shape.CurveRenderer";
460 m_indentLevel--;
461
462 m_inShapeItem = true;
463 } else {
464 stream() << "Item {";
465 }
466
467 if (!info.viewBox.isEmpty()) {
468 m_indentLevel++;
469 stream() << "transform: [";
470 m_indentLevel++;
471 bool translate = !qFuzzyIsNull(info.viewBox.x()) || !qFuzzyIsNull(info.viewBox.y());
472 if (translate)
473 stream() << "Translate { x: " << -info.viewBox.x() << "; y: " << -info.viewBox.y() << " },";
474 stream() << "Scale { xScale: width / " << info.viewBox.width() << "; yScale: height / " << info.viewBox.height() << " }";
475 m_indentLevel--;
476 stream() << "]";
477 m_indentLevel--;
478 }
479
481 m_indentLevel++;
482 } else {
483 m_indentLevel--;
484 stream() << "}";
485 m_inShapeItem = false;
486 }
487
488 return true;
489}
490
492{
493 m_indentLevel = 0;
494 const QStringList comments = m_commentString.split(u'\n');
495
496 if (!isNodeVisible(info)) {
497 if (comments.isEmpty()) {
498 stream() << "// Generated from SVG";
499 } else {
500 for (const auto &comment : comments)
501 stream() << "// " << comment;
502 }
503
504 stream() << "import QtQuick";
505 stream() << "import QtQuick.Shapes" << Qt::endl;
506 stream() << "Item {";
507 m_indentLevel++;
508
509 double w = info.size.width();
510 double h = info.size.height();
511 if (w > 0)
512 stream() << "implicitWidth: " << w;
513 if (h > 0)
514 stream() << "implicitHeight: " << h;
515
516 m_indentLevel--;
517 stream() << "}";
518
519 return false;
520 }
521
522 if (info.stage == StructureNodeStage::Start) {
523 if (comments.isEmpty())
524 stream() << "// Generated from SVG";
525 else
526 for (const auto &comment : comments)
527 stream() << "// " << comment;
528
529 stream() << "import QtQuick";
530 stream() << "import QtQuick.Shapes" << Qt::endl;
531 stream() << "Item {";
532 m_indentLevel++;
533
534 double w = info.size.width();
535 double h = info.size.height();
536 if (w > 0)
537 stream() << "implicitWidth: " << w;
538 if (h > 0)
539 stream() << "implicitHeight: " << h;
540
541 if (!info.viewBox.isEmpty()) {
542 stream() << "transform: [";
543 m_indentLevel++;
544 bool translate = !qFuzzyIsNull(info.viewBox.x()) || !qFuzzyIsNull(info.viewBox.y());
545 if (translate)
546 stream() << "Translate { x: " << -info.viewBox.x() << "; y: " << -info.viewBox.y() << " },";
547 stream() << "Scale { xScale: width / " << info.viewBox.width() << "; yScale: height / " << info.viewBox.height() << " }";
548 m_indentLevel--;
549 stream() << "]";;
550 }
551
553 } else {
554 stream() << "}";
555 m_inShapeItem = false;
556 }
557
558 return true;
559}
560
561QString QQuickQmlGenerator::indent()
562{
563 return QString().fill(QLatin1Char(' '), m_indentLevel * 4);
564}
565
566GeneratorStream QQuickQmlGenerator::stream()
567{
568 GeneratorStream strm(m_stream);
569 strm << indent();
570 return strm;
571}
572
573const char *QQuickQmlGenerator::shapeName() const
574{
575 return m_shapeTypeName.isEmpty() ? "Shape" : m_shapeTypeName.constData();
576}
577
GeneratorStream(QTextStream *result)
GeneratorStream & operator=(GeneratorStream &&other) noexcept
GeneratorStream(GeneratorStream &&other) noexcept
\inmodule QtCore
Definition qbytearray.h:57
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
@ HexArgb
Definition qcolor.h:36
\inmodule QtCore
Definition qdir.h:20
\inmodule QtCore
Definition qfile.h:93
@ Normal
Definition qfont.h:67
\inmodule QtGui
Definition qbrush.h:135
@ LogicalMode
Definition qbrush.h:154
Type type() const
Returns the type of gradient.
Definition qbrush.h:344
@ LinearGradient
Definition qbrush.h:139
@ NoGradient
Definition qbrush.h:142
@ RadialGradient
Definition qbrush.h:140
\inheaderfile QIODevice \inmodule QtCore
\inmodule QtGui
Definition qbrush.h:394
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition qmatrix4x4.h:25
\inmodule QtGui
Qt::FillRule fillRule() const
Returns the painter path's currently set fill rule.
void optimizePaths(const PathNodeInfo &info)
QQuickVectorImageGenerator::GeneratorFlags m_flags
bool isNodeVisible(const NodeInfo &info)
void outputShapePath(const PathNodeInfo &info, const QPainterPath *path, const QQuadPath *quadPath, QQuickVectorImageGenerator::PathSelector pathSelector, const QRectF &boundingRect) override
bool generateRootNode(const StructureNodeInfo &info) override
void setCommentString(const QString commentString)
void generateNode(const NodeInfo &info) override
void generateImageNode(const ImageNodeInfo &info) override
void generateNodeBase(const NodeInfo &info) override
QString shapeTypeName() const
void setShapeTypeName(const QString &name)
QString commentString() const
bool generateStructureNode(const StructureNodeInfo &info) override
QQuickQmlGenerator(const QString fileName, QQuickVectorImageGenerator::GeneratorFlags flags, const QString &outFileName)
void generateUseNode(const UseNodeInfo &info) override
void generatePath(const PathNodeInfo &info) override
bool generateDefsNode(const NodeInfo &info) override
void generateTextNode(const TextNodeInfo &info) override
\inmodule QtGui
Definition qbrush.h:412
\inmodule QtCore\reentrant
Definition qrect.h:484
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString & fill(QChar c, qsizetype size=-1)
Sets every character in the string to character ch.
Definition qstring.cpp:6358
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
QStringList split(const QString &sep, Qt::SplitBehavior behavior=Qt::KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Splits the string into substrings wherever sep occurs, and returns the list of those strings.
Definition qstring.cpp:8218
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
Definition qstring.cpp:5506
\inmodule QtCore
QString str
[2]
constexpr QColor Transparent
Definition qcolor.h:310
QString strokeJoinStyleString(Qt::PenJoinStyle strokeJoinStyle)
Definition utils_p.h:217
QString strokeCapStyleString(Qt::PenCapStyle strokeCapStyle)
Definition utils_p.h:196
QRectF mapToQtLogicalMode(const QRectF &objModeRect, const QRectF &boundingRect)
Definition utils_p.h:120
QString listString(QList< T > list)
Definition utils_p.h:240
QString pathHintString(const QQuadPath &qp)
Definition utils_p.h:84
QString toSvgString(const QPainterPath &path)
Definition utils_p.h:145
Combined button and popup list for selecting options.
@ AlignRight
Definition qnamespace.h:146
@ AlignHCenter
Definition qnamespace.h:148
@ AlignLeft
Definition qnamespace.h:144
QTextStream & endl(QTextStream &stream)
Writes '\n' to the stream and flushes the stream.
#define Q_FALLTHROUGH()
EGLStreamKHR stream
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition qfloat16.h:349
#define qWarning
Definition qlogging.h:166
#define qCWarning(category,...)
#define qCDebug(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
GLint GLint GLint GLint GLint x
[0]
const GLfloat * m
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLbitfield flags
GLuint name
GLint first
GLint y
GLfloat GLfloat GLfloat GLfloat h
GLuint counter
GLdouble s
[6]
Definition qopenglext.h:235
GLuint GLfloat * val
GLuint64EXT * result
[6]
static const QRectF boundingRect(const QPointF *points, int pointCount)
const GeneratorStream & operator<<(const GeneratorStream &str, T val)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
SSL_CTX int void * arg
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
#define Q_UNUSED(x)
static bool translate(xcb_connection_t *connection, xcb_window_t child, xcb_window_t parent, int *x, int *y)
QSharedPointer< T > other(t)
[5]
QHostInfo info
[0]
\inmodule QtCore \reentrant
Definition qchar.h:18