Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qdesigner.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4// designer
5#include "qdesigner.h"
10#include "mainwindow.h"
11#include <QtDesigner/abstractintegration.h>
12#include <QtDesigner/abstractformeditor.h>
13#include <qdesigner_propertysheet_p.h>
14
15#include <QtGui/qevent.h>
16#include <QtWidgets/qmessagebox.h>
17#include <QtGui/qicon.h>
18#include <QtWidgets/qerrormessage.h>
19#include <QtCore/qmetaobject.h>
20#include <QtCore/qdir.h>
21#include <QtCore/qfile.h>
22#include <QtCore/qlocale.h>
23#include <QtCore/qtextstream.h>
24#include <QtCore/qtimer.h>
25#include <QtCore/qtranslator.h>
26#include <QtCore/qfileinfo.h>
27#include <QtCore/qdebug.h>
28#include <QtCore/qcommandlineparser.h>
29#include <QtCore/qcommandlineoption.h>
30#include <QtCore/qvariant.h>
31
32#include <QtDesigner/QDesignerComponents>
33
34QT_BEGIN_NAMESPACE
35
36using namespace Qt::StringLiterals;
37
38static constexpr auto designerApplicationName = "Designer"_L1;
39static constexpr auto designerDisplayName = "Qt Widgets Designer"_L1;
40static constexpr auto designerWarningPrefix = "Designer: "_L1;
42
43static void designerMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
44{
45 // Only Designer warnings are displayed as box
46 QDesigner *designerApp = qDesigner;
47 if (type != QtWarningMsg || !designerApp || !msg.startsWith(designerWarningPrefix)) {
48 previousMessageHandler(type, context, msg);
49 return;
50 }
51 designerApp->showErrorMessage(msg);
52}
53
54QDesigner::QDesigner(int &argc, char **argv)
56{
57 setOrganizationName(u"QtProject"_s);
58 QGuiApplication::setApplicationDisplayName(designerDisplayName);
59 setApplicationName(designerApplicationName);
60 QDesignerComponents::initializeResources();
61
62#if !defined(Q_OS_MACOS) && !defined(Q_OS_WIN)
63 setWindowIcon(QIcon(u":/qt-project.org/designer/images/designer.png"_s));
64#endif
65}
66
68{
69 delete m_workbench;
70 delete m_server;
71 delete m_client;
72}
73
74void QDesigner::showErrorMessage(const QString &message)
75{
76 // strip the prefix
77 const QString qMessage =
78 message.right(message.size() - int(designerWarningPrefix.size()));
79 // If there is no main window yet, just store the message.
80 // The QErrorMessage would otherwise be hidden by the main window.
81 if (m_mainWindow) {
82 showErrorMessageBox(qMessage);
83 } else {
84 const QMessageLogContext emptyContext;
85 previousMessageHandler(QtWarningMsg, emptyContext, message); // just in case we crash
86 m_initializationErrors += qMessage;
87 m_initializationErrors += u'\n';
88 }
89}
90
91void QDesigner::showErrorMessageBox(const QString &msg)
92{
93 // Manually suppress consecutive messages.
94 // This happens if for example sth is wrong with custom widget creation.
95 // The same warning will be displayed by Widget box D&D and form Drop
96 // while trying to create instance.
97 if (m_errorMessageDialog && m_lastErrorMessage == msg)
98 return;
99
100 if (!m_errorMessageDialog) {
101 m_lastErrorMessage.clear();
102 m_errorMessageDialog = new QErrorMessage(m_mainWindow);
103 const QString title = QCoreApplication::translate("QDesigner", "%1 - warning").arg(designerApplicationName);
104 m_errorMessageDialog->setWindowTitle(title);
105 m_errorMessageDialog->setMinimumSize(QSize(600, 250));
106 }
107 m_errorMessageDialog->showMessage(msg);
108 m_lastErrorMessage = msg;
109}
110
112{
113 return m_workbench;
114}
115
116static void showHelp(QCommandLineParser &parser, const QString &errorMessage = QString())
117{
118 QString text;
119 QTextStream str(&text);
120 str << "<html><head/><body>";
121 if (!errorMessage.isEmpty())
122 str << "<p>" << errorMessage << "</p>";
123 str << "<pre>" << parser.helpText().toHtmlEscaped() << "</pre></body></html>";
124 QMessageBox box(errorMessage.isEmpty() ? QMessageBox::Information : QMessageBox::Warning,
125 QGuiApplication::applicationDisplayName(), text,
126 QMessageBox::Ok);
127 box.setTextInteractionFlags(Qt::TextBrowserInteraction);
128 box.exec();
129}
130
131static inline QDesigner::ParseArgumentsResult
132 parseDesignerCommandLineArguments(QCommandLineParser &parser, Options *options,
133 QString *errorMessage)
134{
135 parser.setApplicationDescription(u"Qt Widgets Designer " QT_VERSION_STR "\n\nUI designer for QWidget-based applications."_s);
136 const QCommandLineOption helpOption = parser.addHelpOption();
137 parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
138 const QCommandLineOption tcpServerOption(u"server"_s, u"TCP Server mode"_s);
139 parser.addOption(tcpServerOption);
140 const QCommandLineOption tcpClientOption(u"client"_s, u"TCP Client mode"_s, u"port"_s);
141 parser.addOption(tcpClientOption);
142 const QCommandLineOption webHelpOption(u"web-help"_s, u"Use the Web documentation"_s);
143 parser.addOption(webHelpOption);
144 const QCommandLineOption pythonHelpOption(u"python-help"_s, u"Use the Python documentation"_s);
145 parser.addOption(pythonHelpOption);
146 const QCommandLineOption resourceDirOption(u"resourcedir"_s,
147 u"Resource directory"_s,
148 u"directory"_s);
149 parser.addOption(resourceDirOption);
150 const QCommandLineOption internalDynamicPropertyOption(u"enableinternaldynamicproperties"_s,
151 u"Enable internal dynamic properties"_s);
152 parser.addOption(internalDynamicPropertyOption);
153 const QCommandLineOption pluginPathsOption(u"plugin-path"_s,
154 u"Default plugin path list"_s,
155 u"path"_s);
156 parser.addOption(pluginPathsOption);
157
158 const QCommandLineOption qtVersionOption(u"qt-version"_s,
159 u"Qt Version for writing .ui files"_s,
160 u"version"_s);
161 parser.addOption(qtVersionOption);
162
163 parser.addPositionalArgument(u"files"_s,
164 u"The UI files to open."_s);
165
166 if (!parser.parse(QCoreApplication::arguments())) {
167 *errorMessage = parser.errorText();
168 return QDesigner::ParseArgumentsError;
169 }
170
171 if (parser.isSet(helpOption))
172 return QDesigner::ParseArgumentsHelpRequested;
173 // There is no way to retrieve the complete help text from QCommandLineParser,
174 // so, call process() to display it.
175 if (parser.isSet(u"help-all"_s))
176 parser.process(QCoreApplication::arguments()); // exits
177 options->server = parser.isSet(tcpServerOption);
178 if (parser.isSet(tcpClientOption)) {
179 bool ok = false;
180 options->tcpClientPort = parser.value(tcpClientOption).toUShort(&ok);
181 if (!ok) {
182 *errorMessage = u"Non-numeric argument specified for -client"_s;
183 return QDesigner::ParseArgumentsError;
184 }
185 }
186 if (parser.isSet(webHelpOption))
188 else if (parser.isSet(pythonHelpOption))
190 if (parser.isSet(resourceDirOption))
191 options->resourceDir = parser.value(resourceDirOption);
192 const auto pluginPathValues = parser.values(pluginPathsOption);
193 for (const auto &pluginPath : pluginPathValues)
194 options->pluginPaths.append(pluginPath.split(QDir::listSeparator(), Qt::SkipEmptyParts));
195
196 if (parser.isSet(qtVersionOption))
197 options->qtVersion = QVersionNumber::fromString(parser.value(qtVersionOption));
198
199 options->enableInternalDynamicProperties = parser.isSet(internalDynamicPropertyOption);
200 options->files = parser.positionalArguments();
201 return QDesigner::ParseArgumentsSuccess;
202}
203
205{
206 QString errorMessage;
207 Options options;
208 QCommandLineParser parser;
209 const ParseArgumentsResult result = parseDesignerCommandLineArguments(parser, &options, &errorMessage);
210 if (result != ParseArgumentsSuccess) {
211 showHelp(parser, errorMessage);
212 return result;
213 }
214 // initialize the sub components
215 if (options.tcpClientPort)
216 m_client = new QDesignerTcpClient(options.tcpClientPort, this);
217 if (options.server) {
218 auto *server = new QDesignerTcpServer();
219 m_server = server;
220 printf("%d\n", server->serverPort());
221 fflush(stdout);
222 }
223 if (options.enableInternalDynamicProperties)
224 QDesignerPropertySheet::setInternalDynamicPropertiesEnabled(true);
225
226 std::unique_ptr<QTranslator> designerTranslator(new QTranslator(this));
227 if (designerTranslator->load(QLocale(), u"designer"_s, u"_"_s, options.resourceDir)) {
228 installTranslator(designerTranslator.release());
229 std::unique_ptr<QTranslator> qtTranslator(new QTranslator(this));
230 if (qtTranslator->load(QLocale(), u"qt"_s, u"_"_s, options.resourceDir))
231 installTranslator(qtTranslator.release());
232 }
233
234 m_workbench = new QDesignerWorkbench(options);
235
236 emit initialized();
237 previousMessageHandler = qInstallMessageHandler(designerMessageHandler); // Warn when loading faulty forms
238 Q_ASSERT(previousMessageHandler);
239
240 bool suppressNewFormShow = m_workbench->readInBackup();
241
242 for (auto fileName : std::as_const(options.files)) {
243 // Ensure absolute paths for recent file list to be unique
244 const QFileInfo fi(fileName);
245 if (fi.exists() && fi.isRelative())
246 fileName = fi.absoluteFilePath();
247 m_workbench->readInForm(fileName);
248 }
249
250 if (m_workbench->formWindowCount() > 0)
251 suppressNewFormShow = true;
252
253 if (options.qtVersion.has_value()) {
254#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
255 m_workbench->core()->integration()->setQtVersion(options.qtVersion.value());
256#else
257 auto version = QVariant::fromValue(options.qtVersion.value());
258 m_workbench->core()->integration()->setProperty("qtVersion", version);
259#endif
260 }
261
262 // Show up error box with parent now if something went wrong
263 if (m_initializationErrors.isEmpty()) {
264 if (!isServerOrClientEnabled() && !suppressNewFormShow)
265 m_workbench->showNewForm();
266 } else {
267 showErrorMessageBox(m_initializationErrors);
268 m_initializationErrors.clear();
269 }
270 return result;
271}
272
274{
275 return m_server || m_client;
276}
277
278bool QDesigner::event(QEvent *ev)
279{
280 bool eaten = false;
281 switch (ev->type()) {
282 case QEvent::FileOpen:
283 m_workbench->readInForm(static_cast<QFileOpenEvent *>(ev)->file());
284 m_workbench->requestActivate();
285 eaten = true;
286 break;
287 case QEvent::Close: {
288 auto *closeEvent = static_cast<QCloseEvent *>(ev);
289 closeEvent->setAccepted(m_workbench->handleClose());
290 if (closeEvent->isAccepted()) {
291 // We're going down, make sure that we don't get our settings saved twice.
292 if (m_mainWindow)
293 m_mainWindow->setCloseEventPolicy(MainWindowBase::AcceptCloseEvents);
294 QApplication::event(ev);
295 }
296 eaten = true;
297 break;
298 }
299 default:
300 eaten = QApplication::event(ev);
301 break;
302 }
303 return eaten;
304}
305
307{
308 m_mainWindow = tw;
309}
310
312{
313 return m_mainWindow;
314}
315
316QT_END_NAMESPACE
QDesignerFormEditorInterface * core() const
ParseArgumentsResult parseCommandLineArguments()
QDesigner(int &argc, char **argv)
Definition qdesigner.cpp:54
bool event(QEvent *ev) override
\reimp
void setMainWindow(MainWindowBase *tw)
QDesignerWorkbench * workbench() const
bool isServerOrClientEnabled() const
~QDesigner() override
Definition qdesigner.cpp:67
MainWindowBase * mainWindow() const
HelpClientType
Definition helpclient.h:17
static constexpr auto designerWarningPrefix
Definition qdesigner.cpp:40
static QDesigner::ParseArgumentsResult parseDesignerCommandLineArguments(QCommandLineParser &parser, Options *options, QString *errorMessage)
static void showHelp(QCommandLineParser &parser, const QString &errorMessage=QString())
static void designerMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
Definition qdesigner.cpp:43
static constexpr auto designerApplicationName
Definition qdesigner.cpp:38
static QtMessageHandler previousMessageHandler
Definition qdesigner.cpp:41
static constexpr auto designerDisplayName
Definition qdesigner.cpp:39
#define qDesigner
Definition qdesigner.h:19
bool server
Definition qdesigner.h:35
bool enableInternalDynamicProperties
Definition qdesigner.h:37
HelpClientType helpMode
Definition qdesigner.h:38