diff --git a/docs/qcustomplotdemo/index.html b/docs/qcustomplotdemo/index.html new file mode 100644 index 0000000..f31c7ab --- /dev/null +++ b/docs/qcustomplotdemo/index.html @@ -0,0 +1,72 @@ + + +
'; + return loadingText; + }; + + config.showCanvas = config.showCanvas || function(canvas, container) { + removeChildren(container); + } + + config.showExit = config.showExit || function(crashed, exitCode, container) { + if (!crashed) + return undefined; + + removeChildren(container); + var fontSize = 54; + var crashSymbols = ["\u{1F615}", "\u{1F614}", "\u{1F644}", "\u{1F928}", "\u{1F62C}", + "\u{1F915}", "\u{2639}", "\u{1F62E}", "\u{1F61E}", "\u{1F633}"]; + var symbolIndex = Math.floor(Math.random() * crashSymbols.length); + var errorHtml = ` ${crashSymbols[symbolIndex]} ` + var errorElement = document.createElement("text"); + errorElement.className = "QtExit" + errorElement.innerHTML = errorHtml; + return errorElement; + } + } + + config.restartMode = config.restartMode || "DoNotRestart"; + config.restartLimit = config.restartLimit || 10; + + if (config.stdoutEnabled === undefined) config.stdoutEnabled = true; + if (config.stderrEnabled === undefined) config.stderrEnabled = true; + + // Make sure config.path is defined and ends with "/" if needed + if (config.path === undefined) + config.path = ""; + if (config.path.length > 0 && !config.path.endsWith("/")) + config.path = config.path.concat("/"); + + if (config.environment === undefined) + config.environment = {}; + + var publicAPI = {}; + publicAPI.webAssemblySupported = webAssemblySupported(); + publicAPI.webGLSupported = webGLSupported(); + publicAPI.canLoadQt = canLoadQt(); + publicAPI.canLoadApplication = canLoadQt(); + publicAPI.status = undefined; + publicAPI.loadEmscriptenModule = loadEmscriptenModule; + publicAPI.addCanvasElement = addCanvasElement; + publicAPI.removeCanvasElement = removeCanvasElement; + publicAPI.resizeCanvasElement = resizeCanvasElement; + publicAPI.setFontDpi = setFontDpi; + publicAPI.fontDpi = fontDpi; + + restartCount = 0; + + function fetchResource(filePath) { + var fullPath = config.path + filePath; + return fetch(fullPath).then(function(response) { + if (!response.ok) { + self.error = response.status + " " + response.statusText + " " + response.url; + setStatus("Error"); + return Promise.reject(self.error) + } else { + return response; + } + }); + } + + function fetchText(filePath) { + return fetchResource(filePath).then(function(response) { + return response.text(); + }); + } + + function fetchThenCompileWasm(response) { + return response.arrayBuffer().then(function(data) { + self.loaderSubState = "Compiling"; + setStatus("Loading") // trigger loaderSubState udpate + return WebAssembly.compile(data); + }); + } + + function fetchCompileWasm(filePath) { + return fetchResource(filePath).then(function(response) { + if (typeof WebAssembly.compileStreaming !== "undefined") { + self.loaderSubState = "Downloading/Compiling"; + setStatus("Loading"); + return WebAssembly.compileStreaming(response).catch(function(error) { + // compileStreaming may/will fail if the server does not set the correct + // mime type (application/wasm) for the wasm file. Fall back to fetch, + // then compile in this case. + return fetchThenCompileWasm(response); + }); + } else { + // Fall back to fetch, then compile if compileStreaming is not supported + return fetchThenCompileWasm(response); + } + }); + } + + function loadEmscriptenModule(applicationName) { + + // Loading in qtloader.js goes through four steps: + // 1) Check prerequisites + // 2) Download resources + // 3) Configure the emscripten Module object + // 4) Start the emcripten runtime, after which emscripten takes over + + // Check for Wasm & WebGL support; set error and return before downloading resources if missing + if (!webAssemblySupported()) { + self.error = "Error: WebAssembly is not supported" + setStatus("Error"); + return; + } + if (!webGLSupported()) { + self.error = "Error: WebGL is not supported" + setStatus("Error"); + return; + } + + // Continue waiting if loadEmscriptenModule() is called again + if (publicAPI.status == "Loading") + return; + self.loaderSubState = "Downloading"; + setStatus("Loading"); + + // Fetch emscripten generated javascript runtime + var emscriptenModuleSource = undefined + var emscriptenModuleSourcePromise = fetchText(applicationName + ".js").then(function(source) { + emscriptenModuleSource = source + }); + + // Fetch and compile wasm module + var wasmModule = undefined; + var wasmModulePromise = fetchCompileWasm(applicationName + ".wasm").then(function (module) { + wasmModule = module; + }); + + // Wait for all resources ready + Promise.all([emscriptenModuleSourcePromise, wasmModulePromise]).then(function(){ + completeLoadEmscriptenModule(applicationName, emscriptenModuleSource, wasmModule); + }).catch(function(error) { + self.error = error; + setStatus("Error"); + }); + } + + function completeLoadEmscriptenModule(applicationName, emscriptenModuleSource, wasmModule) { + + // The wasm binary has been compiled into a module during resource download, + // and is ready to be instantiated. Define the instantiateWasm callback which + // emscripten will call to create the instance. + Module.instantiateWasm = function(imports, successCallback) { + WebAssembly.instantiate(wasmModule, imports).then(function(instance) { + successCallback(instance, wasmModule); + }, function(error) { + self.error = error; + setStatus("Error"); + }); + return {}; + }; + + Module.locateFile = Module.locateFile || function(filename) { + return config.path + filename; + }; + + // Attach status callbacks + Module.setStatus = Module.setStatus || function(text) { + // Currently the only usable status update from this function + // is "Running..." + if (text.startsWith("Running")) + setStatus("Running"); + }; + Module.monitorRunDependencies = Module.monitorRunDependencies || function(left) { + // console.log("monitorRunDependencies " + left) + }; + + // Attach standard out/err callbacks. + Module.print = Module.print || function(text) { + if (config.stdoutEnabled) + console.log(text) + }; + Module.printErr = Module.printErr || function(text) { + // Filter out OpenGL getProcAddress warnings. Qt tries to resolve + // all possible function/extension names at startup which causes + // emscripten to spam the console log with warnings. + if (text.startsWith !== undefined && text.startsWith("bad name in getProcAddress:")) + return; + + if (config.stderrEnabled) + console.log(text) + }; + + // Error handling: set status to "Exited", update crashed and + // exitCode according to exit type. + // Emscripten will typically call printErr with the error text + // as well. Note that emscripten may also throw exceptions from + // async callbacks. These should be handled in window.onerror by user code. + Module.onAbort = Module.onAbort || function(text) { + publicAPI.crashed = true; + publicAPI.exitText = text; + setStatus("Exited"); + }; + Module.quit = Module.quit || function(code, exception) { + if (exception.name == "ExitStatus") { + // Clean exit with code + publicAPI.exitText = undefined + publicAPI.exitCode = code; + } else { + publicAPI.exitText = exception.toString(); + publicAPI.crashed = true; + } + setStatus("Exited"); + }; + + // Set environment variables + Module.preRun = Module.preRun || [] + Module.preRun.push(function() { + for (var [key, value] of Object.entries(config.environment)) { + ENV[key.toUpperCase()] = value; + } + }); + + Module.mainScriptUrlOrBlob = new Blob([emscriptenModuleSource], {type: 'text/javascript'}); + + Module.qtCanvasElements = config.canvasElements; + + config.restart = function() { + + // Restart by reloading the page. This will wipe all state which means + // reload loops can't be prevented. + if (config.restartType == "ReloadPage") { + location.reload(); + } + + // Restart by readling the emscripten app module. + ++self.restartCount; + if (self.restartCount > config.restartLimit) { + self.error = "Error: This application has crashed too many times and has been disabled. Reload the page to try again." + setStatus("Error"); + return; + } + loadEmscriptenModule(applicationName); + }; + + publicAPI.exitCode = undefined; + publicAPI.exitText = undefined; + publicAPI.crashed = false; + + // Finally evaluate the emscripten application script, which will + // reference the global Module object created above. + self.eval(emscriptenModuleSource); // ES5 indirect global scope eval + } + + function setErrorContent() { + if (config.containerElements === undefined) { + if (config.showError !== undefined) + config.showError(self.error); + return; + } + + for (container of config.containerElements) { + var errorElement = config.showError(self.error, container); + container.appendChild(errorElement); + } + } + + function setLoaderContent() { + if (config.containerElements === undefined) { + if (config.showLoader !== undefined) + config.showLoader(self.loaderSubState); + return; + } + + for (container of config.containerElements) { + var loaderElement = config.showLoader(self.loaderSubState, container); + container.appendChild(loaderElement); + } + } + + function setCanvasContent() { + if (config.containerElements === undefined) { + if (config.showCanvas !== undefined) + config.showCanvas(); + return; + } + + for (var i = 0; i < config.containerElements.length; ++i) { + var container = config.containerElements[i]; + var canvas = config.canvasElements[i]; + config.showCanvas(canvas, container); + container.appendChild(canvas); + } + } + + function setExitContent() { + + // publicAPI.crashed = true; + + if (publicAPI.status != "Exited") + return; + + if (config.containerElements === undefined) { + if (config.showExit !== undefined) + config.showExit(publicAPI.crashed, publicAPI.exitCode); + return; + } + + if (!publicAPI.crashed) + return; + + for (container of config.containerElements) { + var loaderElement = config.showExit(publicAPI.crashed, publicAPI.exitCode, container); + if (loaderElement !== undefined) + container.appendChild(loaderElement); + } + } + + var committedStatus = undefined; + function handleStatusChange() { + if (publicAPI.status != "Loading" && committedStatus == publicAPI.status) + return; + committedStatus = publicAPI.status; + + if (publicAPI.status == "Error") { + setErrorContent(); + } else if (publicAPI.status == "Loading") { + setLoaderContent(); + } else if (publicAPI.status == "Running") { + setCanvasContent(); + } else if (publicAPI.status == "Exited") { + if (config.restartMode == "RestartOnExit" || + config.restartMode == "RestartOnCrash" && publicAPI.crashed) { + committedStatus = undefined; + config.restart(); + } else { + setExitContent(); + } + } + + // Send status change notification + if (config.statusChanged) + config.statusChanged(publicAPI.status); + } + + function setStatus(status) { + if (status != "Loading" && publicAPI.status == status) + return; + publicAPI.status = status; + + window.setTimeout(function() { handleStatusChange(); }, 0); + } + + function addCanvasElement(element) { + if (publicAPI.status == "Running") + Module.qtAddCanvasElement(element); + else + console.log("Error: addCanvasElement can only be called in the Running state"); + } + + function removeCanvasElement(element) { + if (publicAPI.status == "Running") + Module.qtRemoveCanvasElement(element); + else + console.log("Error: removeCanvasElement can only be called in the Running state"); + } + + function resizeCanvasElement(element) { + if (publicAPI.status == "Running") + Module.qtResizeCanvasElement(element); + } + + function setFontDpi(dpi) { + Module.qtFontDpi = dpi; + if (publicAPI.status == "Running") + Module.qtSetFontDpi(dpi); + } + + function fontDpi() { + return Module.qtFontDpi; + } + + setStatus("Created"); + + return publicAPI; +} diff --git a/docs/qcustomplotdemo/qtlogo.svg b/docs/qcustomplotdemo/qtlogo.svg new file mode 100644 index 0000000..ad7c777 --- /dev/null +++ b/docs/qcustomplotdemo/qtlogo.svg @@ -0,0 +1,40 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/third/qcustomplotdemo/main.cpp b/third/qcustomplotdemo/main.cpp index 5942c4f..5c08b53 100644 --- a/third/qcustomplotdemo/main.cpp +++ b/third/qcustomplotdemo/main.cpp @@ -3,19 +3,46 @@ #include "frmmain.h" #include #include +#include #include #include int main(int argc, char *argv[]) { +#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0)) + QApplication::setAttribute(Qt::AA_Use96Dpi); +#endif #if (QT_VERSION >= QT_VERSION_CHECK(5,14,0)) - //设置高分屏缩放舍入策略 QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Floor); #endif + QApplication a(argc, argv); QFont font; +#ifdef Q_OS_WASM + QString fontFile = ":/font/DroidSansFallback.ttf"; + QString fontName = "Droid Sans Fallback"; + + //判断图形字体是否存在,不存在则加入 + QFontDatabase fontDb; + if (!fontDb.families().contains(fontName)) { + int fontId = fontDb.addApplicationFont(fontFile); + QStringList listName = fontDb.applicationFontFamilies(fontId); + if (listName.count() == 0) { + qDebug() << QString("load %1 error").arg(fontName); + } + } + + //再次判断是否包含字体名称防止加载失败 + if (fontDb.families().contains(fontName)) { + font = QFont(fontName); +#if (QT_VERSION >= QT_VERSION_CHECK(4,8,0)) + font.setHintingPreference(QFont::PreferNoHinting); +#endif + } +#else font.setFamily("Microsoft Yahei"); font.setPixelSize(13); +#endif a.setFont(font); #if (QT_VERSION < QT_VERSION_CHECK(5,0,0)) diff --git a/ui/iconhelper/main.cpp b/ui/iconhelper/main.cpp index 780e53f..7b424b6 100644 --- a/ui/iconhelper/main.cpp +++ b/ui/iconhelper/main.cpp @@ -6,11 +6,14 @@ int main(int argc, char *argv[]) { - QApplication a(argc, argv); +#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0)) + QApplication::setAttribute(Qt::AA_Use96Dpi); +#endif #if (QT_VERSION >= QT_VERSION_CHECK(5,14,0)) - //设置高分屏缩放舍入策略 QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Floor); #endif + + QApplication a(argc, argv); QFont font; font.setFamily("Microsoft Yahei"); font.setPixelSize(13); diff --git a/widget/framelesswidget/main.cpp b/widget/framelesswidget/main.cpp index 3d7f473..f75be6a 100644 --- a/widget/framelesswidget/main.cpp +++ b/widget/framelesswidget/main.cpp @@ -6,9 +6,13 @@ int main(int argc, char *argv[]) { +#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0)) + QApplication::setAttribute(Qt::AA_Use96Dpi); +#endif #if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Floor); #endif + QApplication a(argc, argv); QFont font; font.setFamily("Microsoft Yahei"); diff --git a/widget/gifwidget/main.cpp b/widget/gifwidget/main.cpp index 6ff387c..a8a27e8 100644 --- a/widget/gifwidget/main.cpp +++ b/widget/gifwidget/main.cpp @@ -8,9 +8,13 @@ int main(int argc, char *argv[]) { +#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0)) + QApplication::setAttribute(Qt::AA_Use96Dpi); +#endif #if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Floor); #endif + QApplication a(argc, argv); QFont font; font.setFamily("Microsoft Yahei"); diff --git a/widget/screenwidget/main.cpp b/widget/screenwidget/main.cpp index d461f3a..5d88343 100644 --- a/widget/screenwidget/main.cpp +++ b/widget/screenwidget/main.cpp @@ -6,10 +6,13 @@ int main(int argc, char *argv[]) { +#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0)) + QApplication::setAttribute(Qt::AA_Use96Dpi); +#endif #if (QT_VERSION >= QT_VERSION_CHECK(5,14,0)) - //设置高分屏缩放舍入策略 QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Floor); #endif + QApplication a(argc, argv); QFont font; font.setFamily("Microsoft Yahei");