├── .clang-format ├── .gitattributes ├── .gitignore ├── ControlCar ├── ControlCar.pro ├── car.cpp ├── car.h ├── controller.cpp ├── controller.h ├── controller.ui └── main.cpp ├── LICENSE ├── Preview ├── car.png ├── controlCar.png └── shapeCar.png ├── QtWeb.pro ├── README.md ├── ShapeCar ├── ScrollBackground.qml ├── ShapeCar.pro ├── main.cpp ├── main.qml ├── res.qrc └── track.png ├── WebChannelCar ├── SocketWrap │ ├── websocketclientwrapper.cpp │ ├── websocketclientwrapper.h │ ├── websockettransport.cpp │ └── websockettransport.h ├── Web │ ├── build.bat │ ├── clean.bat │ ├── deploy.bat │ ├── img │ │ ├── connected.svg │ │ └── disconnected.svg │ ├── out │ │ └── main.js │ ├── package-lock.json │ ├── package.json │ ├── page │ │ └── index.html │ ├── src │ │ ├── CarObject.ts │ │ ├── main.ts │ │ ├── qwebchannel.d.ts │ │ ├── qwebchannel.js │ │ └── webchannelCore.ts │ ├── style │ │ ├── layout.css │ │ └── style.css │ ├── tsconfig.json │ ├── tslint.json │ └── webpack.config.js ├── WebChannelCar.pro ├── WebSDK.cpp ├── WebSDK.h ├── car.cpp ├── car.h └── main.cpp ├── WebQml ├── Qrc.qrc ├── WebQml.pro ├── main.cpp └── main.qml └── WebWidget ├── WebWidget.pro └── main.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | # 语言: None, Cpp, Java, JavaScript, ObjC, Proto, TableGen, TextProto 3 | Language: Cpp 4 | # BasedOnStyle: WebKit 5 | # 访问说明符(public、private等)的偏移 6 | AccessModifierOffset: -4 7 | # 开括号(开圆括号、开尖括号、开方括号)后的对齐: Align, DontAlign, AlwaysBreak(总是在开括号后换行) 8 | AlignAfterOpenBracket: AlwaysBreak 9 | # 连续赋值时,对齐所有等号 10 | AlignConsecutiveAssignments: false 11 | # 连续声明时,对齐所有声明的变量名 12 | AlignConsecutiveDeclarations: false 13 | # 左对齐逃脱换行(使用反斜杠换行)的反斜杠 14 | AlignEscapedNewlines: Right 15 | # 水平对齐二元和三元表达式的操作数 16 | AlignOperands: true 17 | # 对齐连续的尾随的注释 18 | AlignTrailingComments: true 19 | # 允许函数声明的所有参数在放在下一行 20 | AllowAllParametersOfDeclarationOnNextLine: false 21 | # 允许短的块放在同一行 22 | AllowShortBlocksOnASingleLine: false 23 | # 允许短的case标签放在同一行 24 | AllowShortCaseLabelsOnASingleLine: false 25 | # 允许短的函数放在同一行: None, InlineOnly(定义在类中), Empty(空函数), Inline(定义在类中,空函数), All 26 | AllowShortFunctionsOnASingleLine: Empty 27 | # 允许短的if语句保持在同一行 28 | AllowShortIfStatementsOnASingleLine: false 29 | # 允许短的循环保持在同一行 30 | AllowShortLoopsOnASingleLine: false 31 | # 总是在定义返回类型后换行(deprecated) 32 | AlwaysBreakAfterDefinitionReturnType: None 33 | # 总是在返回类型后换行: None, All, TopLevel(顶级函数,不包括在类中的函数), 34 | # AllDefinitions(所有的定义,不包括声明), TopLevelDefinitions(所有的顶级函数的定义) 35 | AlwaysBreakAfterReturnType: None 36 | # 总是在多行string字面量前换行 37 | AlwaysBreakBeforeMultilineStrings: false 38 | # 总是在template声明后换行 39 | AlwaysBreakTemplateDeclarations: true 40 | # false表示函数实参要么都在同一行,要么都各自一行 41 | BinPackArguments: false 42 | # false表示所有形参要么都在同一行,要么都各自一行 43 | BinPackParameters: false 44 | 45 | # 在大括号前换行: Attach(始终将大括号附加到周围的上下文), Linux(除函数、命名空间和类定义,与Attach类似), 46 | # Mozilla(除枚举、函数、记录定义,与Attach类似), Stroustrup(除函数定义、catch、else,与Attach类似), 47 | # Allman(总是在大括号前换行), GNU(总是在大括号前换行,并对于控制语句的大括号增加额外的缩进), WebKit(在函数前换行), Custom 48 | # 注:这里认为语句块也属于函数 49 | BreakBeforeBraces: Custom 50 | # 大括号换行,只有当BreakBeforeBraces设置为Custom时才有效 51 | BraceWrapping: 52 | # class定义后面 53 | AfterClass: true 54 | # 控制语句后面 55 | AfterControlStatement: false 56 | # enum定义后面 57 | AfterEnum: true 58 | # 函数定义后面 59 | AfterFunction: true 60 | # 命名空间定义后面 61 | AfterNamespace: true 62 | # ObjC定义后面 63 | AfterObjCDeclaration: false 64 | # struct定义后面 65 | AfterStruct: true 66 | # union定义后面 67 | AfterUnion: true 68 | # extern 定义后面 69 | AfterExternBlock: true 70 | # catch之前 71 | BeforeCatch: false 72 | # else 之前 73 | BeforeElse: false 74 | # 缩进大括号 75 | IndentBraces: false 76 | 77 | SplitEmptyFunction: true 78 | 79 | SplitEmptyRecord: true 80 | 81 | SplitEmptyNamespace: true 82 | # 在二元运算符前换行: None(在操作符后换行), NonAssignment(在非赋值的操作符前换行), All(在操作符前换行) 83 | BreakBeforeBinaryOperators: All 84 | 85 | # 继承列表的逗号前换行 86 | BreakBeforeInheritanceComma: true 87 | # 继承列表换行 88 | #BreakInheritanceList: BeforeColon 89 | # 在三元运算符前换行 90 | BreakBeforeTernaryOperators: true 91 | # 在构造函数的初始化列表的逗号前换行 92 | BreakConstructorInitializersBeforeComma: true 93 | # 初始化列表前换行 94 | BreakConstructorInitializers: BeforeComma 95 | # Java注解后换行 96 | BreakAfterJavaFieldAnnotations: false 97 | 98 | BreakStringLiterals: true 99 | # 每行字符的限制,0表示没有限制 100 | ColumnLimit: 160 101 | # 描述具有特殊意义的注释的正则表达式,它不应该被分割为多行或以其它方式改变 102 | CommentPragmas: '^ IWYU pragma:' 103 | # 紧凑 命名空间 104 | CompactNamespaces: false 105 | # 构造函数的初始化列表要么都在同一行,要么都各自一行 106 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 107 | # 构造函数的初始化列表的缩进宽度 108 | ConstructorInitializerIndentWidth: 4 109 | # 延续的行的缩进宽度 110 | ContinuationIndentWidth: 4 111 | # 去除C++11的列表初始化的大括号{后和}前的空格 112 | Cpp11BracedListStyle: false 113 | # 继承最常用的指针和引用的对齐方式 114 | DerivePointerAlignment: false 115 | # 关闭格式化 116 | DisableFormat: false 117 | # 自动检测函数的调用和定义是否被格式为每行一个参数(Experimental) 118 | ExperimentalAutoDetectBinPacking: false 119 | # 固定命名空间注释 120 | FixNamespaceComments: true 121 | # 需要被解读为foreach循环而不是函数调用的宏 122 | ForEachMacros: 123 | - foreach 124 | - Q_FOREACH 125 | - BOOST_FOREACH 126 | 127 | IncludeBlocks: Preserve 128 | # 对#include进行排序,匹配了某正则表达式的#include拥有对应的优先级,匹配不到的则默认优先级为INT_MAX(优先级越小排序越靠前), 129 | # 可以定义负数优先级从而保证某些#include永远在最前面 130 | IncludeCategories: 131 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 132 | Priority: 2 133 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 134 | Priority: 3 135 | - Regex: 'stdafx\.' 136 | Priority: 1 137 | - Regex: '.*' 138 | Priority: 1 139 | 140 | IncludeIsMainRegex: '(Test)?$' 141 | # 缩进case标签 142 | IndentCaseLabels: true 143 | 144 | IndentPPDirectives: None 145 | # 缩进宽度 146 | IndentWidth: 4 147 | # 函数返回类型换行时,缩进函数声明或函数定义的函数名 148 | IndentWrappedFunctionNames: true 149 | 150 | JavaScriptQuotes: Leave 151 | 152 | JavaScriptWrapImports: true 153 | KeepLineBreaksForNonEmptyLines: false 154 | # 保留在块开始处的空行 155 | KeepEmptyLinesAtTheStartOfBlocks: true 156 | # 开始一个块的宏的正则表达式 157 | MacroBlockBegin: '' 158 | # 结束一个块的宏的正则表达式 159 | MacroBlockEnd: '' 160 | # 连续空行的最大数量 161 | MaxEmptyLinesToKeep: 1 162 | 163 | # 命名空间的缩进: None, Inner(缩进嵌套的命名空间中的内容), All 164 | NamespaceIndentation: All 165 | ObjCBinPackProtocolList: Auto 166 | # 使用ObjC块时缩进宽度 167 | ObjCBlockIndentWidth: 4 168 | # 在ObjC的@property后添加一个空格 169 | ObjCSpaceAfterProperty: true 170 | # 在ObjC的protocol列表前添加一个空格 171 | ObjCSpaceBeforeProtocolList: true 172 | 173 | PenaltyBreakAssignment: 2 174 | 175 | PenaltyBreakBeforeFirstCallParameter: 19 176 | # 在一个注释中引入换行的penalty 177 | PenaltyBreakComment: 300 178 | # 第一次在<<前换行的penalty 179 | PenaltyBreakFirstLessLess: 120 180 | # 在一个字符串字面量中引入换行的penalty 181 | PenaltyBreakString: 1000 182 | PenaltyBreakTemplateDeclaration: 10 183 | # 对于每个在行字符数限制之外的字符的penalty 184 | PenaltyExcessCharacter: 1000000 185 | # 将函数的返回类型放到它自己的行的penalty 186 | PenaltyReturnTypeOnItsOwnLine: 60 187 | # 指针和引用的对齐: Left, Right, Middle 188 | PointerAlignment: Right 189 | 190 | #RawStringFormats: 191 | # - Delimiter: pb 192 | # Language: TextProto 193 | # BasedOnStyle: google 194 | # 允许重新排版注释 195 | ReflowComments: false 196 | # 允许排序#include 197 | SortIncludes: true 198 | 199 | SortUsingDeclarations: true 200 | # 在C风格类型转换后添加空格 201 | SpaceAfterCStyleCast: false 202 | # 模板关键字后面添加空格 203 | SpaceAfterTemplateKeyword: true 204 | # 在赋值运算符之前添加空格 205 | SpaceBeforeAssignmentOperators: true 206 | # 开圆括号之前添加一个空格: Never, ControlStatements, Always 207 | SpaceBeforeCpp11BracedList: false 208 | SpaceBeforeCtorInitializerColon: true 209 | SpaceBeforeInheritanceColon: true 210 | SpaceBeforeParens: ControlStatements 211 | SpaceBeforeRangeBasedForLoopColon: true 212 | # 在空的圆括号中添加空格 213 | SpaceInEmptyParentheses: false 214 | # 在尾随的评论前添加的空格数(只适用于//) 215 | SpacesBeforeTrailingComments: 1 216 | # 在尖括号的<后和>前添加空格 217 | SpacesInAngles: false 218 | # 在容器(ObjC和JavaScript的数组和字典等)字面量中添加空格 219 | SpacesInContainerLiterals: true 220 | # 在C风格类型转换的括号中添加空格 221 | SpacesInCStyleCastParentheses: false 222 | # 在圆括号的(后和)前添加空格 223 | SpacesInParentheses: false 224 | # 在方括号的[后和]前添加空格,lamda表达式和未指明大小的数组的声明不受影响 225 | SpacesInSquareBrackets: false 226 | # 标准: Cpp03, Cpp11, Auto 227 | Standard: Cpp11 228 | # tab宽度 229 | TabWidth: 4 230 | # 使用tab字符: Never, ForIndentation, ForContinuationAndIndentation, Always 231 | UseTab: Never 232 | ... 233 | 234 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | ############################################################################### 6 | # Set default behavior for command prompt diff. 7 | # 8 | # This is need for earlier builds of msysgit that does not have it on by 9 | # default for csharp files. 10 | # Note: This is only used by command line 11 | ############################################################################### 12 | #*.cs diff=csharp 13 | ############################################################################### 14 | # Set the merge driver for project and solution files 15 | # 16 | # Merging from the command prompt will add diff markers to the files if there 17 | # are conflicts (Merging from VS is not affected by the settings below, in VS 18 | # the diff markers are never inserted). Diff markers may cause the following 19 | # file extensions to fail to load in VS. An alternative would be to treat 20 | # these files as binary and thus will always conflict and require user 21 | # intervention with every merge. To do so, just uncomment the entries below 22 | ############################################################################### 23 | #*.sln merge=binary 24 | #*.csproj merge=binary 25 | #*.vbproj merge=binary 26 | #*.vcxproj merge=binary 27 | #*.vcproj merge=binary 28 | #*.dbproj merge=binary 29 | #*.fsproj merge=binary 30 | #*.lsproj merge=binary 31 | #*.wixproj merge=binary 32 | #*.modelproj merge=binary 33 | #*.sqlproj merge=binary 34 | #*.wwaproj merge=binary 35 | ############################################################################### 36 | # behavior for image files 37 | # 38 | # image files are treated as binary by default. 39 | ############################################################################### 40 | #*.jpg binary 41 | #*.png binary 42 | #*.gif binary 43 | ############################################################################### 44 | # diff behavior for common document formats 45 | # 46 | # Convert binary document formats to text before diffing them. This feature 47 | # is only available from the command line. Turn it on by uncommenting the 48 | # entries below. 49 | ############################################################################### 50 | #*.doc diff=astextplain 51 | #*.DOC diff=astextplain 52 | #*.docx diff=astextplain 53 | #*.DOCX diff=astextplain 54 | #*.dot diff=astextplain 55 | #*.DOT diff=astextplain 56 | #*.pdf diff=astextplain 57 | #*.PDF diff=astextplain 58 | #*.rtf diff=astextplain 59 | #*.RTF diff=astextplain 60 | #*.png filter=lfs diff=lfs merge=lfs -text 61 | #*.jpg filter=lfs diff=lfs merge=lfs -text 62 | #*.gif filter=lfs diff=lfs merge=lfs -text 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | build/ 5 | run/ 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # DNX 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | 50 | *_i.c 51 | *_p.c 52 | *_i.h 53 | *.ilk 54 | *.meta 55 | *.obj 56 | *.pch 57 | *.pdb 58 | *.pgc 59 | *.pgd 60 | *.rsp 61 | *.sbr 62 | *.tlb 63 | *.tli 64 | *.tlh 65 | *.tmp 66 | *.tmp_proj 67 | *.log 68 | *.vspscc 69 | *.vssscc 70 | .builds 71 | *.pidb 72 | *.svclog 73 | *.scc 74 | 75 | # Chutzpah Test files 76 | _Chutzpah* 77 | 78 | # Visual C++ cache files 79 | ipch/ 80 | *.aps 81 | *.ncb 82 | *.opendb 83 | *.opensdf 84 | *.sdf 85 | *.cachefile 86 | *.VC.db 87 | *.VC.VC.opendb 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | *.sap 94 | 95 | # TFS 2012 Local Workspace 96 | $tf/ 97 | 98 | # Guidance Automation Toolkit 99 | *.gpState 100 | 101 | # ReSharper is a .NET coding add-in 102 | _ReSharper*/ 103 | *.[Rr]e[Ss]harper 104 | *.DotSettings.user 105 | 106 | # JustCode is a .NET coding add-in 107 | .JustCode 108 | 109 | # TeamCity is a build add-in 110 | _TeamCity* 111 | 112 | # DotCover is a Code Coverage Tool 113 | *.dotCover 114 | 115 | # NCrunch 116 | _NCrunch_* 117 | .*crunch*.local.xml 118 | nCrunchTemp_* 119 | 120 | # MightyMoose 121 | *.mm.* 122 | AutoTest.Net/ 123 | 124 | # Web workbench (sass) 125 | .sass-cache/ 126 | 127 | # Installshield output folder 128 | [Ee]xpress/ 129 | 130 | # DocProject is a documentation generator add-in 131 | DocProject/buildhelp/ 132 | DocProject/Help/*.HxT 133 | DocProject/Help/*.HxC 134 | DocProject/Help/*.hhc 135 | DocProject/Help/*.hhk 136 | DocProject/Help/*.hhp 137 | DocProject/Help/Html2 138 | DocProject/Help/html 139 | 140 | # Click-Once directory 141 | publish/ 142 | 143 | # Publish Web Output 144 | *.[Pp]ublish.xml 145 | *.azurePubxml 146 | # TODO: Comment the next line if you want to checkin your web deploy settings 147 | # but database connection strings (with potential passwords) will be unencrypted 148 | #*.pubxml 149 | *.publishproj 150 | 151 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 152 | # checkin your Azure Web App publish settings, but sensitive information contained 153 | # in these scripts will be unencrypted 154 | PublishScripts/ 155 | 156 | # NuGet Packages 157 | *.nupkg 158 | # The packages folder can be ignored because of Package Restore 159 | **/packages/* 160 | # except build/, which is used as an MSBuild target. 161 | !**/packages/build/ 162 | # Uncomment if necessary however generally it will be regenerated when needed 163 | #!**/packages/repositories.config 164 | # NuGet v3's project.json files produces more ignoreable files 165 | *.nuget.props 166 | *.nuget.targets 167 | 168 | # Microsoft Azure Build Output 169 | csx/ 170 | *.build.csdef 171 | 172 | # Microsoft Azure Emulator 173 | ecf/ 174 | rcf/ 175 | 176 | # Windows Store app package directories and files 177 | AppPackages/ 178 | BundleArtifacts/ 179 | Package.StoreAssociation.xml 180 | _pkginfo.txt 181 | 182 | # Visual Studio cache files 183 | # files ending in .cache can be ignored 184 | *.[Cc]ache 185 | # but keep track of directories ending in .cache 186 | !*.[Cc]ache/ 187 | 188 | # Others 189 | ClientBin/ 190 | ~$* 191 | *~ 192 | *.dbmdl 193 | *.dbproj.schemaview 194 | *.jfm 195 | *.pfx 196 | *.publishsettings 197 | node_modules/ 198 | orleans.codegen.cs 199 | 200 | # Since there are multiple workflows, uncomment next line to ignore bower_components 201 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 202 | #bower_components/ 203 | 204 | # RIA/Silverlight projects 205 | Generated_Code/ 206 | 207 | # Backup & report files from converting an old project file 208 | # to a newer Visual Studio version. Backup files are not needed, 209 | # because we have git ;-) 210 | _UpgradeReport_Files/ 211 | Backup*/ 212 | UpgradeLog*.XML 213 | UpgradeLog*.htm 214 | 215 | # SQL Server files 216 | *.mdf 217 | *.ldf 218 | 219 | # Business Intelligence projects 220 | *.rdl.data 221 | *.bim.layout 222 | *.bim_*.settings 223 | 224 | # Microsoft Fakes 225 | FakesAssemblies/ 226 | 227 | # GhostDoc plugin setting file 228 | *.GhostDoc.xml 229 | 230 | # Node.js Tools for Visual Studio 231 | .ntvs_analysis.dat 232 | 233 | # Visual Studio 6 build log 234 | *.plg 235 | 236 | # Visual Studio 6 workspace options file 237 | *.opt 238 | 239 | # Visual Studio LightSwitch build output 240 | **/*.HTMLClient/GeneratedArtifacts 241 | **/*.DesktopClient/GeneratedArtifacts 242 | **/*.DesktopClient/ModelManifest.xml 243 | **/*.Server/GeneratedArtifacts 244 | **/*.Server/ModelManifest.xml 245 | _Pvt_Extensions 246 | 247 | # Paket dependency manager 248 | .paket/paket.exe 249 | paket-files/ 250 | 251 | # FAKE - F# Make 252 | .fake/ 253 | 254 | # JetBrains Rider 255 | .idea/ 256 | *.sln.iml 257 | 258 | # CodeRush 259 | .cr/ 260 | 261 | # Python Tools for Visual Studio (PTVS) 262 | __pycache__/ 263 | *.pyc 264 | /.vscode/settings.json 265 | -------------------------------------------------------------------------------- /ControlCar/ControlCar.pro: -------------------------------------------------------------------------------- 1 | QT += dbus widgets 2 | 3 | HEADERS += car.h \ 4 | controller.h 5 | SOURCES += car.cpp \ 6 | main.cpp \ 7 | controller.cpp 8 | 9 | CONFIG(debug, debug|release) { 10 | DESTDIR = $$PWD/bin/debug 11 | } else { 12 | DESTDIR = $$PWD/bin/release 13 | } 14 | 15 | FORMS += \ 16 | controller.ui 17 | -------------------------------------------------------------------------------- /ControlCar/car.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the examples of the Qt Toolkit. 7 | ** 8 | ** $QT_BEGIN_LICENSE:BSD$ 9 | ** Commercial License Usage 10 | ** Licensees holding valid commercial Qt licenses may use this file in 11 | ** accordance with the commercial license agreement provided with the 12 | ** Software or, alternatively, in accordance with the terms contained in 13 | ** a written agreement between you and The Qt Company. For licensing terms 14 | ** and conditions see https://www.qt.io/terms-conditions. For further 15 | ** information use the contact form at https://www.qt.io/contact-us. 16 | ** 17 | ** BSD License Usage 18 | ** Alternatively, you may use this file under the terms of the BSD license 19 | ** as follows: 20 | ** 21 | ** "Redistribution and use in source and binary forms, with or without 22 | ** modification, are permitted provided that the following conditions are 23 | ** met: 24 | ** * Redistributions of source code must retain the above copyright 25 | ** notice, this list of conditions and the following disclaimer. 26 | ** * Redistributions in binary form must reproduce the above copyright 27 | ** notice, this list of conditions and the following disclaimer in 28 | ** the documentation and/or other materials provided with the 29 | ** distribution. 30 | ** * Neither the name of The Qt Company Ltd nor the names of its 31 | ** contributors may be used to endorse or promote products derived 32 | ** from this software without specific prior written permission. 33 | ** 34 | ** 35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 46 | ** 47 | ** $QT_END_LICENSE$ 48 | ** 49 | ****************************************************************************/ 50 | 51 | #include "car.h" 52 | #include 53 | #include 54 | 55 | QRectF Car::boundingRect() const 56 | { 57 | return QRectF(-35, -81, 70, 115); 58 | } 59 | 60 | Car::Car() : color(Qt::green), wheelsAngle(0), speed(0) 61 | { 62 | startTimer(1000 / 33); 63 | setFlag(QGraphicsItem::ItemIsMovable, true); 64 | setFlag(QGraphicsItem::ItemIsFocusable, true); 65 | } 66 | 67 | void Car::accelerate() 68 | { 69 | if (speed < 10) 70 | ++speed; 71 | } 72 | 73 | void Car::decelerate() 74 | { 75 | if (speed > -10) 76 | --speed; 77 | } 78 | 79 | void Car::turnLeft() 80 | { 81 | if (wheelsAngle > -30) 82 | wheelsAngle -= 5; 83 | } 84 | 85 | void Car::turnRight() 86 | { 87 | if (wheelsAngle < 30) 88 | wheelsAngle += 5; 89 | } 90 | 91 | void Car::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) 92 | { 93 | Q_UNUSED(option); 94 | Q_UNUSED(widget); 95 | 96 | painter->setBrush(Qt::gray); 97 | painter->drawRect(-20, -58, 40, 2); // front axel 98 | painter->drawRect(-20, 7, 40, 2); // rear axel 99 | 100 | painter->setBrush(color); 101 | painter->drawRect(-25, -79, 50, 10); // front wing 102 | 103 | painter->drawEllipse(-25, -48, 50, 20); // side pods 104 | painter->drawRect(-25, -38, 50, 35); // side pods 105 | painter->drawRect(-5, 9, 10, 10); // back pod 106 | 107 | painter->drawEllipse(-10, -81, 20, 100); // main body 108 | 109 | painter->drawRect(-17, 19, 34, 15); // rear wing 110 | 111 | painter->setBrush(Qt::black); 112 | painter->drawPie(-5, -51, 10, 15, 0, 180 * 16); 113 | painter->drawRect(-5, -44, 10, 10); // cocpit 114 | 115 | painter->save(); 116 | painter->translate(-20, -58); 117 | painter->rotate(wheelsAngle); 118 | painter->drawRect(-10, -7, 10, 15); // front left 119 | painter->restore(); 120 | 121 | painter->save(); 122 | painter->translate(20, -58); 123 | painter->rotate(wheelsAngle); 124 | painter->drawRect(0, -7, 10, 15); // front left 125 | painter->restore(); 126 | 127 | painter->drawRect(-30, 0, 12, 17); // rear left 128 | painter->drawRect(19, 0, 12, 17); // rear right 129 | } 130 | 131 | void Car::timerEvent(QTimerEvent *event) 132 | { 133 | Q_UNUSED(event); 134 | 135 | const qreal axelDistance = 54; 136 | qreal wheelsAngleRads = qDegreesToRadians(wheelsAngle); 137 | qreal turnDistance = ::cos(wheelsAngleRads) * axelDistance * 2; 138 | qreal turnRateRads = wheelsAngleRads / turnDistance; // rough estimate 139 | qreal turnRate = qRadiansToDegrees(turnRateRads); 140 | qreal rotation = speed * turnRate; 141 | 142 | setTransform(QTransform().rotate(rotation), true); 143 | setTransform(QTransform::fromTranslate(0, -speed), true); 144 | update(); 145 | } 146 | -------------------------------------------------------------------------------- /ControlCar/car.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the examples of the Qt Toolkit. 7 | ** 8 | ** $QT_BEGIN_LICENSE:BSD$ 9 | ** Commercial License Usage 10 | ** Licensees holding valid commercial Qt licenses may use this file in 11 | ** accordance with the commercial license agreement provided with the 12 | ** Software or, alternatively, in accordance with the terms contained in 13 | ** a written agreement between you and The Qt Company. For licensing terms 14 | ** and conditions see https://www.qt.io/terms-conditions. For further 15 | ** information use the contact form at https://www.qt.io/contact-us. 16 | ** 17 | ** BSD License Usage 18 | ** Alternatively, you may use this file under the terms of the BSD license 19 | ** as follows: 20 | ** 21 | ** "Redistribution and use in source and binary forms, with or without 22 | ** modification, are permitted provided that the following conditions are 23 | ** met: 24 | ** * Redistributions of source code must retain the above copyright 25 | ** notice, this list of conditions and the following disclaimer. 26 | ** * Redistributions in binary form must reproduce the above copyright 27 | ** notice, this list of conditions and the following disclaimer in 28 | ** the documentation and/or other materials provided with the 29 | ** distribution. 30 | ** * Neither the name of The Qt Company Ltd nor the names of its 31 | ** contributors may be used to endorse or promote products derived 32 | ** from this software without specific prior written permission. 33 | ** 34 | ** 35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 46 | ** 47 | ** $QT_END_LICENSE$ 48 | ** 49 | ****************************************************************************/ 50 | 51 | #ifndef CAR_H 52 | #define CAR_H 53 | 54 | #include 55 | #include 56 | 57 | class Car : public QGraphicsObject 58 | { 59 | Q_OBJECT 60 | public: 61 | Car(); 62 | QRectF boundingRect() const; 63 | 64 | public Q_SLOTS: 65 | void accelerate(); 66 | void decelerate(); 67 | void turnLeft(); 68 | void turnRight(); 69 | 70 | protected: 71 | void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); 72 | void timerEvent(QTimerEvent *event); 73 | 74 | private: 75 | QBrush color; 76 | qreal wheelsAngle; // used when applying rotation 77 | qreal speed; // delta movement along the body axis 78 | }; 79 | 80 | #endif // CAR_H 81 | -------------------------------------------------------------------------------- /ControlCar/controller.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the examples of the Qt Toolkit. 7 | ** 8 | ** $QT_BEGIN_LICENSE:BSD$ 9 | ** Commercial License Usage 10 | ** Licensees holding valid commercial Qt licenses may use this file in 11 | ** accordance with the commercial license agreement provided with the 12 | ** Software or, alternatively, in accordance with the terms contained in 13 | ** a written agreement between you and The Qt Company. For licensing terms 14 | ** and conditions see https://www.qt.io/terms-conditions. For further 15 | ** information use the contact form at https://www.qt.io/contact-us. 16 | ** 17 | ** BSD License Usage 18 | ** Alternatively, you may use this file under the terms of the BSD license 19 | ** as follows: 20 | ** 21 | ** "Redistribution and use in source and binary forms, with or without 22 | ** modification, are permitted provided that the following conditions are 23 | ** met: 24 | ** * Redistributions of source code must retain the above copyright 25 | ** notice, this list of conditions and the following disclaimer. 26 | ** * Redistributions in binary form must reproduce the above copyright 27 | ** notice, this list of conditions and the following disclaimer in 28 | ** the documentation and/or other materials provided with the 29 | ** distribution. 30 | ** * Neither the name of The Qt Company Ltd nor the names of its 31 | ** contributors may be used to endorse or promote products derived 32 | ** from this software without specific prior written permission. 33 | ** 34 | ** 35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 46 | ** 47 | ** $QT_END_LICENSE$ 48 | ** 49 | ****************************************************************************/ 50 | 51 | #include 52 | 53 | #include "controller.h" 54 | 55 | Controller::Controller(Car *pCar, QWidget *parent) 56 | : QWidget(parent), m_pCar(pCar) { 57 | ui.setupUi(this); 58 | } 59 | 60 | void Controller::on_accelerate_clicked() { m_pCar->accelerate(); } 61 | 62 | void Controller::on_decelerate_clicked() { m_pCar->decelerate(); } 63 | 64 | void Controller::on_left_clicked() { m_pCar->turnLeft(); } 65 | 66 | void Controller::on_right_clicked() { m_pCar->turnRight(); } 67 | -------------------------------------------------------------------------------- /ControlCar/controller.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the examples of the Qt Toolkit. 7 | ** 8 | ** $QT_BEGIN_LICENSE:BSD$ 9 | ** Commercial License Usage 10 | ** Licensees holding valid commercial Qt licenses may use this file in 11 | ** accordance with the commercial license agreement provided with the 12 | ** Software or, alternatively, in accordance with the terms contained in 13 | ** a written agreement between you and The Qt Company. For licensing terms 14 | ** and conditions see https://www.qt.io/terms-conditions. For further 15 | ** information use the contact form at https://www.qt.io/contact-us. 16 | ** 17 | ** BSD License Usage 18 | ** Alternatively, you may use this file under the terms of the BSD license 19 | ** as follows: 20 | ** 21 | ** "Redistribution and use in source and binary forms, with or without 22 | ** modification, are permitted provided that the following conditions are 23 | ** met: 24 | ** * Redistributions of source code must retain the above copyright 25 | ** notice, this list of conditions and the following disclaimer. 26 | ** * Redistributions in binary form must reproduce the above copyright 27 | ** notice, this list of conditions and the following disclaimer in 28 | ** the documentation and/or other materials provided with the 29 | ** distribution. 30 | ** * Neither the name of The Qt Company Ltd nor the names of its 31 | ** contributors may be used to endorse or promote products derived 32 | ** from this software without specific prior written permission. 33 | ** 34 | ** 35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 46 | ** 47 | ** $QT_END_LICENSE$ 48 | ** 49 | ****************************************************************************/ 50 | 51 | #ifndef CONTROLLER_H 52 | #define CONTROLLER_H 53 | 54 | #include "ui_controller.h" 55 | #include 56 | #include "car.h" 57 | 58 | class Controller : public QWidget 59 | { 60 | Q_OBJECT 61 | 62 | public: 63 | Controller(Car *pCar, QWidget *parent = nullptr); 64 | private slots: 65 | void on_accelerate_clicked(); 66 | void on_decelerate_clicked(); 67 | void on_left_clicked(); 68 | void on_right_clicked(); 69 | 70 | private: 71 | Ui::Controller ui; 72 | Car *m_pCar = nullptr; 73 | }; 74 | 75 | #endif 76 | 77 | -------------------------------------------------------------------------------- /ControlCar/controller.ui: -------------------------------------------------------------------------------- 1 | 2 | Controller 3 | 4 | 5 | 6 | 0 7 | 0 8 | 255 9 | 111 10 | 11 | 12 | 13 | Controller 14 | 15 | 16 | 17 | 9 18 | 19 | 20 | 6 21 | 22 | 23 | 24 | 25 | Controller 26 | 27 | 28 | Qt::AlignCenter 29 | 30 | 31 | 32 | 33 | 34 | 35 | Decelerate 36 | 37 | 38 | 39 | 40 | 41 | 42 | Accelerate 43 | 44 | 45 | 46 | 47 | 48 | 49 | Right 50 | 51 | 52 | 53 | 54 | 55 | 56 | Left 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /ControlCar/main.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the examples of the Qt Toolkit. 7 | ** 8 | ** $QT_BEGIN_LICENSE:BSD$ 9 | ** Commercial License Usage 10 | ** Licensees holding valid commercial Qt licenses may use this file in 11 | ** accordance with the commercial license agreement provided with the 12 | ** Software or, alternatively, in accordance with the terms contained in 13 | ** a written agreement between you and The Qt Company. For licensing terms 14 | ** and conditions see https://www.qt.io/terms-conditions. For further 15 | ** information use the contact form at https://www.qt.io/contact-us. 16 | ** 17 | ** BSD License Usage 18 | ** Alternatively, you may use this file under the terms of the BSD license 19 | ** as follows: 20 | ** 21 | ** "Redistribution and use in source and binary forms, with or without 22 | ** modification, are permitted provided that the following conditions are 23 | ** met: 24 | ** * Redistributions of source code must retain the above copyright 25 | ** notice, this list of conditions and the following disclaimer. 26 | ** * Redistributions in binary form must reproduce the above copyright 27 | ** notice, this list of conditions and the following disclaimer in 28 | ** the documentation and/or other materials provided with the 29 | ** distribution. 30 | ** * Neither the name of The Qt Company Ltd nor the names of its 31 | ** contributors may be used to endorse or promote products derived 32 | ** from this software without specific prior written permission. 33 | ** 34 | ** 35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 46 | ** 47 | ** $QT_END_LICENSE$ 48 | ** 49 | ****************************************************************************/ 50 | 51 | #include "car.h" 52 | #include "controller.h" 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | int main(int argc, char *argv[]) 59 | { 60 | QApplication app(argc, argv); 61 | 62 | QGraphicsScene scene; 63 | scene.setSceneRect(-500, -500, 1000, 1000); 64 | scene.setItemIndexMethod(QGraphicsScene::NoIndex); 65 | 66 | Car *car = new Car(); 67 | scene.addItem(car); 68 | 69 | QGraphicsView view(&scene); 70 | view.setRenderHint(QPainter::Antialiasing); 71 | view.setBackgroundBrush(Qt::darkGray); 72 | view.setWindowTitle(QT_TRANSLATE_NOOP(QGraphicsView, "Qt Controlled Car")); 73 | view.resize(400, 300); 74 | view.show(); 75 | 76 | Controller controller(car); 77 | controller.setGeometry(view.x() + view.width(), view.y() + 30, controller.width(), 78 | controller.height()); 79 | controller.show(); 80 | return app.exec(); 81 | } 82 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 JaredTao jared2020@163.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Preview/car.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredtao/QtWeb/18e87bcc3db7a680b31b53d89d1a267ac73a8870/Preview/car.png -------------------------------------------------------------------------------- /Preview/controlCar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredtao/QtWeb/18e87bcc3db7a680b31b53d89d1a267ac73a8870/Preview/controlCar.png -------------------------------------------------------------------------------- /Preview/shapeCar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredtao/QtWeb/18e87bcc3db7a680b31b53d89d1a267ac73a8870/Preview/shapeCar.png -------------------------------------------------------------------------------- /QtWeb.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = subdirs 2 | 3 | SUBDIRS += \ 4 | WebQml \ 5 | WebWidget \ 6 | ShapeCar \ 7 | ControlCar \ 8 | WebChannelCar 9 | 10 | OTHER_FILES += \ 11 | README.md \ 12 | .clang-format \ 13 | LICENSE 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QtWeb 2 | 3 | QtWeb 混合开发系列文章的源码 4 | 5 | 项目结构 6 | 7 | |目录|描述|效果预览| 8 | |--|--|:--:| 9 | |WebWidget|Widget使用WebEngine的最简Demo|| 10 | |WebQml|Qml使用WebEngine的最简Demo|| 11 | |ControlCar|控制小车的简单Demo|| 12 | |WebChannelCar|WebChannel的方式控制小车|| 13 | |ShapeCar|Qml Shapes 重写的小车|| 14 | 15 | 文章在博客和知乎专栏。 16 | 17 | [涛哥的博客](https://jaredtao.github.io) 18 | 19 | [涛哥的博客-国内镜像](https://jaredtao.gitee.io) 20 | 21 | [知乎专栏-Qt进阶之路](https://zhuanlan.zhihu.com/TaoQt) 22 | 23 | # 联系方式 24 | 25 | *** 26 | 27 | | 作者 | 涛哥 | 28 | | ---- | -------------------------------- | 29 | |开发理念 | 传承工匠精神 | 30 | | 博客 | https://jaredtao.github.io/ | 31 | |博客-国内镜像|https://jaredtao.gitee.io| 32 | |知乎专栏| https://zhuanlan.zhihu.com/TaoQt | 33 | |QQ群| 734623697(高质量群,只能交流技术、分享书籍、帮助解决实际问题)| 34 | | 邮箱 | jared2020@163.com | 35 | | 微信 | xsd2410421 | 36 | | QQ | 759378563 | 37 | *** 38 | 39 | QQ(TIM)、微信二维码 40 | 41 | 42 | 43 | 44 | ****** 请放心联系我,乐于提供咨询服务,也可洽谈有偿技术支持相关事宜。 45 | 46 | *** 47 | ## 赞助 48 | 49 | 50 | ****** 觉得分享的内容还不错, 就请作者喝杯奶茶吧~~ 51 | *** 52 | -------------------------------------------------------------------------------- /ShapeCar/ScrollBackground.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Controls 2.12 3 | Item { 4 | id: root 5 | property real scrollSpeed: 0 6 | 7 | Image { 8 | id: image1 9 | source: "qrc:/track.png" 10 | sourceSize.width: 178 11 | sourceSize.height: 656 12 | width: root.width 13 | height: root.width / 178 * 656 14 | Component.onCompleted: { 15 | y = root.height - height 16 | } 17 | } 18 | Image { 19 | id: image2 20 | source: "qrc:/track.png" 21 | sourceSize.width: 178 22 | sourceSize.height: 656 23 | width: root.width 24 | height: root.width / 178 * 656 25 | } 26 | SequentialAnimation { 27 | running: scrollSpeed > 0 28 | loops: -1 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ShapeCar/ShapeCar.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | 3 | QT += core gui qml quick 4 | 5 | DESTDIR = $$PWD/bin 6 | 7 | HEADERS += 8 | 9 | SOURCES += \ 10 | main.cpp 11 | 12 | RESOURCES += \ 13 | res.qrc 14 | 15 | -------------------------------------------------------------------------------- /ShapeCar/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | int main(int argc, char *argv[]) 4 | { 5 | QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); 6 | 7 | QGuiApplication a(argc, argv); 8 | 9 | QQuickView view; 10 | view.setSource(QUrl("qrc:/main.qml")); 11 | view.show(); 12 | 13 | return a.exec(); 14 | } 15 | -------------------------------------------------------------------------------- /ShapeCar/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Controls 2.12 3 | import QtQuick.Shapes 1.12 4 | Item { 5 | id: root 6 | width: 400 7 | height: 600 8 | 9 | //车速 10 | property real speed: speedSlider.value 11 | //角度 12 | property real angle: angleSlider.value 13 | 14 | //车宽 15 | readonly property int carWidth: 100 16 | //车高 17 | readonly property int carHeight: 180 18 | //头宽 19 | readonly property int headWidth: 80 20 | //头高 21 | readonly property int headHeight: 16 22 | //尾宽 23 | readonly property int tailWidth: 60 24 | //尾高 25 | readonly property int tailHeight: 24 26 | //轮宽 27 | readonly property int wheelWidth: 16 28 | //轮高 29 | readonly property int wheelHeight: 30 30 | //轴高 31 | readonly property int axiasHeight: 6 32 | //前轴Y中心 33 | readonly property int frontAxiasYCenter: 36 34 | //后轴Y中心 35 | readonly property int backAxiasYCenter: 136 36 | //车尾连接杆宽度 37 | readonly property int tailHandleWidth: 10 38 | //车尾连接杆高度 39 | readonly property int tailHandleHeight: 10 40 | //车身弧高度 41 | readonly property int bodyArcHeight: 24 42 | //车身高度 43 | readonly property int bodyHeight: 54 44 | //车身Y起点 45 | readonly property int bodyYStart: 64 46 | //驾驶位Y终点 47 | readonly property int driverYEnd: 70 48 | //驾驶位高度 49 | readonly property int driverHeight: 30 50 | //驾驶位宽度 51 | readonly property int driverWidth: 16 52 | ScrollBackground { 53 | scrollSpeed: speed 54 | anchors.fill: parent 55 | } 56 | Shape { 57 | id: carShape 58 | width: carWidth 59 | height: carHeight 60 | x: (parent.width - width) / 2 61 | y: parent.height - height 62 | 63 | //抗锯齿 64 | //启用layer 65 | layer.enabled: true 66 | //4倍采样 67 | layer.samples: 8 68 | //平滑 69 | smooth: true 70 | //抗锯齿 71 | antialiasing: true 72 | 73 | //前轮架 矩形 74 | ShapePath { 75 | fillColor: "gray" 76 | startX: wheelWidth 77 | startY: frontAxiasYCenter - axiasHeight / 2 78 | PathLine { x: carWidth - wheelWidth; y: frontAxiasYCenter - axiasHeight / 2} 79 | PathLine { x: carWidth - wheelWidth; y: frontAxiasYCenter + axiasHeight / 2} 80 | PathLine { x: wheelWidth; y: frontAxiasYCenter + axiasHeight / 2} 81 | } 82 | //后轮架 矩形 83 | ShapePath { 84 | fillColor: "gray" 85 | startX: wheelWidth 86 | startY: backAxiasYCenter - axiasHeight / 2 87 | PathLine { x: carWidth - wheelWidth; y: backAxiasYCenter - axiasHeight / 2} 88 | PathLine { x: carWidth - wheelWidth; y: backAxiasYCenter + axiasHeight / 2} 89 | PathLine { x: wheelWidth; y: backAxiasYCenter + axiasHeight / 2} 90 | } 91 | 92 | //左后轮 矩形 93 | ShapePath { 94 | fillColor: "black" 95 | capStyle: ShapePath.RoundCap 96 | joinStyle: ShapePath.RoundJoin 97 | startX: 0 98 | startY: backAxiasYCenter - wheelHeight / 2 99 | PathLine { x: wheelWidth; y: backAxiasYCenter - wheelHeight / 2} 100 | PathLine { x: wheelWidth; y: backAxiasYCenter + wheelHeight / 2} 101 | PathLine { x: 0; y: backAxiasYCenter + wheelHeight / 2} 102 | } 103 | //右后轮 矩形 104 | ShapePath { 105 | fillColor: "black" 106 | capStyle: ShapePath.RoundCap 107 | joinStyle: ShapePath.RoundJoin 108 | startX: carWidth - wheelWidth 109 | startY: backAxiasYCenter - wheelHeight / 2 110 | PathLine { x: carWidth; y: backAxiasYCenter - wheelHeight / 2} 111 | PathLine { x: carWidth; y: backAxiasYCenter + wheelHeight / 2} 112 | PathLine { x: carWidth - wheelWidth; y: backAxiasYCenter + wheelHeight / 2} 113 | } 114 | //车头 矩形 115 | ShapePath { 116 | fillColor: "green" 117 | capStyle: ShapePath.RoundCap 118 | joinStyle: ShapePath.RoundJoin 119 | startX: (carWidth - headWidth) / 2 120 | startY: 0 121 | PathLine { x: carWidth - (carWidth - headWidth) / 2; y: 0} 122 | PathLine { x: carWidth - (carWidth - headWidth) / 2; y: headHeight} 123 | PathLine { x: (carWidth - headWidth) / 2; y: headHeight} 124 | } 125 | 126 | //车尾 矩形 127 | ShapePath { 128 | fillColor: "green" 129 | capStyle: ShapePath.RoundCap 130 | joinStyle: ShapePath.RoundJoin 131 | startX: (carWidth - tailWidth) / 2 132 | startY: carHeight - tailHeight 133 | PathLine { x: carWidth - (carWidth - tailWidth) / 2; y: carHeight - tailHeight} 134 | PathLine { x: carWidth - (carWidth - tailWidth) / 2; y: carHeight} 135 | PathLine { x: (carWidth - tailWidth) / 2; y: carHeight} 136 | } 137 | //车尾连接杆 矩形 138 | ShapePath { 139 | fillColor: "green" 140 | capStyle: ShapePath.RoundCap 141 | joinStyle: ShapePath.RoundJoin 142 | startX: (carWidth - tailHandleWidth) / 2 143 | startY: carHeight - tailHeight - tailHandleHeight 144 | PathLine { x: carWidth - (carWidth - tailHandleWidth) / 2; y: carHeight - tailHeight - tailHandleHeight} 145 | PathLine { x: carWidth - (carWidth - tailHandleWidth) / 2; y: carHeight - tailHeight} 146 | PathLine { x:(carWidth - tailHandleWidth) / 2; y: carHeight - tailHeight} 147 | } 148 | //车身前弧 弓形 149 | ShapePath { 150 | fillColor: "green" 151 | capStyle: ShapePath.RoundCap 152 | joinStyle: ShapePath.RoundJoin 153 | startX: (carWidth - headWidth) / 2 154 | startY: bodyYStart 155 | PathArc { 156 | x: carWidth - (carWidth - headWidth) / 2 157 | y: bodyYStart 158 | radiusX: carWidth / 2 159 | //测试得出的值 160 | radiusY: bodyYStart - bodyHeight / 2 161 | useLargeArc: false 162 | } 163 | } 164 | //车身 矩形 165 | ShapePath { 166 | fillColor: "green" 167 | capStyle: ShapePath.RoundCap 168 | joinStyle: ShapePath.RoundJoin 169 | startX: (carWidth - headWidth) / 2 170 | startY: bodyYStart 171 | PathLine { x: carWidth - (carWidth - headWidth) / 2; y: bodyYStart} 172 | PathLine { x: carWidth - (carWidth - headWidth) / 2; y: bodyYStart + bodyHeight} 173 | PathLine { x: (carWidth - headWidth) / 2; y: bodyYStart + bodyHeight} 174 | } 175 | //龙骨 梭形 两个圆弧 176 | ShapePath { 177 | fillColor: "green" 178 | capStyle: ShapePath.RoundCap 179 | joinStyle: ShapePath.RoundJoin 180 | startX: carWidth / 2 181 | startY: 0 182 | PathArc { 183 | x: carWidth / 2 184 | y: carHeight - tailHeight 185 | //测试得出的值 186 | radiusX: carWidth / 2 - headWidth 187 | radiusY: carHeight / 2 188 | useLargeArc: false 189 | } 190 | PathArc { 191 | x: carWidth / 2 192 | y: 0 193 | //测试得出的值 194 | radiusX: carWidth / 2 - headWidth 195 | radiusY: carHeight / 2 196 | useLargeArc: false 197 | } 198 | } 199 | //驾驶位 矩形+半圆 200 | ShapePath { 201 | fillColor: "black" 202 | capStyle: ShapePath.RoundCap 203 | startX: (carWidth - driverWidth) / 2 204 | startY: driverYEnd 205 | PathLine { 206 | x: (carWidth - driverWidth) / 2 207 | y: driverYEnd - driverHeight / 2 208 | } 209 | PathArc { 210 | x: carWidth / 2 + driverWidth / 2 211 | y: driverYEnd - driverHeight / 2 212 | useLargeArc: false 213 | radiusX: carWidth / 2 214 | //测试值 215 | radiusY: 420 216 | } 217 | PathLine { 218 | x: carWidth / 2 + driverWidth / 2 219 | y: driverYEnd 220 | } 221 | } 222 | } 223 | //左前轮 224 | Item { 225 | anchors { 226 | fill: carShape 227 | horizontalCenterOffset: -10 228 | } 229 | Rectangle { 230 | id: frontLeftWheel 231 | width: wheelWidth 232 | height: wheelHeight 233 | radius: 2 234 | x: 0 + 10 235 | y: frontAxiasYCenter - wheelHeight / 2 236 | color: angle === 0 ? "black" : "blue" 237 | antialiasing: true 238 | smooth: true 239 | transform: Rotation { 240 | angle: root.angle 241 | origin { 242 | x: frontLeftWheel.width / 2 + 10 243 | y: frontLeftWheel.height / 2 244 | } 245 | } 246 | } 247 | } 248 | //右前轮 249 | Item { 250 | anchors { 251 | fill: carShape 252 | horizontalCenterOffset: 10 253 | } 254 | Rectangle { 255 | id: frontRightWheel 256 | width: wheelWidth 257 | height: wheelHeight 258 | radius: 2 259 | x: carWidth - wheelWidth - 10 260 | y: frontAxiasYCenter - wheelHeight / 2 261 | color: angle === 0 ? "black" : "blue" 262 | antialiasing: true 263 | smooth: true 264 | transform: Rotation { 265 | angle: root.angle 266 | origin { 267 | x: frontRightWheel.width / 2 - 10 268 | y: frontRightWheel.height / 2 269 | } 270 | } 271 | } 272 | } 273 | Column { 274 | anchors { 275 | top: parent.top 276 | right: parent.right 277 | } 278 | Slider { 279 | id: angleSlider 280 | from: -15 281 | to: 15 282 | value: 0 283 | } 284 | Slider { 285 | id: speedSlider 286 | from: 0 287 | to: 10 288 | value: 0 289 | } 290 | } 291 | } 292 | 293 | -------------------------------------------------------------------------------- /ShapeCar/res.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | track.png 5 | ScrollBackground.qml 6 | 7 | 8 | -------------------------------------------------------------------------------- /ShapeCar/track.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredtao/QtWeb/18e87bcc3db7a680b31b53d89d1a267ac73a8870/ShapeCar/track.png -------------------------------------------------------------------------------- /WebChannelCar/SocketWrap/websocketclientwrapper.cpp: -------------------------------------------------------------------------------- 1 | #include "websocketclientwrapper.h" 2 | #include "websockettransport.h" 3 | 4 | #include 5 | #include 6 | #include 7 | WebSocketClientWrapper::WebSocketClientWrapper(QWebSocketServer *server, QObject *parent) : QObject(parent), m_server(server) 8 | { 9 | connect(server, &QWebSocketServer::newConnection, this, &WebSocketClientWrapper::handleNewConnection); 10 | } 11 | 12 | void WebSocketClientWrapper::handleNewConnection() 13 | { 14 | auto pSocket = m_server->nextPendingConnection(); 15 | if (pSocket) 16 | { 17 | connect(pSocket, &QWebSocket::disconnected, this, &WebSocketClientWrapper::clientDisconnected); 18 | emit clientConnected(new WebSocketTransport(pSocket)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /WebChannelCar/SocketWrap/websocketclientwrapper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class WebSocketTransport; 6 | 7 | QT_BEGIN_NAMESPACE 8 | class QWebSocketServer; 9 | QT_END_NAMESPACE 10 | 11 | class WebSocketClientWrapper : public QObject 12 | { 13 | Q_OBJECT 14 | public: 15 | WebSocketClientWrapper(QWebSocketServer *server, QObject *parent = nullptr); 16 | 17 | signals: 18 | void clientConnected(WebSocketTransport *client); 19 | void clientDisconnected(); 20 | private slots: 21 | void handleNewConnection(); 22 | 23 | private: 24 | QWebSocketServer *m_server; 25 | }; 26 | -------------------------------------------------------------------------------- /WebChannelCar/SocketWrap/websockettransport.cpp: -------------------------------------------------------------------------------- 1 | #include "websockettransport.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | /*! 8 | \brief QWebChannelAbstractSocket implementation that uses a QWebSocket internally. 9 | 10 | The transport delegates all messages received over the QWebSocket over its 11 | textMessageReceived signal. Analogously, all calls to sendTextMessage will 12 | be send over the QWebSocket to the remote client. 13 | */ 14 | 15 | /*! 16 | Construct the transport object and wrap the given socket. 17 | 18 | The socket is also set as the parent of the transport object. 19 | */ 20 | WebSocketTransport::WebSocketTransport(QWebSocket *socket) : QWebChannelAbstractTransport(socket), m_socket(socket) 21 | { 22 | connect(socket, &QWebSocket::textMessageReceived, this, &WebSocketTransport::textMessageReceived); 23 | connect(socket, &QWebSocket::disconnected, this, &WebSocketTransport::deleteLater); 24 | } 25 | 26 | /*! 27 | Destroys the WebSocketTransport. 28 | */ 29 | WebSocketTransport::~WebSocketTransport() 30 | { 31 | m_socket->deleteLater(); 32 | m_socket = nullptr; 33 | } 34 | 35 | /*! 36 | Serialize the JSON message and send it as a text message via the WebSocket to the client. 37 | */ 38 | void WebSocketTransport::sendMessage(const QJsonObject &message) 39 | { 40 | QJsonDocument doc(message); 41 | m_socket->sendTextMessage(QString::fromUtf8(doc.toJson(QJsonDocument::Compact))); 42 | } 43 | 44 | /*! 45 | Deserialize the stringified JSON messageData and emit messageReceived. 46 | */ 47 | void WebSocketTransport::textMessageReceived(const QString &messageData) 48 | { 49 | QJsonParseError error; 50 | QJsonDocument message = QJsonDocument::fromJson(messageData.toUtf8(), &error); 51 | if (error.error) 52 | { 53 | qWarning() << "Failed to parse text message as JSON object:" << messageData << "Error is:" << error.errorString(); 54 | return; 55 | } 56 | else if (!message.isObject()) 57 | { 58 | qWarning() << "Received JSON message that is not an object: " << messageData; 59 | return; 60 | } 61 | emit messageReceived(message.object(), this); 62 | } 63 | -------------------------------------------------------------------------------- /WebChannelCar/SocketWrap/websockettransport.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | QT_BEGIN_NAMESPACE 4 | class QWebSocket; 5 | QT_END_NAMESPACE 6 | 7 | class WebSocketTransport : public QWebChannelAbstractTransport 8 | { 9 | Q_OBJECT 10 | public: 11 | explicit WebSocketTransport(QWebSocket *socket); 12 | virtual ~WebSocketTransport() override; 13 | 14 | void sendMessage(const QJsonObject &message) override; 15 | 16 | private slots: 17 | void textMessageReceived(const QString &message); 18 | 19 | private: 20 | QWebSocket *m_socket; 21 | }; 22 | -------------------------------------------------------------------------------- /WebChannelCar/Web/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | npm run build -------------------------------------------------------------------------------- /WebChannelCar/Web/clean.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | if exist out/ ( rd /s /q out ) -------------------------------------------------------------------------------- /WebChannelCar/Web/deploy.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | set targetPath=..\bin\release 3 | if exist %targetPath%\out\ ( rd /s /q %targetPath%\out) 4 | if exist %targetPath%\page\ ( rd /s /q %targetPath%\page) 5 | if exist %targetPath%\style\ ( rd /s /q %targetPath%\style) 6 | if exist %targetPath%\img\ ( rd /s /q %targetPath%\img) 7 | md %targetPath%\out 8 | md %targetPath%\page 9 | md %targetPath%\style 10 | md %targetPath%\img 11 | xcopy /s /q /y page %targetPath%\page 12 | xcopy /s /q /y out %targetPath%\out 13 | xcopy /s /q /y style %targetPath%\style 14 | xcopy /s /q /y img %targetPath%\img 15 | -------------------------------------------------------------------------------- /WebChannelCar/Web/img/connected.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /WebChannelCar/Web/img/disconnected.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /WebChannelCar/Web/out/main.js: -------------------------------------------------------------------------------- 1 | /******/ (function(modules) { // webpackBootstrap 2 | /******/ // The module cache 3 | /******/ var installedModules = {}; 4 | /******/ 5 | /******/ // The require function 6 | /******/ function __webpack_require__(moduleId) { 7 | /******/ 8 | /******/ // Check if module is in cache 9 | /******/ if(installedModules[moduleId]) { 10 | /******/ return installedModules[moduleId].exports; 11 | /******/ } 12 | /******/ // Create a new module (and put it into the cache) 13 | /******/ var module = installedModules[moduleId] = { 14 | /******/ i: moduleId, 15 | /******/ l: false, 16 | /******/ exports: {} 17 | /******/ }; 18 | /******/ 19 | /******/ // Execute the module function 20 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 21 | /******/ 22 | /******/ // Flag the module as loaded 23 | /******/ module.l = true; 24 | /******/ 25 | /******/ // Return the exports of the module 26 | /******/ return module.exports; 27 | /******/ } 28 | /******/ 29 | /******/ 30 | /******/ // expose the modules object (__webpack_modules__) 31 | /******/ __webpack_require__.m = modules; 32 | /******/ 33 | /******/ // expose the module cache 34 | /******/ __webpack_require__.c = installedModules; 35 | /******/ 36 | /******/ // define getter function for harmony exports 37 | /******/ __webpack_require__.d = function(exports, name, getter) { 38 | /******/ if(!__webpack_require__.o(exports, name)) { 39 | /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); 40 | /******/ } 41 | /******/ }; 42 | /******/ 43 | /******/ // define __esModule on exports 44 | /******/ __webpack_require__.r = function(exports) { 45 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 46 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 47 | /******/ } 48 | /******/ Object.defineProperty(exports, '__esModule', { value: true }); 49 | /******/ }; 50 | /******/ 51 | /******/ // create a fake namespace object 52 | /******/ // mode & 1: value is a module id, require it 53 | /******/ // mode & 2: merge all properties of value into the ns 54 | /******/ // mode & 4: return value when already ns object 55 | /******/ // mode & 8|1: behave like require 56 | /******/ __webpack_require__.t = function(value, mode) { 57 | /******/ if(mode & 1) value = __webpack_require__(value); 58 | /******/ if(mode & 8) return value; 59 | /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; 60 | /******/ var ns = Object.create(null); 61 | /******/ __webpack_require__.r(ns); 62 | /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); 63 | /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); 64 | /******/ return ns; 65 | /******/ }; 66 | /******/ 67 | /******/ // getDefaultExport function for compatibility with non-harmony modules 68 | /******/ __webpack_require__.n = function(module) { 69 | /******/ var getter = module && module.__esModule ? 70 | /******/ function getDefault() { return module['default']; } : 71 | /******/ function getModuleExports() { return module; }; 72 | /******/ __webpack_require__.d(getter, 'a', getter); 73 | /******/ return getter; 74 | /******/ }; 75 | /******/ 76 | /******/ // Object.prototype.hasOwnProperty.call 77 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 78 | /******/ 79 | /******/ // __webpack_public_path__ 80 | /******/ __webpack_require__.p = ""; 81 | /******/ 82 | /******/ 83 | /******/ // Load entry module and return exports 84 | /******/ return __webpack_require__(__webpack_require__.s = "./src/main.ts-exposed"); 85 | /******/ }) 86 | /************************************************************************/ 87 | /******/ ({ 88 | 89 | /***/ "./node_modules/ts-loader/index.js!./src/main.ts": 90 | /*!**********************************************!*\ 91 | !*** ./node_modules/ts-loader!./src/main.ts ***! 92 | \**********************************************/ 93 | /*! no static exports found */ 94 | /***/ (function(module, exports, __webpack_require__) { 95 | 96 | "use strict"; 97 | eval("\r\nvar __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {\r\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\r\n return new (P || (P = Promise))(function (resolve, reject) {\r\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\r\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\r\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\r\n step((generator = generator.apply(thisArg, _arguments || [])).next());\r\n });\r\n};\r\nvar __generator = (this && this.__generator) || function (thisArg, body) {\r\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;\r\n return g = { next: verb(0), \"throw\": verb(1), \"return\": verb(2) }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\r\n function verb(n) { return function (v) { return step([n, v]); }; }\r\n function step(op) {\r\n if (f) throw new TypeError(\"Generator is already executing.\");\r\n while (_) try {\r\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\r\n if (y = 0, t) op = [op[0] & 2, t.value];\r\n switch (op[0]) {\r\n case 0: case 1: t = op; break;\r\n case 4: _.label++; return { value: op[1], done: false };\r\n case 5: _.label++; y = op[1]; op = [0]; continue;\r\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\r\n default:\r\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\r\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\r\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\r\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\r\n if (t[2]) _.ops.pop();\r\n _.trys.pop(); continue;\r\n }\r\n op = body.call(thisArg, _);\r\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\r\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\r\n }\r\n};\r\nvar __importDefault = (this && this.__importDefault) || function (mod) {\r\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\r\n};\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nvar webchannelCore_1 = __importDefault(__webpack_require__(/*! ./webchannelCore */ \"./src/webchannelCore.ts-exposed\"));\r\nwindow.onload = function () {\r\n webchannelCore_1.default.initialize(onInit, onUninit);\r\n};\r\nfunction onInit() {\r\n window.document.getElementById(\"img\").src = \"../img/connected.svg\";\r\n var car = webchannelCore_1.default.SDK.car;\r\n var upBtn = window.document.getElementById(\"up\");\r\n var downBtn = window.document.getElementById(\"down\");\r\n var leftBtn = window.document.getElementById(\"left\");\r\n var rightBtn = window.document.getElementById(\"right\");\r\n var speedLabel = window.document.getElementById(\"speed\");\r\n var angleLabel = window.document.getElementById(\"angle\");\r\n upBtn.onclick = function () {\r\n car.accelerate();\r\n };\r\n downBtn.onclick = function () {\r\n car.decelerate();\r\n };\r\n leftBtn.onclick = function () {\r\n car.turnLeft();\r\n };\r\n rightBtn.onclick = function () {\r\n car.turnRight();\r\n };\r\n car.speedChanged.connect(onSpeedChanged);\r\n car.angleChanged.connect(onAngleChanged);\r\n}\r\nfunction onUninit() {\r\n window.document.getElementById(\"img\").src = \"../img/disconnected.svg\";\r\n}\r\nfunction onSpeedChanged() {\r\n return __awaiter(this, void 0, void 0, function () {\r\n var speedLabel, car, _a;\r\n return __generator(this, function (_b) {\r\n switch (_b.label) {\r\n case 0:\r\n speedLabel = window.document.getElementById(\"speed\");\r\n car = webchannelCore_1.default.SDK.car;\r\n _a = speedLabel;\r\n return [4 /*yield*/, car.getSpeed()];\r\n case 1:\r\n _a.textContent = _b.sent();\r\n return [2 /*return*/];\r\n }\r\n });\r\n });\r\n}\r\nfunction onAngleChanged() {\r\n return __awaiter(this, void 0, void 0, function () {\r\n var angleLabel, car, _a;\r\n return __generator(this, function (_b) {\r\n switch (_b.label) {\r\n case 0:\r\n angleLabel = window.document.getElementById(\"angle\");\r\n car = webchannelCore_1.default.SDK.car;\r\n _a = angleLabel;\r\n return [4 /*yield*/, car.getAngle()];\r\n case 1:\r\n _a.textContent = _b.sent();\r\n return [2 /*return*/];\r\n }\r\n });\r\n });\r\n}\r\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vLi9zcmMvbWFpbi50cz9jZDQ5Il0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUEsdUhBQThDO0FBQzlDLE1BQU0sQ0FBQyxNQUFNLEdBQUc7SUFDWix3QkFBYyxDQUFDLFVBQVUsQ0FBQyxNQUFNLEVBQUUsUUFBUSxDQUFDLENBQUM7QUFDaEQsQ0FBQztBQUNELFNBQVMsTUFBTTtJQUNWLE1BQWMsQ0FBQyxRQUFRLENBQUMsY0FBYyxDQUFDLEtBQUssQ0FBQyxDQUFDLEdBQUcsR0FBRyxzQkFBc0IsQ0FBQztJQUU1RSxJQUFJLEdBQUcsR0FBRyx3QkFBYyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUM7SUFFakMsSUFBSSxLQUFLLEdBQUksTUFBYyxDQUFDLFFBQVEsQ0FBQyxjQUFjLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDMUQsSUFBSSxPQUFPLEdBQUksTUFBYyxDQUFDLFFBQVEsQ0FBQyxjQUFjLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDOUQsSUFBSSxPQUFPLEdBQUksTUFBYyxDQUFDLFFBQVEsQ0FBQyxjQUFjLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDOUQsSUFBSSxRQUFRLEdBQUksTUFBYyxDQUFDLFFBQVEsQ0FBQyxjQUFjLENBQUMsT0FBTyxDQUFDLENBQUM7SUFFaEUsSUFBSSxVQUFVLEdBQUksTUFBYyxDQUFDLFFBQVEsQ0FBQyxjQUFjLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDbEUsSUFBSSxVQUFVLEdBQUksTUFBYyxDQUFDLFFBQVEsQ0FBQyxjQUFjLENBQUMsT0FBTyxDQUFDLENBQUM7SUFFbEUsS0FBSyxDQUFDLE9BQU8sR0FBRztRQUNaLEdBQUcsQ0FBQyxVQUFVLEVBQUUsQ0FBQztJQUNyQixDQUFDO0lBQ0QsT0FBTyxDQUFDLE9BQU8sR0FBRztRQUNkLEdBQUcsQ0FBQyxVQUFVLEVBQUUsQ0FBQztJQUNyQixDQUFDO0lBQ0QsT0FBTyxDQUFDLE9BQU8sR0FBRztRQUNkLEdBQUcsQ0FBQyxRQUFRLEVBQUUsQ0FBQztJQUNuQixDQUFDO0lBQ0QsUUFBUSxDQUFDLE9BQU8sR0FBRztRQUNmLEdBQUcsQ0FBQyxTQUFTLEVBQUUsQ0FBQztJQUNwQixDQUFDO0lBRUQsR0FBRyxDQUFDLFlBQVksQ0FBQyxPQUFPLENBQUMsY0FBYyxDQUFDLENBQUM7SUFDekMsR0FBRyxDQUFDLFlBQVksQ0FBQyxPQUFPLENBQUMsY0FBYyxDQUFDLENBQUM7QUFDN0MsQ0FBQztBQUNELFNBQVMsUUFBUTtJQUNaLE1BQWMsQ0FBQyxRQUFRLENBQUMsY0FBYyxDQUFDLEtBQUssQ0FBQyxDQUFDLEdBQUcsR0FBRyx5QkFBeUIsQ0FBQztBQUNuRixDQUFDO0FBRUQsU0FBZSxjQUFjOzs7Ozs7b0JBQ3JCLFVBQVUsR0FBSSxNQUFjLENBQUMsUUFBUSxDQUFDLGNBQWMsQ0FBQyxPQUFPLENBQUMsQ0FBQztvQkFDOUQsR0FBRyxHQUFHLHdCQUFjLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQztvQkFFakMsZUFBVTtvQkFBZSxxQkFBTSxHQUFHLENBQUMsUUFBUSxFQUFFOztvQkFBN0MsR0FBVyxXQUFXLEdBQUcsU0FBb0IsQ0FBQzs7Ozs7Q0FDakQ7QUFDRCxTQUFlLGNBQWM7Ozs7OztvQkFDckIsVUFBVSxHQUFJLE1BQWMsQ0FBQyxRQUFRLENBQUMsY0FBYyxDQUFDLE9BQU8sQ0FBQyxDQUFDO29CQUM5RCxHQUFHLEdBQUcsd0JBQWMsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDO29CQUVqQyxlQUFVO29CQUFlLHFCQUFNLEdBQUcsQ0FBQyxRQUFRLEVBQUU7O29CQUE3QyxHQUFXLFdBQVcsR0FBRyxTQUFvQixDQUFDOzs7OztDQUNqRCIsImZpbGUiOiIuL25vZGVfbW9kdWxlcy90cy1sb2FkZXIvaW5kZXguanMhLi9zcmMvbWFpbi50cy5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBXZWJDaGFubmVsQ29yZSBmcm9tIFwiLi93ZWJjaGFubmVsQ29yZVwiO1xyXG53aW5kb3cub25sb2FkID0gKCkgPT4ge1xyXG4gICAgV2ViQ2hhbm5lbENvcmUuaW5pdGlhbGl6ZShvbkluaXQsIG9uVW5pbml0KTtcclxufVxyXG5mdW5jdGlvbiBvbkluaXQoKSB7XHJcbiAgICAod2luZG93IGFzIGFueSkuZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJpbWdcIikuc3JjID0gXCIuLi9pbWcvY29ubmVjdGVkLnN2Z1wiO1xyXG5cclxuICAgIGxldCBjYXIgPSBXZWJDaGFubmVsQ29yZS5TREsuY2FyO1xyXG5cclxuICAgIGxldCB1cEJ0biA9ICh3aW5kb3cgYXMgYW55KS5kb2N1bWVudC5nZXRFbGVtZW50QnlJZChcInVwXCIpO1xyXG4gICAgbGV0IGRvd25CdG4gPSAod2luZG93IGFzIGFueSkuZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJkb3duXCIpO1xyXG4gICAgbGV0IGxlZnRCdG4gPSAod2luZG93IGFzIGFueSkuZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJsZWZ0XCIpO1xyXG4gICAgbGV0IHJpZ2h0QnRuID0gKHdpbmRvdyBhcyBhbnkpLmRvY3VtZW50LmdldEVsZW1lbnRCeUlkKFwicmlnaHRcIik7XHJcblxyXG4gICAgbGV0IHNwZWVkTGFiZWwgPSAod2luZG93IGFzIGFueSkuZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJzcGVlZFwiKTtcclxuICAgIGxldCBhbmdsZUxhYmVsID0gKHdpbmRvdyBhcyBhbnkpLmRvY3VtZW50LmdldEVsZW1lbnRCeUlkKFwiYW5nbGVcIik7XHJcblxyXG4gICAgdXBCdG4ub25jbGljayA9ICgpID0+IHtcclxuICAgICAgICBjYXIuYWNjZWxlcmF0ZSgpO1xyXG4gICAgfVxyXG4gICAgZG93bkJ0bi5vbmNsaWNrID0gKCkgPT4ge1xyXG4gICAgICAgIGNhci5kZWNlbGVyYXRlKCk7XHJcbiAgICB9XHJcbiAgICBsZWZ0QnRuLm9uY2xpY2sgPSAoKSA9PiB7XHJcbiAgICAgICAgY2FyLnR1cm5MZWZ0KCk7XHJcbiAgICB9XHJcbiAgICByaWdodEJ0bi5vbmNsaWNrID0gKCkgPT4ge1xyXG4gICAgICAgIGNhci50dXJuUmlnaHQoKTtcclxuICAgIH1cclxuXHJcbiAgICBjYXIuc3BlZWRDaGFuZ2VkLmNvbm5lY3Qob25TcGVlZENoYW5nZWQpO1xyXG4gICAgY2FyLmFuZ2xlQ2hhbmdlZC5jb25uZWN0KG9uQW5nbGVDaGFuZ2VkKTtcclxufVxyXG5mdW5jdGlvbiBvblVuaW5pdCgpIHtcclxuICAgICh3aW5kb3cgYXMgYW55KS5kb2N1bWVudC5nZXRFbGVtZW50QnlJZChcImltZ1wiKS5zcmMgPSBcIi4uL2ltZy9kaXNjb25uZWN0ZWQuc3ZnXCI7XHJcbn1cclxuXHJcbmFzeW5jIGZ1bmN0aW9uIG9uU3BlZWRDaGFuZ2VkKCkge1xyXG4gICAgbGV0IHNwZWVkTGFiZWwgPSAod2luZG93IGFzIGFueSkuZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJzcGVlZFwiKTtcclxuICAgIGxldCBjYXIgPSBXZWJDaGFubmVsQ29yZS5TREsuY2FyO1xyXG4gICAgXHJcbiAgICBzcGVlZExhYmVsLnRleHRDb250ZW50ID0gYXdhaXQgY2FyLmdldFNwZWVkKCk7XHJcbn1cclxuYXN5bmMgZnVuY3Rpb24gb25BbmdsZUNoYW5nZWQoKSB7XHJcbiAgICBsZXQgYW5nbGVMYWJlbCA9ICh3aW5kb3cgYXMgYW55KS5kb2N1bWVudC5nZXRFbGVtZW50QnlJZChcImFuZ2xlXCIpO1xyXG4gICAgbGV0IGNhciA9IFdlYkNoYW5uZWxDb3JlLlNESy5jYXI7XHJcblxyXG4gICAgYW5nbGVMYWJlbC50ZXh0Q29udGVudCA9IGF3YWl0IGNhci5nZXRBbmdsZSgpO1xyXG59XHJcbiJdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///./node_modules/ts-loader/index.js!./src/main.ts\n"); 98 | 99 | /***/ }), 100 | 101 | /***/ "./node_modules/ts-loader/index.js!./src/webchannelCore.ts": 102 | /*!********************************************************!*\ 103 | !*** ./node_modules/ts-loader!./src/webchannelCore.ts ***! 104 | \********************************************************/ 105 | /*! no static exports found */ 106 | /***/ (function(module, exports, __webpack_require__) { 107 | 108 | "use strict"; 109 | eval("\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nvar qwebchannel_1 = __webpack_require__(/*! ./qwebchannel */ \"./src/qwebchannel.js\");\r\nvar WebChannelCore = /** @class */ (function () {\r\n function WebChannelCore() {\r\n }\r\n WebChannelCore.initialize = function (connectedCb, disconnectedCb) {\r\n if (connectedCb === void 0) { connectedCb = function () { }; }\r\n if (disconnectedCb === void 0) { disconnectedCb = function () { }; }\r\n if (WebChannelCore.SDK != undefined) {\r\n return;\r\n }\r\n WebChannelCore.connectedCb = connectedCb;\r\n WebChannelCore.disconnectedCb = disconnectedCb;\r\n try {\r\n WebChannelCore.link(function (socket) {\r\n qwebchannel_1.QWebChannel(socket, function (channel) {\r\n WebChannelCore.SDK = channel.objects;\r\n WebChannelCore.connectedCb();\r\n });\r\n }, function (error) {\r\n console.log(\"socket error\", error);\r\n WebChannelCore.disconnectedCb();\r\n });\r\n }\r\n catch (error) {\r\n console.log(\"socket exception:\", error);\r\n WebChannelCore.disconnectedCb();\r\n WebChannelCore.SDK = undefined;\r\n }\r\n };\r\n WebChannelCore.link = function (resolve, reject) {\r\n var baseUrl = \"ws://localhost:12345\";\r\n if (window.location.search != \"\") {\r\n baseUrl = (/[?&]webChannelBaseUrl=([A-Za-z0-9\\-:/\\.]+)/.exec(window.location.search)[1]);\r\n }\r\n console.log(\"Connectiong to WebSocket server at: \", baseUrl);\r\n var socket = new WebSocket(baseUrl);\r\n WebChannelCore.socket = socket;\r\n socket.onopen = function () {\r\n resolve(socket);\r\n };\r\n socket.onerror = function (error) {\r\n reject(error);\r\n };\r\n socket.onclose = function (error) {\r\n reject(error);\r\n };\r\n };\r\n WebChannelCore.SDK = undefined;\r\n return WebChannelCore;\r\n}());\r\nexports.default = WebChannelCore;\r\nwindow.SDK = WebChannelCore.SDK;\r\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vLi9zcmMvd2ViY2hhbm5lbENvcmUudHM/ZDU2MyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLHFGQUE0QztBQUc1QztJQUFBO0lBaURBLENBQUM7SUE1Q2lCLHlCQUFVLEdBQXhCLFVBQXlCLFdBQWlDLEVBQUUsY0FBb0M7UUFBdkUsMERBQWdDLENBQUM7UUFBRSxnRUFBbUMsQ0FBQztRQUM1RixJQUFJLGNBQWMsQ0FBQyxHQUFHLElBQUksU0FBUyxFQUFFO1lBQ2pDLE9BQU87U0FDVjtRQUNELGNBQWMsQ0FBQyxXQUFXLEdBQUcsV0FBVyxDQUFDO1FBQ3pDLGNBQWMsQ0FBQyxjQUFjLEdBQUcsY0FBYyxDQUFDO1FBQy9DLElBQUk7WUFDQSxjQUFjLENBQUMsSUFBSSxDQUNmLFVBQUMsTUFBTTtnQkFDSCx5QkFBVyxDQUFDLE1BQU0sRUFBRSxVQUFDLE9BQVk7b0JBQzdCLGNBQWMsQ0FBQyxHQUFHLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQztvQkFDckMsY0FBYyxDQUFDLFdBQVcsRUFBRSxDQUFDO2dCQUNqQyxDQUFDLENBQUMsQ0FBQztZQUNQLENBQUMsRUFDQyxVQUFDLEtBQUs7Z0JBQ0osT0FBTyxDQUFDLEdBQUcsQ0FBQyxjQUFjLEVBQUUsS0FBSyxDQUFDLENBQUM7Z0JBQ25DLGNBQWMsQ0FBQyxjQUFjLEVBQUUsQ0FBQztZQUNwQyxDQUFDLENBQUMsQ0FBQztTQUNWO1FBQUMsT0FBTyxLQUFLLEVBQUU7WUFDWixPQUFPLENBQUMsR0FBRyxDQUFDLG1CQUFtQixFQUFFLEtBQUssQ0FBQyxDQUFDO1lBQ3hDLGNBQWMsQ0FBQyxjQUFjLEVBQUUsQ0FBQztZQUNoQyxjQUFjLENBQUMsR0FBRyxHQUFHLFNBQVMsQ0FBQztTQUNsQztJQUNMLENBQUM7SUFFYyxtQkFBSSxHQUFuQixVQUFvQixPQUFvQyxFQUFFLE1BQTJDO1FBQ2pHLElBQUksT0FBTyxHQUFHLHNCQUFzQixDQUFDO1FBQ3JDLElBQUksTUFBTSxDQUFDLFFBQVEsQ0FBQyxNQUFNLElBQUksRUFBRSxFQUFFO1lBQzlCLE9BQU8sR0FBRyxDQUFDLDRDQUE0QyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7U0FDN0Y7UUFDRCxPQUFPLENBQUMsR0FBRyxDQUFDLHNDQUFzQyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBRTdELElBQUksTUFBTSxHQUFHLElBQUksU0FBUyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ3BDLGNBQWMsQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDO1FBQy9CLE1BQU0sQ0FBQyxNQUFNLEdBQUc7WUFDWixPQUFPLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDcEIsQ0FBQyxDQUFDO1FBQ0YsTUFBTSxDQUFDLE9BQU8sR0FBRyxVQUFDLEtBQUs7WUFDbkIsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ2xCLENBQUMsQ0FBQztRQUNGLE1BQU0sQ0FBQyxPQUFPLEdBQUcsVUFBQyxLQUFLO1lBQ25CLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUNsQixDQUFDLENBQUM7SUFDTixDQUFDO0lBL0NhLGtCQUFHLEdBQVEsU0FBUyxDQUFDO0lBZ0R2QyxxQkFBQztDQUFBO2tCQWpEb0IsY0FBYztBQWtEbEMsTUFBYyxDQUFDLEdBQUcsR0FBRyxjQUFjLENBQUMsR0FBRyxDQUFDIiwiZmlsZSI6Ii4vbm9kZV9tb2R1bGVzL3RzLWxvYWRlci9pbmRleC5qcyEuL3NyYy93ZWJjaGFubmVsQ29yZS50cy5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IFFXZWJDaGFubmVsIH0gZnJvbSAnLi9xd2ViY2hhbm5lbCc7XHJcblxyXG50eXBlIGNhbGxiYWNrID0gKCkgPT4gdm9pZDtcclxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgV2ViQ2hhbm5lbENvcmUge1xyXG4gICAgcHVibGljIHN0YXRpYyBTREs6IGFueSA9IHVuZGVmaW5lZDtcclxuICAgIHByaXZhdGUgc3RhdGljIGNvbm5lY3RlZENiOiBjYWxsYmFjaztcclxuICAgIHByaXZhdGUgc3RhdGljIGRpc2Nvbm5lY3RlZENiOiBjYWxsYmFjaztcclxuICAgIHByaXZhdGUgc3RhdGljIHNvY2tldDogV2ViU29ja2V0O1xyXG4gICAgcHVibGljIHN0YXRpYyBpbml0aWFsaXplKGNvbm5lY3RlZENiOiBjYWxsYmFjayA9ICgpID0+IHsgfSwgZGlzY29ubmVjdGVkQ2I6IGNhbGxiYWNrID0gKCkgPT4geyB9KSB7XHJcbiAgICAgICAgaWYgKFdlYkNoYW5uZWxDb3JlLlNESyAhPSB1bmRlZmluZWQpIHtcclxuICAgICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgIH1cclxuICAgICAgICBXZWJDaGFubmVsQ29yZS5jb25uZWN0ZWRDYiA9IGNvbm5lY3RlZENiO1xyXG4gICAgICAgIFdlYkNoYW5uZWxDb3JlLmRpc2Nvbm5lY3RlZENiID0gZGlzY29ubmVjdGVkQ2I7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgV2ViQ2hhbm5lbENvcmUubGluayhcclxuICAgICAgICAgICAgICAgIChzb2NrZXQpID0+IHtcclxuICAgICAgICAgICAgICAgICAgICBRV2ViQ2hhbm5lbChzb2NrZXQsIChjaGFubmVsOiBhbnkpID0+IHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgV2ViQ2hhbm5lbENvcmUuU0RLID0gY2hhbm5lbC5vYmplY3RzO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBXZWJDaGFubmVsQ29yZS5jb25uZWN0ZWRDYigpO1xyXG4gICAgICAgICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgLCAoZXJyb3IpID0+IHtcclxuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcInNvY2tldCBlcnJvclwiLCBlcnJvcik7XHJcbiAgICAgICAgICAgICAgICAgICAgV2ViQ2hhbm5lbENvcmUuZGlzY29ubmVjdGVkQ2IoKTtcclxuICAgICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKFwic29ja2V0IGV4Y2VwdGlvbjpcIiwgZXJyb3IpO1xyXG4gICAgICAgICAgICBXZWJDaGFubmVsQ29yZS5kaXNjb25uZWN0ZWRDYigpO1xyXG4gICAgICAgICAgICBXZWJDaGFubmVsQ29yZS5TREsgPSB1bmRlZmluZWQ7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIHByaXZhdGUgc3RhdGljIGxpbmsocmVzb2x2ZTogKHNvY2tldDogV2ViU29ja2V0KSA9PiB2b2lkLCByZWplY3Q6IChlcnJvcjogRXZlbnQgfCBDbG9zZUV2ZW50KSA9PiB2b2lkKSB7XHJcbiAgICAgICAgbGV0IGJhc2VVcmwgPSBcIndzOi8vbG9jYWxob3N0OjEyMzQ1XCI7XHJcbiAgICAgICAgaWYgKHdpbmRvdy5sb2NhdGlvbi5zZWFyY2ggIT0gXCJcIikge1xyXG4gICAgICAgICAgICBiYXNlVXJsID0gKC9bPyZdd2ViQ2hhbm5lbEJhc2VVcmw9KFtBLVphLXowLTlcXC06L1xcLl0rKS8uZXhlYyh3aW5kb3cubG9jYXRpb24uc2VhcmNoKSFbMV0pO1xyXG4gICAgICAgIH1cclxuICAgICAgICBjb25zb2xlLmxvZyhcIkNvbm5lY3Rpb25nIHRvIFdlYlNvY2tldCBzZXJ2ZXIgYXQ6IFwiLCBiYXNlVXJsKTtcclxuXHJcbiAgICAgICAgbGV0IHNvY2tldCA9IG5ldyBXZWJTb2NrZXQoYmFzZVVybCk7XHJcbiAgICAgICAgV2ViQ2hhbm5lbENvcmUuc29ja2V0ID0gc29ja2V0O1xyXG4gICAgICAgIHNvY2tldC5vbm9wZW4gPSAoKSA9PiB7XHJcbiAgICAgICAgICAgIHJlc29sdmUoc29ja2V0KTtcclxuICAgICAgICB9O1xyXG4gICAgICAgIHNvY2tldC5vbmVycm9yID0gKGVycm9yKSA9PiB7XHJcbiAgICAgICAgICAgIHJlamVjdChlcnJvcik7XHJcbiAgICAgICAgfTtcclxuICAgICAgICBzb2NrZXQub25jbG9zZSA9IChlcnJvcikgPT4ge1xyXG4gICAgICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICAgIH07XHJcbiAgICB9XHJcbn1cclxuKHdpbmRvdyBhcyBhbnkpLlNESyA9IFdlYkNoYW5uZWxDb3JlLlNESztcclxuIl0sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///./node_modules/ts-loader/index.js!./src/webchannelCore.ts\n"); 110 | 111 | /***/ }), 112 | 113 | /***/ "./node_modules/webpack/buildin/global.js": 114 | /*!***********************************!*\ 115 | !*** (webpack)/buildin/global.js ***! 116 | \***********************************/ 117 | /*! no static exports found */ 118 | /***/ (function(module, exports) { 119 | 120 | eval("var g;\n\n// This works in non-strict mode\ng = (function() {\n\treturn this;\n})();\n\ntry {\n\t// This works if eval is allowed (see CSP)\n\tg = g || new Function(\"return this\")();\n} catch (e) {\n\t// This works if the window reference is available\n\tif (typeof window === \"object\") g = window;\n}\n\n// g can still be undefined, but nothing to do about it...\n// We return undefined, instead of nothing here, so it's\n// easier to handle this case. if(!global) { ...}\n\nmodule.exports = g;\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vKHdlYnBhY2spL2J1aWxkaW4vZ2xvYmFsLmpzP2NkMDAiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsQ0FBQzs7QUFFRDtBQUNBO0FBQ0E7QUFDQSxDQUFDO0FBQ0Q7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSw0Q0FBNEM7O0FBRTVDIiwiZmlsZSI6Ii4vbm9kZV9tb2R1bGVzL3dlYnBhY2svYnVpbGRpbi9nbG9iYWwuanMuanMiLCJzb3VyY2VzQ29udGVudCI6WyJ2YXIgZztcblxuLy8gVGhpcyB3b3JrcyBpbiBub24tc3RyaWN0IG1vZGVcbmcgPSAoZnVuY3Rpb24oKSB7XG5cdHJldHVybiB0aGlzO1xufSkoKTtcblxudHJ5IHtcblx0Ly8gVGhpcyB3b3JrcyBpZiBldmFsIGlzIGFsbG93ZWQgKHNlZSBDU1ApXG5cdGcgPSBnIHx8IG5ldyBGdW5jdGlvbihcInJldHVybiB0aGlzXCIpKCk7XG59IGNhdGNoIChlKSB7XG5cdC8vIFRoaXMgd29ya3MgaWYgdGhlIHdpbmRvdyByZWZlcmVuY2UgaXMgYXZhaWxhYmxlXG5cdGlmICh0eXBlb2Ygd2luZG93ID09PSBcIm9iamVjdFwiKSBnID0gd2luZG93O1xufVxuXG4vLyBnIGNhbiBzdGlsbCBiZSB1bmRlZmluZWQsIGJ1dCBub3RoaW5nIHRvIGRvIGFib3V0IGl0Li4uXG4vLyBXZSByZXR1cm4gdW5kZWZpbmVkLCBpbnN0ZWFkIG9mIG5vdGhpbmcgaGVyZSwgc28gaXQnc1xuLy8gZWFzaWVyIHRvIGhhbmRsZSB0aGlzIGNhc2UuIGlmKCFnbG9iYWwpIHsgLi4ufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGc7XG4iXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./node_modules/webpack/buildin/global.js\n"); 121 | 122 | /***/ }), 123 | 124 | /***/ "./src/main.ts-exposed": 125 | /*!*****************************!*\ 126 | !*** ./src/main.ts-exposed ***! 127 | \*****************************/ 128 | /*! no static exports found */ 129 | /***/ (function(module, exports, __webpack_require__) { 130 | 131 | eval("/* WEBPACK VAR INJECTION */(function(global) {module.exports = global[\"Library\"] = __webpack_require__(/*! -!./node_modules/ts-loader!./main.ts */ \"./node_modules/ts-loader/index.js!./src/main.ts\");\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../node_modules/webpack/buildin/global.js */ \"./node_modules/webpack/buildin/global.js\")))//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vLi9zcmMvbWFpbi50cy1leHBvc2VkPzY2MjYiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsbUZBQXFDLG1CQUFPLENBQUMsNkZBQW9HLEUiLCJmaWxlIjoiLi9zcmMvbWFpbi50cy1leHBvc2VkLmpzIiwic291cmNlc0NvbnRlbnQiOlsibW9kdWxlLmV4cG9ydHMgPSBnbG9iYWxbXCJMaWJyYXJ5XCJdID0gcmVxdWlyZShcIi0hRTpcXFxcRGV2XFxcXExhYnNcXFxcUXRcXFxcTWluZVxcXFxRdFdlYlxcXFxXZWJDaGFubmVsQ2FyXFxcXFdlYlxcXFxub2RlX21vZHVsZXNcXFxcdHMtbG9hZGVyXFxcXGluZGV4LmpzIS5cXFxcbWFpbi50c1wiKTsiXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./src/main.ts-exposed\n"); 132 | 133 | /***/ }), 134 | 135 | /***/ "./src/qwebchannel.js": 136 | /*!****************************!*\ 137 | !*** ./src/qwebchannel.js ***! 138 | \****************************/ 139 | /*! no static exports found */ 140 | /***/ (function(module, exports, __webpack_require__) { 141 | 142 | "use strict"; 143 | eval("/****************************************************************************\r\n**\r\n** Copyright (C) 2016 The Qt Company Ltd.\r\n** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff \r\n** Contact: https://www.qt.io/licensing/\r\n**\r\n** This file is part of the QtWebChannel module of the Qt Toolkit.\r\n**\r\n** $QT_BEGIN_LICENSE:LGPL$\r\n** Commercial License Usage\r\n** Licensees holding valid commercial Qt licenses may use this file in\r\n** accordance with the commercial license agreement provided with the\r\n** Software or, alternatively, in accordance with the terms contained in\r\n** a written agreement between you and The Qt Company. For licensing terms\r\n** and conditions see https://www.qt.io/terms-conditions. For further\r\n** information use the contact form at https://www.qt.io/contact-us.\r\n**\r\n** GNU Lesser General Public License Usage\r\n** Alternatively, this file may be used under the terms of the GNU Lesser\r\n** General Public License version 3 as published by the Free Software\r\n** Foundation and appearing in the file LICENSE.LGPL3 included in the\r\n** packaging of this file. Please review the following information to\r\n** ensure the GNU Lesser General Public License version 3 requirements\r\n** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\r\n**\r\n** GNU General Public License Usage\r\n** Alternatively, this file may be used under the terms of the GNU\r\n** General Public License version 2.0 or (at your option) the GNU General\r\n** Public license version 3 or any later version approved by the KDE Free\r\n** Qt Foundation. The licenses are as published by the Free Software\r\n** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\r\n** included in the packaging of this file. Please review the following\r\n** information to ensure the GNU General Public License requirements will\r\n** be met: https://www.gnu.org/licenses/gpl-2.0.html and\r\n** https://www.gnu.org/licenses/gpl-3.0.html.\r\n**\r\n** $QT_END_LICENSE$\r\n**\r\n****************************************************************************/\r\n\r\n\r\n\r\nvar QWebChannelMessageTypes = {\r\n signal: 1,\r\n propertyUpdate: 2,\r\n init: 3,\r\n idle: 4,\r\n debug: 5,\r\n invokeMethod: 6,\r\n connectToSignal: 7,\r\n disconnectFromSignal: 8,\r\n setProperty: 9,\r\n response: 10,\r\n};\r\n\r\nvar QWebChannel = function(transport, initCallback)\r\n{\r\n if (typeof transport !== \"object\" || typeof transport.send !== \"function\") {\r\n console.error(\"The QWebChannel expects a transport object with a send function and onmessage callback property.\" +\r\n \" Given is: transport: \" + typeof(transport) + \", transport.send: \" + typeof(transport.send));\r\n return;\r\n }\r\n\r\n var channel = this;\r\n this.transport = transport;\r\n\r\n this.send = function(data)\r\n {\r\n if (typeof(data) !== \"string\") {\r\n data = JSON.stringify(data);\r\n }\r\n channel.transport.send(data);\r\n }\r\n\r\n this.transport.onmessage = function(message)\r\n {\r\n var data = message.data;\r\n if (typeof data === \"string\") {\r\n data = JSON.parse(data);\r\n }\r\n switch (data.type) {\r\n case QWebChannelMessageTypes.signal:\r\n channel.handleSignal(data);\r\n break;\r\n case QWebChannelMessageTypes.response:\r\n channel.handleResponse(data);\r\n break;\r\n case QWebChannelMessageTypes.propertyUpdate:\r\n channel.handlePropertyUpdate(data);\r\n break;\r\n default:\r\n console.error(\"invalid message received:\", message.data);\r\n break;\r\n }\r\n }\r\n\r\n this.execCallbacks = {};\r\n this.execId = 0;\r\n this.exec = function(data, callback)\r\n {\r\n if (!callback) {\r\n // if no callback is given, send directly\r\n channel.send(data);\r\n return;\r\n }\r\n if (channel.execId === Number.MAX_VALUE) {\r\n // wrap\r\n channel.execId = Number.MIN_VALUE;\r\n }\r\n if (data.hasOwnProperty(\"id\")) {\r\n console.error(\"Cannot exec message with property id: \" + JSON.stringify(data));\r\n return;\r\n }\r\n data.id = channel.execId++;\r\n channel.execCallbacks[data.id] = callback;\r\n channel.send(data);\r\n };\r\n\r\n this.objects = {};\r\n\r\n this.handleSignal = function(message)\r\n {\r\n var object = channel.objects[message.object];\r\n if (object) {\r\n object.signalEmitted(message.signal, message.args);\r\n } else {\r\n console.warn(\"Unhandled signal: \" + message.object + \"::\" + message.signal);\r\n }\r\n }\r\n\r\n this.handleResponse = function(message)\r\n {\r\n if (!message.hasOwnProperty(\"id\")) {\r\n console.error(\"Invalid response message received: \", JSON.stringify(message));\r\n return;\r\n }\r\n channel.execCallbacks[message.id](message.data);\r\n delete channel.execCallbacks[message.id];\r\n }\r\n\r\n this.handlePropertyUpdate = function(message)\r\n {\r\n for (var i in message.data) {\r\n var data = message.data[i];\r\n var object = channel.objects[data.object];\r\n if (object) {\r\n object.propertyUpdate(data.signals, data.properties);\r\n } else {\r\n console.warn(\"Unhandled property update: \" + data.object + \"::\" + data.signal);\r\n }\r\n }\r\n channel.exec({type: QWebChannelMessageTypes.idle});\r\n }\r\n\r\n this.debug = function(message)\r\n {\r\n channel.send({type: QWebChannelMessageTypes.debug, data: message});\r\n };\r\n\r\n channel.exec({type: QWebChannelMessageTypes.init}, function(data) {\r\n for (var objectName in data) {\r\n var object = new QObject(objectName, data[objectName], channel);\r\n }\r\n // now unwrap properties, which might reference other registered objects\r\n for (var objectName in channel.objects) {\r\n channel.objects[objectName].unwrapProperties();\r\n }\r\n if (initCallback) {\r\n initCallback(channel);\r\n }\r\n channel.exec({type: QWebChannelMessageTypes.idle});\r\n });\r\n};\r\n\r\nfunction QObject(name, data, webChannel)\r\n{\r\n this.__id__ = name;\r\n webChannel.objects[name] = this;\r\n\r\n // List of callbacks that get invoked upon signal emission\r\n this.__objectSignals__ = {};\r\n\r\n // Cache of all properties, updated when a notify signal is emitted\r\n this.__propertyCache__ = {};\r\n\r\n var object = this;\r\n\r\n // ----------------------------------------------------------------------\r\n\r\n this.unwrapQObject = function(response)\r\n {\r\n if (response instanceof Array) {\r\n // support list of objects\r\n var ret = new Array(response.length);\r\n for (var i = 0; i < response.length; ++i) {\r\n ret[i] = object.unwrapQObject(response[i]);\r\n }\r\n return ret;\r\n }\r\n if (!response\r\n || !response[\"__QObject*__\"]\r\n || response.id === undefined) {\r\n return response;\r\n }\r\n\r\n var objectId = response.id;\r\n if (webChannel.objects[objectId])\r\n return webChannel.objects[objectId];\r\n\r\n if (!response.data) {\r\n console.error(\"Cannot unwrap unknown QObject \" + objectId + \" without data.\");\r\n return;\r\n }\r\n\r\n var qObject = new QObject( objectId, response.data, webChannel );\r\n qObject.destroyed.connect(function() {\r\n if (webChannel.objects[objectId] === qObject) {\r\n delete webChannel.objects[objectId];\r\n // reset the now deleted QObject to an empty {} object\r\n // just assigning {} though would not have the desired effect, but the\r\n // below also ensures all external references will see the empty map\r\n // NOTE: this detour is necessary to workaround QTBUG-40021\r\n var propertyNames = [];\r\n for (var propertyName in qObject) {\r\n propertyNames.push(propertyName);\r\n }\r\n for (var idx in propertyNames) {\r\n delete qObject[propertyNames[idx]];\r\n }\r\n }\r\n });\r\n // here we are already initialized, and thus must directly unwrap the properties\r\n qObject.unwrapProperties();\r\n return qObject;\r\n }\r\n\r\n this.unwrapProperties = function()\r\n {\r\n for (var propertyIdx in object.__propertyCache__) {\r\n object.__propertyCache__[propertyIdx] = object.unwrapQObject(object.__propertyCache__[propertyIdx]);\r\n }\r\n }\r\n\r\n function addSignal(signalData, isPropertyNotifySignal)\r\n {\r\n var signalName = signalData[0];\r\n var signalIndex = signalData[1];\r\n object[signalName] = {\r\n connect: function(callback) {\r\n if (typeof(callback) !== \"function\") {\r\n console.error(\"Bad callback given to connect to signal \" + signalName);\r\n return;\r\n }\r\n\r\n object.__objectSignals__[signalIndex] = object.__objectSignals__[signalIndex] || [];\r\n object.__objectSignals__[signalIndex].push(callback);\r\n\r\n if (!isPropertyNotifySignal && signalName !== \"destroyed\") {\r\n // only required for \"pure\" signals, handled separately for properties in propertyUpdate\r\n // also note that we always get notified about the destroyed signal\r\n webChannel.exec({\r\n type: QWebChannelMessageTypes.connectToSignal,\r\n object: object.__id__,\r\n signal: signalIndex\r\n });\r\n }\r\n },\r\n disconnect: function(callback) {\r\n if (typeof(callback) !== \"function\") {\r\n console.error(\"Bad callback given to disconnect from signal \" + signalName);\r\n return;\r\n }\r\n object.__objectSignals__[signalIndex] = object.__objectSignals__[signalIndex] || [];\r\n var idx = object.__objectSignals__[signalIndex].indexOf(callback);\r\n if (idx === -1) {\r\n console.error(\"Cannot find connection of signal \" + signalName + \" to \" + callback.name);\r\n return;\r\n }\r\n object.__objectSignals__[signalIndex].splice(idx, 1);\r\n if (!isPropertyNotifySignal && object.__objectSignals__[signalIndex].length === 0) {\r\n // only required for \"pure\" signals, handled separately for properties in propertyUpdate\r\n webChannel.exec({\r\n type: QWebChannelMessageTypes.disconnectFromSignal,\r\n object: object.__id__,\r\n signal: signalIndex\r\n });\r\n }\r\n }\r\n };\r\n }\r\n\r\n /**\r\n * Invokes all callbacks for the given signalname. Also works for property notify callbacks.\r\n */\r\n function invokeSignalCallbacks(signalName, signalArgs)\r\n {\r\n var connections = object.__objectSignals__[signalName];\r\n if (connections) {\r\n connections.forEach(function(callback) {\r\n callback.apply(callback, signalArgs);\r\n });\r\n }\r\n }\r\n\r\n this.propertyUpdate = function(signals, propertyMap)\r\n {\r\n // update property cache\r\n for (var propertyIndex in propertyMap) {\r\n var propertyValue = propertyMap[propertyIndex];\r\n object.__propertyCache__[propertyIndex] = propertyValue;\r\n }\r\n\r\n for (var signalName in signals) {\r\n // Invoke all callbacks, as signalEmitted() does not. This ensures the\r\n // property cache is updated before the callbacks are invoked.\r\n invokeSignalCallbacks(signalName, signals[signalName]);\r\n }\r\n }\r\n\r\n this.signalEmitted = function(signalName, signalArgs)\r\n {\r\n invokeSignalCallbacks(signalName, this.unwrapQObject(signalArgs));\r\n }\r\n\r\n function addMethod(methodData)\r\n {\r\n var methodName = methodData[0];\r\n var methodIdx = methodData[1];\r\n object[methodName] = async function() {\r\n var args = [];\r\n var callback;\r\n var result;\r\n for (var i = 0; i < arguments.length; ++i) {\r\n var argument = arguments[i];\r\n if (typeof argument === \"function\")\r\n callback = argument;\r\n else if (argument instanceof QObject && webChannel.objects[argument.__id__] !== undefined)\r\n args.push({\r\n \"id\": argument.__id__\r\n });\r\n else\r\n args.push(argument);\r\n }\r\n\r\n await new Promise((resolve)=>{\r\n webChannel.exec({\r\n \"type\": QWebChannelMessageTypes.invokeMethod,\r\n \"object\": object.__id__,\r\n \"method\": methodIdx,\r\n \"args\": args\r\n }, function(response) {\r\n if (response !== undefined) {\r\n result = object.unwrapQObject(response);\r\n if (callback) {\r\n (callback)(result);\r\n }\r\n resolve();\r\n }\r\n });\r\n });\r\n return result;\r\n };\r\n }\r\n\r\n function bindGetterSetter(propertyInfo)\r\n {\r\n var propertyIndex = propertyInfo[0];\r\n var propertyName = propertyInfo[1];\r\n var notifySignalData = propertyInfo[2];\r\n // initialize property cache with current value\r\n // NOTE: if this is an object, it is not directly unwrapped as it might\r\n // reference other QObject that we do not know yet\r\n object.__propertyCache__[propertyIndex] = propertyInfo[3];\r\n\r\n if (notifySignalData) {\r\n if (notifySignalData[0] === 1) {\r\n // signal name is optimized away, reconstruct the actual name\r\n notifySignalData[0] = propertyName + \"Changed\";\r\n }\r\n addSignal(notifySignalData, true);\r\n }\r\n\r\n Object.defineProperty(object, propertyName, {\r\n configurable: true,\r\n get: function () {\r\n var propertyValue = object.__propertyCache__[propertyIndex];\r\n if (propertyValue === undefined) {\r\n // This shouldn't happen\r\n console.warn(\"Undefined value in property cache for property \\\"\" + propertyName + \"\\\" in object \" + object.__id__);\r\n }\r\n\r\n return propertyValue;\r\n },\r\n set: function(value) {\r\n if (value === undefined) {\r\n console.warn(\"Property setter for \" + propertyName + \" called with undefined value!\");\r\n return;\r\n }\r\n object.__propertyCache__[propertyIndex] = value;\r\n var valueToSend = value;\r\n if (valueToSend instanceof QObject && webChannel.objects[valueToSend.__id__] !== undefined)\r\n valueToSend = { \"id\": valueToSend.__id__ };\r\n webChannel.exec({\r\n \"type\": QWebChannelMessageTypes.setProperty,\r\n \"object\": object.__id__,\r\n \"property\": propertyIndex,\r\n \"value\": valueToSend\r\n });\r\n }\r\n });\r\n }\r\n\r\n // ----------------------------------------------------------------------\r\n\r\n data.methods.forEach(addMethod);\r\n\r\n data.properties.forEach(bindGetterSetter);\r\n\r\n data.signals.forEach(function(signal) { addSignal(signal, false); });\r\n\r\n for (var name in data.enums) {\r\n object[name] = data.enums[name];\r\n }\r\n}\r\n\r\n//required for use with nodejs\r\nif (true) {\r\n module.exports = {\r\n QWebChannel: QWebChannel\r\n };\r\n}\r\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vLi9zcmMvcXdlYmNoYW5uZWwuanM/MTI5NSJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRWE7O0FBRWI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxhQUFhO0FBQ2I7QUFDQTtBQUNBO0FBQ0Esc0JBQXNCLG1DQUFtQztBQUN6RDs7QUFFQTtBQUNBO0FBQ0Esc0JBQXNCLG1EQUFtRDtBQUN6RTs7QUFFQSxrQkFBa0IsbUNBQW1DO0FBQ3JEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esc0JBQXNCLG1DQUFtQztBQUN6RCxLQUFLO0FBQ0w7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSwyQkFBMkIscUJBQXFCO0FBQ2hEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSwrREFBK0Q7QUFDL0Qsb0NBQW9DO0FBQ3BDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EscUJBQXFCO0FBQ3JCO0FBQ0EsYUFBYTtBQUNiO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHFCQUFxQjtBQUNyQjtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxhQUFhO0FBQ2I7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDJCQUEyQixzQkFBc0I7QUFDakQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EscUJBQXFCO0FBQ3JCO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxpQkFBaUI7QUFDakI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxpQkFBaUI7QUFDakIsYUFBYTtBQUNiO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLGFBQWE7QUFDYjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsbUNBQW1DO0FBQ25DO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxpQkFBaUI7QUFDakI7QUFDQSxTQUFTO0FBQ1Q7O0FBRUE7O0FBRUE7O0FBRUE7O0FBRUEsMkNBQTJDLDBCQUEwQixFQUFFOztBQUV2RTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLElBQUksSUFBMEI7QUFDOUI7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoiLi9zcmMvcXdlYmNoYW5uZWwuanMuanMiLCJzb3VyY2VzQ29udGVudCI6WyIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG4qKlxyXG4qKiBDb3B5cmlnaHQgKEMpIDIwMTYgVGhlIFF0IENvbXBhbnkgTHRkLlxyXG4qKiBDb3B5cmlnaHQgKEMpIDIwMTYgS2xhcsOkbHZkYWxlbnMgRGF0YWtvbnN1bHQgQUIsIGEgS0RBQiBHcm91cCBjb21wYW55LCBpbmZvQGtkYWIuY29tLCBhdXRob3IgTWlsaWFuIFdvbGZmIDxtaWxpYW4ud29sZmZAa2RhYi5jb20+XHJcbioqIENvbnRhY3Q6IGh0dHBzOi8vd3d3LnF0LmlvL2xpY2Vuc2luZy9cclxuKipcclxuKiogVGhpcyBmaWxlIGlzIHBhcnQgb2YgdGhlIFF0V2ViQ2hhbm5lbCBtb2R1bGUgb2YgdGhlIFF0IFRvb2xraXQuXHJcbioqXHJcbioqICRRVF9CRUdJTl9MSUNFTlNFOkxHUEwkXHJcbioqIENvbW1lcmNpYWwgTGljZW5zZSBVc2FnZVxyXG4qKiBMaWNlbnNlZXMgaG9sZGluZyB2YWxpZCBjb21tZXJjaWFsIFF0IGxpY2Vuc2VzIG1heSB1c2UgdGhpcyBmaWxlIGluXHJcbioqIGFjY29yZGFuY2Ugd2l0aCB0aGUgY29tbWVyY2lhbCBsaWNlbnNlIGFncmVlbWVudCBwcm92aWRlZCB3aXRoIHRoZVxyXG4qKiBTb2Z0d2FyZSBvciwgYWx0ZXJuYXRpdmVseSwgaW4gYWNjb3JkYW5jZSB3aXRoIHRoZSB0ZXJtcyBjb250YWluZWQgaW5cclxuKiogYSB3cml0dGVuIGFncmVlbWVudCBiZXR3ZWVuIHlvdSBhbmQgVGhlIFF0IENvbXBhbnkuIEZvciBsaWNlbnNpbmcgdGVybXNcclxuKiogYW5kIGNvbmRpdGlvbnMgc2VlIGh0dHBzOi8vd3d3LnF0LmlvL3Rlcm1zLWNvbmRpdGlvbnMuIEZvciBmdXJ0aGVyXHJcbioqIGluZm9ybWF0aW9uIHVzZSB0aGUgY29udGFjdCBmb3JtIGF0IGh0dHBzOi8vd3d3LnF0LmlvL2NvbnRhY3QtdXMuXHJcbioqXHJcbioqIEdOVSBMZXNzZXIgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBVc2FnZVxyXG4qKiBBbHRlcm5hdGl2ZWx5LCB0aGlzIGZpbGUgbWF5IGJlIHVzZWQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyXHJcbioqIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgdmVyc2lvbiAzIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZVxyXG4qKiBGb3VuZGF0aW9uIGFuZCBhcHBlYXJpbmcgaW4gdGhlIGZpbGUgTElDRU5TRS5MR1BMMyBpbmNsdWRlZCBpbiB0aGVcclxuKiogcGFja2FnaW5nIG9mIHRoaXMgZmlsZS4gUGxlYXNlIHJldmlldyB0aGUgZm9sbG93aW5nIGluZm9ybWF0aW9uIHRvXHJcbioqIGVuc3VyZSB0aGUgR05VIExlc3NlciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIHZlcnNpb24gMyByZXF1aXJlbWVudHNcclxuKiogd2lsbCBiZSBtZXQ6IGh0dHBzOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvbGdwbC0zLjAuaHRtbC5cclxuKipcclxuKiogR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgVXNhZ2VcclxuKiogQWx0ZXJuYXRpdmVseSwgdGhpcyBmaWxlIG1heSBiZSB1c2VkIHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VXHJcbioqIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgdmVyc2lvbiAyLjAgb3IgKGF0IHlvdXIgb3B0aW9uKSB0aGUgR05VIEdlbmVyYWxcclxuKiogUHVibGljIGxpY2Vuc2UgdmVyc2lvbiAzIG9yIGFueSBsYXRlciB2ZXJzaW9uIGFwcHJvdmVkIGJ5IHRoZSBLREUgRnJlZVxyXG4qKiBRdCBGb3VuZGF0aW9uLiBUaGUgbGljZW5zZXMgYXJlIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZVxyXG4qKiBGb3VuZGF0aW9uIGFuZCBhcHBlYXJpbmcgaW4gdGhlIGZpbGUgTElDRU5TRS5HUEwyIGFuZCBMSUNFTlNFLkdQTDNcclxuKiogaW5jbHVkZWQgaW4gdGhlIHBhY2thZ2luZyBvZiB0aGlzIGZpbGUuIFBsZWFzZSByZXZpZXcgdGhlIGZvbGxvd2luZ1xyXG4qKiBpbmZvcm1hdGlvbiB0byBlbnN1cmUgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIHJlcXVpcmVtZW50cyB3aWxsXHJcbioqIGJlIG1ldDogaHR0cHM6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy9ncGwtMi4wLmh0bWwgYW5kXHJcbioqIGh0dHBzOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvZ3BsLTMuMC5odG1sLlxyXG4qKlxyXG4qKiAkUVRfRU5EX0xJQ0VOU0UkXHJcbioqXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5cInVzZSBzdHJpY3RcIjtcclxuXHJcbnZhciBRV2ViQ2hhbm5lbE1lc3NhZ2VUeXBlcyA9IHtcclxuICAgIHNpZ25hbDogMSxcclxuICAgIHByb3BlcnR5VXBkYXRlOiAyLFxyXG4gICAgaW5pdDogMyxcclxuICAgIGlkbGU6IDQsXHJcbiAgICBkZWJ1ZzogNSxcclxuICAgIGludm9rZU1ldGhvZDogNixcclxuICAgIGNvbm5lY3RUb1NpZ25hbDogNyxcclxuICAgIGRpc2Nvbm5lY3RGcm9tU2lnbmFsOiA4LFxyXG4gICAgc2V0UHJvcGVydHk6IDksXHJcbiAgICByZXNwb25zZTogMTAsXHJcbn07XHJcblxyXG52YXIgUVdlYkNoYW5uZWwgPSBmdW5jdGlvbih0cmFuc3BvcnQsIGluaXRDYWxsYmFjaylcclxue1xyXG4gICAgaWYgKHR5cGVvZiB0cmFuc3BvcnQgIT09IFwib2JqZWN0XCIgfHwgdHlwZW9mIHRyYW5zcG9ydC5zZW5kICE9PSBcImZ1bmN0aW9uXCIpIHtcclxuICAgICAgICBjb25zb2xlLmVycm9yKFwiVGhlIFFXZWJDaGFubmVsIGV4cGVjdHMgYSB0cmFuc3BvcnQgb2JqZWN0IHdpdGggYSBzZW5kIGZ1bmN0aW9uIGFuZCBvbm1lc3NhZ2UgY2FsbGJhY2sgcHJvcGVydHkuXCIgK1xyXG4gICAgICAgICAgICAgICAgICAgICAgXCIgR2l2ZW4gaXM6IHRyYW5zcG9ydDogXCIgKyB0eXBlb2YodHJhbnNwb3J0KSArIFwiLCB0cmFuc3BvcnQuc2VuZDogXCIgKyB0eXBlb2YodHJhbnNwb3J0LnNlbmQpKTtcclxuICAgICAgICByZXR1cm47XHJcbiAgICB9XHJcblxyXG4gICAgdmFyIGNoYW5uZWwgPSB0aGlzO1xyXG4gICAgdGhpcy50cmFuc3BvcnQgPSB0cmFuc3BvcnQ7XHJcblxyXG4gICAgdGhpcy5zZW5kID0gZnVuY3Rpb24oZGF0YSlcclxuICAgIHtcclxuICAgICAgICBpZiAodHlwZW9mKGRhdGEpICE9PSBcInN0cmluZ1wiKSB7XHJcbiAgICAgICAgICAgIGRhdGEgPSBKU09OLnN0cmluZ2lmeShkYXRhKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgY2hhbm5lbC50cmFuc3BvcnQuc2VuZChkYXRhKTtcclxuICAgIH1cclxuXHJcbiAgICB0aGlzLnRyYW5zcG9ydC5vbm1lc3NhZ2UgPSBmdW5jdGlvbihtZXNzYWdlKVxyXG4gICAge1xyXG4gICAgICAgIHZhciBkYXRhID0gbWVzc2FnZS5kYXRhO1xyXG4gICAgICAgIGlmICh0eXBlb2YgZGF0YSA9PT0gXCJzdHJpbmdcIikge1xyXG4gICAgICAgICAgICBkYXRhID0gSlNPTi5wYXJzZShkYXRhKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgc3dpdGNoIChkYXRhLnR5cGUpIHtcclxuICAgICAgICAgICAgY2FzZSBRV2ViQ2hhbm5lbE1lc3NhZ2VUeXBlcy5zaWduYWw6XHJcbiAgICAgICAgICAgICAgICBjaGFubmVsLmhhbmRsZVNpZ25hbChkYXRhKTtcclxuICAgICAgICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgICAgICBjYXNlIFFXZWJDaGFubmVsTWVzc2FnZVR5cGVzLnJlc3BvbnNlOlxyXG4gICAgICAgICAgICAgICAgY2hhbm5lbC5oYW5kbGVSZXNwb25zZShkYXRhKTtcclxuICAgICAgICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgICAgICBjYXNlIFFXZWJDaGFubmVsTWVzc2FnZVR5cGVzLnByb3BlcnR5VXBkYXRlOlxyXG4gICAgICAgICAgICAgICAgY2hhbm5lbC5oYW5kbGVQcm9wZXJ0eVVwZGF0ZShkYXRhKTtcclxuICAgICAgICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgICAgICBkZWZhdWx0OlxyXG4gICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihcImludmFsaWQgbWVzc2FnZSByZWNlaXZlZDpcIiwgbWVzc2FnZS5kYXRhKTtcclxuICAgICAgICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICB0aGlzLmV4ZWNDYWxsYmFja3MgPSB7fTtcclxuICAgIHRoaXMuZXhlY0lkID0gMDtcclxuICAgIHRoaXMuZXhlYyA9IGZ1bmN0aW9uKGRhdGEsIGNhbGxiYWNrKVxyXG4gICAge1xyXG4gICAgICAgIGlmICghY2FsbGJhY2spIHtcclxuICAgICAgICAgICAgLy8gaWYgbm8gY2FsbGJhY2sgaXMgZ2l2ZW4sIHNlbmQgZGlyZWN0bHlcclxuICAgICAgICAgICAgY2hhbm5lbC5zZW5kKGRhdGEpO1xyXG4gICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGlmIChjaGFubmVsLmV4ZWNJZCA9PT0gTnVtYmVyLk1BWF9WQUxVRSkge1xyXG4gICAgICAgICAgICAvLyB3cmFwXHJcbiAgICAgICAgICAgIGNoYW5uZWwuZXhlY0lkID0gTnVtYmVyLk1JTl9WQUxVRTtcclxuICAgICAgICB9XHJcbiAgICAgICAgaWYgKGRhdGEuaGFzT3duUHJvcGVydHkoXCJpZFwiKSkge1xyXG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiQ2Fubm90IGV4ZWMgbWVzc2FnZSB3aXRoIHByb3BlcnR5IGlkOiBcIiArIEpTT04uc3RyaW5naWZ5KGRhdGEpKTtcclxuICAgICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgIH1cclxuICAgICAgICBkYXRhLmlkID0gY2hhbm5lbC5leGVjSWQrKztcclxuICAgICAgICBjaGFubmVsLmV4ZWNDYWxsYmFja3NbZGF0YS5pZF0gPSBjYWxsYmFjaztcclxuICAgICAgICBjaGFubmVsLnNlbmQoZGF0YSk7XHJcbiAgICB9O1xyXG5cclxuICAgIHRoaXMub2JqZWN0cyA9IHt9O1xyXG5cclxuICAgIHRoaXMuaGFuZGxlU2lnbmFsID0gZnVuY3Rpb24obWVzc2FnZSlcclxuICAgIHtcclxuICAgICAgICB2YXIgb2JqZWN0ID0gY2hhbm5lbC5vYmplY3RzW21lc3NhZ2Uub2JqZWN0XTtcclxuICAgICAgICBpZiAob2JqZWN0KSB7XHJcbiAgICAgICAgICAgIG9iamVjdC5zaWduYWxFbWl0dGVkKG1lc3NhZ2Uuc2lnbmFsLCBtZXNzYWdlLmFyZ3MpO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgIGNvbnNvbGUud2FybihcIlVuaGFuZGxlZCBzaWduYWw6IFwiICsgbWVzc2FnZS5vYmplY3QgKyBcIjo6XCIgKyBtZXNzYWdlLnNpZ25hbCk7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIHRoaXMuaGFuZGxlUmVzcG9uc2UgPSBmdW5jdGlvbihtZXNzYWdlKVxyXG4gICAge1xyXG4gICAgICAgIGlmICghbWVzc2FnZS5oYXNPd25Qcm9wZXJ0eShcImlkXCIpKSB7XHJcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJJbnZhbGlkIHJlc3BvbnNlIG1lc3NhZ2UgcmVjZWl2ZWQ6IFwiLCBKU09OLnN0cmluZ2lmeShtZXNzYWdlKSk7XHJcbiAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICB9XHJcbiAgICAgICAgY2hhbm5lbC5leGVjQ2FsbGJhY2tzW21lc3NhZ2UuaWRdKG1lc3NhZ2UuZGF0YSk7XHJcbiAgICAgICAgZGVsZXRlIGNoYW5uZWwuZXhlY0NhbGxiYWNrc1ttZXNzYWdlLmlkXTtcclxuICAgIH1cclxuXHJcbiAgICB0aGlzLmhhbmRsZVByb3BlcnR5VXBkYXRlID0gZnVuY3Rpb24obWVzc2FnZSlcclxuICAgIHtcclxuICAgICAgICBmb3IgKHZhciBpIGluIG1lc3NhZ2UuZGF0YSkge1xyXG4gICAgICAgICAgICB2YXIgZGF0YSA9IG1lc3NhZ2UuZGF0YVtpXTtcclxuICAgICAgICAgICAgdmFyIG9iamVjdCA9IGNoYW5uZWwub2JqZWN0c1tkYXRhLm9iamVjdF07XHJcbiAgICAgICAgICAgIGlmIChvYmplY3QpIHtcclxuICAgICAgICAgICAgICAgIG9iamVjdC5wcm9wZXJ0eVVwZGF0ZShkYXRhLnNpZ25hbHMsIGRhdGEucHJvcGVydGllcyk7XHJcbiAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oXCJVbmhhbmRsZWQgcHJvcGVydHkgdXBkYXRlOiBcIiArIGRhdGEub2JqZWN0ICsgXCI6OlwiICsgZGF0YS5zaWduYWwpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGNoYW5uZWwuZXhlYyh7dHlwZTogUVdlYkNoYW5uZWxNZXNzYWdlVHlwZXMuaWRsZX0pO1xyXG4gICAgfVxyXG5cclxuICAgIHRoaXMuZGVidWcgPSBmdW5jdGlvbihtZXNzYWdlKVxyXG4gICAge1xyXG4gICAgICAgIGNoYW5uZWwuc2VuZCh7dHlwZTogUVdlYkNoYW5uZWxNZXNzYWdlVHlwZXMuZGVidWcsIGRhdGE6IG1lc3NhZ2V9KTtcclxuICAgIH07XHJcblxyXG4gICAgY2hhbm5lbC5leGVjKHt0eXBlOiBRV2ViQ2hhbm5lbE1lc3NhZ2VUeXBlcy5pbml0fSwgZnVuY3Rpb24oZGF0YSkge1xyXG4gICAgICAgIGZvciAodmFyIG9iamVjdE5hbWUgaW4gZGF0YSkge1xyXG4gICAgICAgICAgICB2YXIgb2JqZWN0ID0gbmV3IFFPYmplY3Qob2JqZWN0TmFtZSwgZGF0YVtvYmplY3ROYW1lXSwgY2hhbm5lbCk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIC8vIG5vdyB1bndyYXAgcHJvcGVydGllcywgd2hpY2ggbWlnaHQgcmVmZXJlbmNlIG90aGVyIHJlZ2lzdGVyZWQgb2JqZWN0c1xyXG4gICAgICAgIGZvciAodmFyIG9iamVjdE5hbWUgaW4gY2hhbm5lbC5vYmplY3RzKSB7XHJcbiAgICAgICAgICAgIGNoYW5uZWwub2JqZWN0c1tvYmplY3ROYW1lXS51bndyYXBQcm9wZXJ0aWVzKCk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGlmIChpbml0Q2FsbGJhY2spIHtcclxuICAgICAgICAgICAgaW5pdENhbGxiYWNrKGNoYW5uZWwpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBjaGFubmVsLmV4ZWMoe3R5cGU6IFFXZWJDaGFubmVsTWVzc2FnZVR5cGVzLmlkbGV9KTtcclxuICAgIH0pO1xyXG59O1xyXG5cclxuZnVuY3Rpb24gUU9iamVjdChuYW1lLCBkYXRhLCB3ZWJDaGFubmVsKVxyXG57XHJcbiAgICB0aGlzLl9faWRfXyA9IG5hbWU7XHJcbiAgICB3ZWJDaGFubmVsLm9iamVjdHNbbmFtZV0gPSB0aGlzO1xyXG5cclxuICAgIC8vIExpc3Qgb2YgY2FsbGJhY2tzIHRoYXQgZ2V0IGludm9rZWQgdXBvbiBzaWduYWwgZW1pc3Npb25cclxuICAgIHRoaXMuX19vYmplY3RTaWduYWxzX18gPSB7fTtcclxuXHJcbiAgICAvLyBDYWNoZSBvZiBhbGwgcHJvcGVydGllcywgdXBkYXRlZCB3aGVuIGEgbm90aWZ5IHNpZ25hbCBpcyBlbWl0dGVkXHJcbiAgICB0aGlzLl9fcHJvcGVydHlDYWNoZV9fID0ge307XHJcblxyXG4gICAgdmFyIG9iamVjdCA9IHRoaXM7XHJcblxyXG4gICAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG5cclxuICAgIHRoaXMudW53cmFwUU9iamVjdCA9IGZ1bmN0aW9uKHJlc3BvbnNlKVxyXG4gICAge1xyXG4gICAgICAgIGlmIChyZXNwb25zZSBpbnN0YW5jZW9mIEFycmF5KSB7XHJcbiAgICAgICAgICAgIC8vIHN1cHBvcnQgbGlzdCBvZiBvYmplY3RzXHJcbiAgICAgICAgICAgIHZhciByZXQgPSBuZXcgQXJyYXkocmVzcG9uc2UubGVuZ3RoKTtcclxuICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCByZXNwb25zZS5sZW5ndGg7ICsraSkge1xyXG4gICAgICAgICAgICAgICAgcmV0W2ldID0gb2JqZWN0LnVud3JhcFFPYmplY3QocmVzcG9uc2VbaV0pO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIHJldHVybiByZXQ7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGlmICghcmVzcG9uc2VcclxuICAgICAgICAgICAgfHwgIXJlc3BvbnNlW1wiX19RT2JqZWN0Kl9fXCJdXHJcbiAgICAgICAgICAgIHx8IHJlc3BvbnNlLmlkID09PSB1bmRlZmluZWQpIHtcclxuICAgICAgICAgICAgcmV0dXJuIHJlc3BvbnNlO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdmFyIG9iamVjdElkID0gcmVzcG9uc2UuaWQ7XHJcbiAgICAgICAgaWYgKHdlYkNoYW5uZWwub2JqZWN0c1tvYmplY3RJZF0pXHJcbiAgICAgICAgICAgIHJldHVybiB3ZWJDaGFubmVsLm9iamVjdHNbb2JqZWN0SWRdO1xyXG5cclxuICAgICAgICBpZiAoIXJlc3BvbnNlLmRhdGEpIHtcclxuICAgICAgICAgICAgY29uc29sZS5lcnJvcihcIkNhbm5vdCB1bndyYXAgdW5rbm93biBRT2JqZWN0IFwiICsgb2JqZWN0SWQgKyBcIiB3aXRob3V0IGRhdGEuXCIpO1xyXG4gICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICB2YXIgcU9iamVjdCA9IG5ldyBRT2JqZWN0KCBvYmplY3RJZCwgcmVzcG9uc2UuZGF0YSwgd2ViQ2hhbm5lbCApO1xyXG4gICAgICAgIHFPYmplY3QuZGVzdHJveWVkLmNvbm5lY3QoZnVuY3Rpb24oKSB7XHJcbiAgICAgICAgICAgIGlmICh3ZWJDaGFubmVsLm9iamVjdHNbb2JqZWN0SWRdID09PSBxT2JqZWN0KSB7XHJcbiAgICAgICAgICAgICAgICBkZWxldGUgd2ViQ2hhbm5lbC5vYmplY3RzW29iamVjdElkXTtcclxuICAgICAgICAgICAgICAgIC8vIHJlc2V0IHRoZSBub3cgZGVsZXRlZCBRT2JqZWN0IHRvIGFuIGVtcHR5IHt9IG9iamVjdFxyXG4gICAgICAgICAgICAgICAgLy8ganVzdCBhc3NpZ25pbmcge30gdGhvdWdoIHdvdWxkIG5vdCBoYXZlIHRoZSBkZXNpcmVkIGVmZmVjdCwgYnV0IHRoZVxyXG4gICAgICAgICAgICAgICAgLy8gYmVsb3cgYWxzbyBlbnN1cmVzIGFsbCBleHRlcm5hbCByZWZlcmVuY2VzIHdpbGwgc2VlIHRoZSBlbXB0eSBtYXBcclxuICAgICAgICAgICAgICAgIC8vIE5PVEU6IHRoaXMgZGV0b3VyIGlzIG5lY2Vzc2FyeSB0byB3b3JrYXJvdW5kIFFUQlVHLTQwMDIxXHJcbiAgICAgICAgICAgICAgICB2YXIgcHJvcGVydHlOYW1lcyA9IFtdO1xyXG4gICAgICAgICAgICAgICAgZm9yICh2YXIgcHJvcGVydHlOYW1lIGluIHFPYmplY3QpIHtcclxuICAgICAgICAgICAgICAgICAgICBwcm9wZXJ0eU5hbWVzLnB1c2gocHJvcGVydHlOYW1lKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIGZvciAodmFyIGlkeCBpbiBwcm9wZXJ0eU5hbWVzKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgZGVsZXRlIHFPYmplY3RbcHJvcGVydHlOYW1lc1tpZHhdXTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH0pO1xyXG4gICAgICAgIC8vIGhlcmUgd2UgYXJlIGFscmVhZHkgaW5pdGlhbGl6ZWQsIGFuZCB0aHVzIG11c3QgZGlyZWN0bHkgdW53cmFwIHRoZSBwcm9wZXJ0aWVzXHJcbiAgICAgICAgcU9iamVjdC51bndyYXBQcm9wZXJ0aWVzKCk7XHJcbiAgICAgICAgcmV0dXJuIHFPYmplY3Q7XHJcbiAgICB9XHJcblxyXG4gICAgdGhpcy51bndyYXBQcm9wZXJ0aWVzID0gZnVuY3Rpb24oKVxyXG4gICAge1xyXG4gICAgICAgIGZvciAodmFyIHByb3BlcnR5SWR4IGluIG9iamVjdC5fX3Byb3BlcnR5Q2FjaGVfXykge1xyXG4gICAgICAgICAgICBvYmplY3QuX19wcm9wZXJ0eUNhY2hlX19bcHJvcGVydHlJZHhdID0gb2JqZWN0LnVud3JhcFFPYmplY3Qob2JqZWN0Ll9fcHJvcGVydHlDYWNoZV9fW3Byb3BlcnR5SWR4XSk7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIGZ1bmN0aW9uIGFkZFNpZ25hbChzaWduYWxEYXRhLCBpc1Byb3BlcnR5Tm90aWZ5U2lnbmFsKVxyXG4gICAge1xyXG4gICAgICAgIHZhciBzaWduYWxOYW1lID0gc2lnbmFsRGF0YVswXTtcclxuICAgICAgICB2YXIgc2lnbmFsSW5kZXggPSBzaWduYWxEYXRhWzFdO1xyXG4gICAgICAgIG9iamVjdFtzaWduYWxOYW1lXSA9IHtcclxuICAgICAgICAgICAgY29ubmVjdDogZnVuY3Rpb24oY2FsbGJhY2spIHtcclxuICAgICAgICAgICAgICAgIGlmICh0eXBlb2YoY2FsbGJhY2spICE9PSBcImZ1bmN0aW9uXCIpIHtcclxuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiQmFkIGNhbGxiYWNrIGdpdmVuIHRvIGNvbm5lY3QgdG8gc2lnbmFsIFwiICsgc2lnbmFsTmFtZSk7XHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAgIG9iamVjdC5fX29iamVjdFNpZ25hbHNfX1tzaWduYWxJbmRleF0gPSBvYmplY3QuX19vYmplY3RTaWduYWxzX19bc2lnbmFsSW5kZXhdIHx8IFtdO1xyXG4gICAgICAgICAgICAgICAgb2JqZWN0Ll9fb2JqZWN0U2lnbmFsc19fW3NpZ25hbEluZGV4XS5wdXNoKGNhbGxiYWNrKTtcclxuXHJcbiAgICAgICAgICAgICAgICBpZiAoIWlzUHJvcGVydHlOb3RpZnlTaWduYWwgJiYgc2lnbmFsTmFtZSAhPT0gXCJkZXN0cm95ZWRcIikge1xyXG4gICAgICAgICAgICAgICAgICAgIC8vIG9ubHkgcmVxdWlyZWQgZm9yIFwicHVyZVwiIHNpZ25hbHMsIGhhbmRsZWQgc2VwYXJhdGVseSBmb3IgcHJvcGVydGllcyBpbiBwcm9wZXJ0eVVwZGF0ZVxyXG4gICAgICAgICAgICAgICAgICAgIC8vIGFsc28gbm90ZSB0aGF0IHdlIGFsd2F5cyBnZXQgbm90aWZpZWQgYWJvdXQgdGhlIGRlc3Ryb3llZCBzaWduYWxcclxuICAgICAgICAgICAgICAgICAgICB3ZWJDaGFubmVsLmV4ZWMoe1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiBRV2ViQ2hhbm5lbE1lc3NhZ2VUeXBlcy5jb25uZWN0VG9TaWduYWwsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIG9iamVjdDogb2JqZWN0Ll9faWRfXyxcclxuICAgICAgICAgICAgICAgICAgICAgICAgc2lnbmFsOiBzaWduYWxJbmRleFxyXG4gICAgICAgICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9LFxyXG4gICAgICAgICAgICBkaXNjb25uZWN0OiBmdW5jdGlvbihjYWxsYmFjaykge1xyXG4gICAgICAgICAgICAgICAgaWYgKHR5cGVvZihjYWxsYmFjaykgIT09IFwiZnVuY3Rpb25cIikge1xyXG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJCYWQgY2FsbGJhY2sgZ2l2ZW4gdG8gZGlzY29ubmVjdCBmcm9tIHNpZ25hbCBcIiArIHNpZ25hbE5hbWUpO1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIG9iamVjdC5fX29iamVjdFNpZ25hbHNfX1tzaWduYWxJbmRleF0gPSBvYmplY3QuX19vYmplY3RTaWduYWxzX19bc2lnbmFsSW5kZXhdIHx8IFtdO1xyXG4gICAgICAgICAgICAgICAgdmFyIGlkeCA9IG9iamVjdC5fX29iamVjdFNpZ25hbHNfX1tzaWduYWxJbmRleF0uaW5kZXhPZihjYWxsYmFjayk7XHJcbiAgICAgICAgICAgICAgICBpZiAoaWR4ID09PSAtMSkge1xyXG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJDYW5ub3QgZmluZCBjb25uZWN0aW9uIG9mIHNpZ25hbCBcIiArIHNpZ25hbE5hbWUgKyBcIiB0byBcIiArIGNhbGxiYWNrLm5hbWUpO1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIG9iamVjdC5fX29iamVjdFNpZ25hbHNfX1tzaWduYWxJbmRleF0uc3BsaWNlKGlkeCwgMSk7XHJcbiAgICAgICAgICAgICAgICBpZiAoIWlzUHJvcGVydHlOb3RpZnlTaWduYWwgJiYgb2JqZWN0Ll9fb2JqZWN0U2lnbmFsc19fW3NpZ25hbEluZGV4XS5sZW5ndGggPT09IDApIHtcclxuICAgICAgICAgICAgICAgICAgICAvLyBvbmx5IHJlcXVpcmVkIGZvciBcInB1cmVcIiBzaWduYWxzLCBoYW5kbGVkIHNlcGFyYXRlbHkgZm9yIHByb3BlcnRpZXMgaW4gcHJvcGVydHlVcGRhdGVcclxuICAgICAgICAgICAgICAgICAgICB3ZWJDaGFubmVsLmV4ZWMoe1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiBRV2ViQ2hhbm5lbE1lc3NhZ2VUeXBlcy5kaXNjb25uZWN0RnJvbVNpZ25hbCxcclxuICAgICAgICAgICAgICAgICAgICAgICAgb2JqZWN0OiBvYmplY3QuX19pZF9fLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICBzaWduYWw6IHNpZ25hbEluZGV4XHJcbiAgICAgICAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9O1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogSW52b2tlcyBhbGwgY2FsbGJhY2tzIGZvciB0aGUgZ2l2ZW4gc2lnbmFsbmFtZS4gQWxzbyB3b3JrcyBmb3IgcHJvcGVydHkgbm90aWZ5IGNhbGxiYWNrcy5cclxuICAgICAqL1xyXG4gICAgZnVuY3Rpb24gaW52b2tlU2lnbmFsQ2FsbGJhY2tzKHNpZ25hbE5hbWUsIHNpZ25hbEFyZ3MpXHJcbiAgICB7XHJcbiAgICAgICAgdmFyIGNvbm5lY3Rpb25zID0gb2JqZWN0Ll9fb2JqZWN0U2lnbmFsc19fW3NpZ25hbE5hbWVdO1xyXG4gICAgICAgIGlmIChjb25uZWN0aW9ucykge1xyXG4gICAgICAgICAgICBjb25uZWN0aW9ucy5mb3JFYWNoKGZ1bmN0aW9uKGNhbGxiYWNrKSB7XHJcbiAgICAgICAgICAgICAgICBjYWxsYmFjay5hcHBseShjYWxsYmFjaywgc2lnbmFsQXJncyk7XHJcbiAgICAgICAgICAgIH0pO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICB0aGlzLnByb3BlcnR5VXBkYXRlID0gZnVuY3Rpb24oc2lnbmFscywgcHJvcGVydHlNYXApXHJcbiAgICB7XHJcbiAgICAgICAgLy8gdXBkYXRlIHByb3BlcnR5IGNhY2hlXHJcbiAgICAgICAgZm9yICh2YXIgcHJvcGVydHlJbmRleCBpbiBwcm9wZXJ0eU1hcCkge1xyXG4gICAgICAgICAgICB2YXIgcHJvcGVydHlWYWx1ZSA9IHByb3BlcnR5TWFwW3Byb3BlcnR5SW5kZXhdO1xyXG4gICAgICAgICAgICBvYmplY3QuX19wcm9wZXJ0eUNhY2hlX19bcHJvcGVydHlJbmRleF0gPSBwcm9wZXJ0eVZhbHVlO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgZm9yICh2YXIgc2lnbmFsTmFtZSBpbiBzaWduYWxzKSB7XHJcbiAgICAgICAgICAgIC8vIEludm9rZSBhbGwgY2FsbGJhY2tzLCBhcyBzaWduYWxFbWl0dGVkKCkgZG9lcyBub3QuIFRoaXMgZW5zdXJlcyB0aGVcclxuICAgICAgICAgICAgLy8gcHJvcGVydHkgY2FjaGUgaXMgdXBkYXRlZCBiZWZvcmUgdGhlIGNhbGxiYWNrcyBhcmUgaW52b2tlZC5cclxuICAgICAgICAgICAgaW52b2tlU2lnbmFsQ2FsbGJhY2tzKHNpZ25hbE5hbWUsIHNpZ25hbHNbc2lnbmFsTmFtZV0pO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICB0aGlzLnNpZ25hbEVtaXR0ZWQgPSBmdW5jdGlvbihzaWduYWxOYW1lLCBzaWduYWxBcmdzKVxyXG4gICAge1xyXG4gICAgICAgIGludm9rZVNpZ25hbENhbGxiYWNrcyhzaWduYWxOYW1lLCB0aGlzLnVud3JhcFFPYmplY3Qoc2lnbmFsQXJncykpO1xyXG4gICAgfVxyXG5cclxuICAgIGZ1bmN0aW9uIGFkZE1ldGhvZChtZXRob2REYXRhKVxyXG4gICAge1xyXG4gICAgICAgIHZhciBtZXRob2ROYW1lID0gbWV0aG9kRGF0YVswXTtcclxuICAgICAgICB2YXIgbWV0aG9kSWR4ID0gbWV0aG9kRGF0YVsxXTtcclxuICAgICAgICBvYmplY3RbbWV0aG9kTmFtZV0gPSBhc3luYyBmdW5jdGlvbigpIHtcclxuICAgICAgICAgICAgdmFyIGFyZ3MgPSBbXTtcclxuICAgICAgICAgICAgdmFyIGNhbGxiYWNrO1xyXG4gICAgICAgICAgICB2YXIgcmVzdWx0O1xyXG4gICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGFyZ3VtZW50cy5sZW5ndGg7ICsraSkge1xyXG4gICAgICAgICAgICAgICAgdmFyIGFyZ3VtZW50ID0gYXJndW1lbnRzW2ldO1xyXG4gICAgICAgICAgICAgICAgaWYgKHR5cGVvZiBhcmd1bWVudCA9PT0gXCJmdW5jdGlvblwiKVxyXG4gICAgICAgICAgICAgICAgICAgIGNhbGxiYWNrID0gYXJndW1lbnQ7XHJcbiAgICAgICAgICAgICAgICBlbHNlIGlmIChhcmd1bWVudCBpbnN0YW5jZW9mIFFPYmplY3QgJiYgd2ViQ2hhbm5lbC5vYmplY3RzW2FyZ3VtZW50Ll9faWRfX10gIT09IHVuZGVmaW5lZClcclxuICAgICAgICAgICAgICAgICAgICBhcmdzLnB1c2goe1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBcImlkXCI6IGFyZ3VtZW50Ll9faWRfX1xyXG4gICAgICAgICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICAgICAgZWxzZVxyXG4gICAgICAgICAgICAgICAgICAgIGFyZ3MucHVzaChhcmd1bWVudCk7XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNvbHZlKT0+e1xyXG4gICAgICAgICAgICAgICAgd2ViQ2hhbm5lbC5leGVjKHtcclxuICAgICAgICAgICAgICAgICAgICBcInR5cGVcIjogUVdlYkNoYW5uZWxNZXNzYWdlVHlwZXMuaW52b2tlTWV0aG9kLFxyXG4gICAgICAgICAgICAgICAgICAgIFwib2JqZWN0XCI6IG9iamVjdC5fX2lkX18sXHJcbiAgICAgICAgICAgICAgICAgICAgXCJtZXRob2RcIjogbWV0aG9kSWR4LFxyXG4gICAgICAgICAgICAgICAgICAgIFwiYXJnc1wiOiBhcmdzXHJcbiAgICAgICAgICAgICAgICB9LCBmdW5jdGlvbihyZXNwb25zZSkge1xyXG4gICAgICAgICAgICAgICAgICAgIGlmIChyZXNwb25zZSAhPT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCA9IG9iamVjdC51bndyYXBRT2JqZWN0KHJlc3BvbnNlKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGNhbGxiYWNrKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAoY2FsbGJhY2spKHJlc3VsdCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZSgpO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgcmV0dXJuIHJlc3VsdDtcclxuICAgICAgICB9O1xyXG4gICAgfVxyXG5cclxuICAgIGZ1bmN0aW9uIGJpbmRHZXR0ZXJTZXR0ZXIocHJvcGVydHlJbmZvKVxyXG4gICAge1xyXG4gICAgICAgIHZhciBwcm9wZXJ0eUluZGV4ID0gcHJvcGVydHlJbmZvWzBdO1xyXG4gICAgICAgIHZhciBwcm9wZXJ0eU5hbWUgPSBwcm9wZXJ0eUluZm9bMV07XHJcbiAgICAgICAgdmFyIG5vdGlmeVNpZ25hbERhdGEgPSBwcm9wZXJ0eUluZm9bMl07XHJcbiAgICAgICAgLy8gaW5pdGlhbGl6ZSBwcm9wZXJ0eSBjYWNoZSB3aXRoIGN1cnJlbnQgdmFsdWVcclxuICAgICAgICAvLyBOT1RFOiBpZiB0aGlzIGlzIGFuIG9iamVjdCwgaXQgaXMgbm90IGRpcmVjdGx5IHVud3JhcHBlZCBhcyBpdCBtaWdodFxyXG4gICAgICAgIC8vIHJlZmVyZW5jZSBvdGhlciBRT2JqZWN0IHRoYXQgd2UgZG8gbm90IGtub3cgeWV0XHJcbiAgICAgICAgb2JqZWN0Ll9fcHJvcGVydHlDYWNoZV9fW3Byb3BlcnR5SW5kZXhdID0gcHJvcGVydHlJbmZvWzNdO1xyXG5cclxuICAgICAgICBpZiAobm90aWZ5U2lnbmFsRGF0YSkge1xyXG4gICAgICAgICAgICBpZiAobm90aWZ5U2lnbmFsRGF0YVswXSA9PT0gMSkge1xyXG4gICAgICAgICAgICAgICAgLy8gc2lnbmFsIG5hbWUgaXMgb3B0aW1pemVkIGF3YXksIHJlY29uc3RydWN0IHRoZSBhY3R1YWwgbmFtZVxyXG4gICAgICAgICAgICAgICAgbm90aWZ5U2lnbmFsRGF0YVswXSA9IHByb3BlcnR5TmFtZSArIFwiQ2hhbmdlZFwiO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIGFkZFNpZ25hbChub3RpZnlTaWduYWxEYXRhLCB0cnVlKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShvYmplY3QsIHByb3BlcnR5TmFtZSwge1xyXG4gICAgICAgICAgICBjb25maWd1cmFibGU6IHRydWUsXHJcbiAgICAgICAgICAgIGdldDogZnVuY3Rpb24gKCkge1xyXG4gICAgICAgICAgICAgICAgdmFyIHByb3BlcnR5VmFsdWUgPSBvYmplY3QuX19wcm9wZXJ0eUNhY2hlX19bcHJvcGVydHlJbmRleF07XHJcbiAgICAgICAgICAgICAgICBpZiAocHJvcGVydHlWYWx1ZSA9PT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgLy8gVGhpcyBzaG91bGRuJ3QgaGFwcGVuXHJcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS53YXJuKFwiVW5kZWZpbmVkIHZhbHVlIGluIHByb3BlcnR5IGNhY2hlIGZvciBwcm9wZXJ0eSBcXFwiXCIgKyBwcm9wZXJ0eU5hbWUgKyBcIlxcXCIgaW4gb2JqZWN0IFwiICsgb2JqZWN0Ll9faWRfXyk7XHJcbiAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgcmV0dXJuIHByb3BlcnR5VmFsdWU7XHJcbiAgICAgICAgICAgIH0sXHJcbiAgICAgICAgICAgIHNldDogZnVuY3Rpb24odmFsdWUpIHtcclxuICAgICAgICAgICAgICAgIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS53YXJuKFwiUHJvcGVydHkgc2V0dGVyIGZvciBcIiArIHByb3BlcnR5TmFtZSArIFwiIGNhbGxlZCB3aXRoIHVuZGVmaW5lZCB2YWx1ZSFcIik7XHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgb2JqZWN0Ll9fcHJvcGVydHlDYWNoZV9fW3Byb3BlcnR5SW5kZXhdID0gdmFsdWU7XHJcbiAgICAgICAgICAgICAgICB2YXIgdmFsdWVUb1NlbmQgPSB2YWx1ZTtcclxuICAgICAgICAgICAgICAgIGlmICh2YWx1ZVRvU2VuZCBpbnN0YW5jZW9mIFFPYmplY3QgJiYgd2ViQ2hhbm5lbC5vYmplY3RzW3ZhbHVlVG9TZW5kLl9faWRfX10gIT09IHVuZGVmaW5lZClcclxuICAgICAgICAgICAgICAgICAgICB2YWx1ZVRvU2VuZCA9IHsgXCJpZFwiOiB2YWx1ZVRvU2VuZC5fX2lkX18gfTtcclxuICAgICAgICAgICAgICAgIHdlYkNoYW5uZWwuZXhlYyh7XHJcbiAgICAgICAgICAgICAgICAgICAgXCJ0eXBlXCI6IFFXZWJDaGFubmVsTWVzc2FnZVR5cGVzLnNldFByb3BlcnR5LFxyXG4gICAgICAgICAgICAgICAgICAgIFwib2JqZWN0XCI6IG9iamVjdC5fX2lkX18sXHJcbiAgICAgICAgICAgICAgICAgICAgXCJwcm9wZXJ0eVwiOiBwcm9wZXJ0eUluZGV4LFxyXG4gICAgICAgICAgICAgICAgICAgIFwidmFsdWVcIjogdmFsdWVUb1NlbmRcclxuICAgICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG5cclxuICAgIGRhdGEubWV0aG9kcy5mb3JFYWNoKGFkZE1ldGhvZCk7XHJcblxyXG4gICAgZGF0YS5wcm9wZXJ0aWVzLmZvckVhY2goYmluZEdldHRlclNldHRlcik7XHJcblxyXG4gICAgZGF0YS5zaWduYWxzLmZvckVhY2goZnVuY3Rpb24oc2lnbmFsKSB7IGFkZFNpZ25hbChzaWduYWwsIGZhbHNlKTsgfSk7XHJcblxyXG4gICAgZm9yICh2YXIgbmFtZSBpbiBkYXRhLmVudW1zKSB7XHJcbiAgICAgICAgb2JqZWN0W25hbWVdID0gZGF0YS5lbnVtc1tuYW1lXTtcclxuICAgIH1cclxufVxyXG5cclxuLy9yZXF1aXJlZCBmb3IgdXNlIHdpdGggbm9kZWpzXHJcbmlmICh0eXBlb2YgbW9kdWxlID09PSAnb2JqZWN0Jykge1xyXG4gICAgbW9kdWxlLmV4cG9ydHMgPSB7XHJcbiAgICAgICAgUVdlYkNoYW5uZWw6IFFXZWJDaGFubmVsXHJcbiAgICB9O1xyXG59XHJcbiJdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///./src/qwebchannel.js\n"); 144 | 145 | /***/ }), 146 | 147 | /***/ "./src/webchannelCore.ts-exposed": 148 | /*!***************************************!*\ 149 | !*** ./src/webchannelCore.ts-exposed ***! 150 | \***************************************/ 151 | /*! no static exports found */ 152 | /***/ (function(module, exports, __webpack_require__) { 153 | 154 | eval("/* WEBPACK VAR INJECTION */(function(global) {module.exports = global[\"Library\"] = __webpack_require__(/*! -!./node_modules/ts-loader!./webchannelCore.ts */ \"./node_modules/ts-loader/index.js!./src/webchannelCore.ts\");\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../node_modules/webpack/buildin/global.js */ \"./node_modules/webpack/buildin/global.js\")))//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vLi9zcmMvd2ViY2hhbm5lbENvcmUudHMtZXhwb3NlZD9kOWNhIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLG1GQUFxQyxtQkFBTyxDQUFDLGlIQUE4RyxFIiwiZmlsZSI6Ii4vc3JjL3dlYmNoYW5uZWxDb3JlLnRzLWV4cG9zZWQuanMiLCJzb3VyY2VzQ29udGVudCI6WyJtb2R1bGUuZXhwb3J0cyA9IGdsb2JhbFtcIkxpYnJhcnlcIl0gPSByZXF1aXJlKFwiLSFFOlxcXFxEZXZcXFxcTGFic1xcXFxRdFxcXFxNaW5lXFxcXFF0V2ViXFxcXFdlYkNoYW5uZWxDYXJcXFxcV2ViXFxcXG5vZGVfbW9kdWxlc1xcXFx0cy1sb2FkZXJcXFxcaW5kZXguanMhLlxcXFx3ZWJjaGFubmVsQ29yZS50c1wiKTsiXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./src/webchannelCore.ts-exposed\n"); 155 | 156 | /***/ }) 157 | 158 | /******/ }); -------------------------------------------------------------------------------- /WebChannelCar/Web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "WebSDK", 3 | "version": "1.0.0", 4 | "description": "editor sdk for web", 5 | "main": "./src/main.ts", 6 | "author": "LubanSoft", 7 | "license": "MIT", 8 | "scripts": { 9 | "build": "webpack" 10 | }, 11 | "devDependencies": { 12 | "expose-loader": "^0.7.5", 13 | "ts-loader": "^6.2.1", 14 | "typescript": "^3.7.4", 15 | "webpack": "^4.41.6", 16 | "webpack-cli": "^3.3.11" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /WebChannelCar/Web/page/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 | 19 | 20 |
21 |
22 | 23 | 24 |
25 |
26 | 27 | 30 | 31 | -------------------------------------------------------------------------------- /WebChannelCar/Web/src/CarObject.ts: -------------------------------------------------------------------------------- 1 | declare class Car { 2 | get speed():number; 3 | set speed(value:number); 4 | 5 | get angle():number; 6 | set angle(vlaue:number); 7 | 8 | public accelerate():void; 9 | public decelerate():void; 10 | public turnLeft():void; 11 | public turnRight():void; 12 | } -------------------------------------------------------------------------------- /WebChannelCar/Web/src/main.ts: -------------------------------------------------------------------------------- 1 | import WebChannelCore from "./webchannelCore"; 2 | window.onload = () => { 3 | WebChannelCore.initialize(onInit, onUninit); 4 | } 5 | function onInit() { 6 | (window as any).document.getElementById("img").src = "../img/connected.svg"; 7 | 8 | let car = WebChannelCore.SDK.car; 9 | 10 | let upBtn = (window as any).document.getElementById("up"); 11 | let downBtn = (window as any).document.getElementById("down"); 12 | let leftBtn = (window as any).document.getElementById("left"); 13 | let rightBtn = (window as any).document.getElementById("right"); 14 | 15 | let speedLabel = (window as any).document.getElementById("speed"); 16 | let angleLabel = (window as any).document.getElementById("angle"); 17 | 18 | upBtn.onclick = () => { 19 | car.accelerate(); 20 | } 21 | downBtn.onclick = () => { 22 | car.decelerate(); 23 | } 24 | leftBtn.onclick = () => { 25 | car.turnLeft(); 26 | } 27 | rightBtn.onclick = () => { 28 | car.turnRight(); 29 | } 30 | 31 | car.speedChanged.connect(onSpeedChanged); 32 | car.angleChanged.connect(onAngleChanged); 33 | } 34 | function onUninit() { 35 | (window as any).document.getElementById("img").src = "../img/disconnected.svg"; 36 | } 37 | 38 | async function onSpeedChanged() { 39 | let speedLabel = (window as any).document.getElementById("speed"); 40 | let car = WebChannelCore.SDK.car; 41 | 42 | speedLabel.textContent = await car.getSpeed(); 43 | } 44 | async function onAngleChanged() { 45 | let angleLabel = (window as any).document.getElementById("angle"); 46 | let car = WebChannelCore.SDK.car; 47 | 48 | angleLabel.textContent = await car.getAngle(); 49 | } 50 | -------------------------------------------------------------------------------- /WebChannelCar/Web/src/qwebchannel.d.ts: -------------------------------------------------------------------------------- 1 | export declare function QWebChannel(transport: any, initCallback: Function): void; -------------------------------------------------------------------------------- /WebChannelCar/Web/src/qwebchannel.js: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff 5 | ** Contact: https://www.qt.io/licensing/ 6 | ** 7 | ** This file is part of the QtWebChannel module of the Qt Toolkit. 8 | ** 9 | ** $QT_BEGIN_LICENSE:LGPL$ 10 | ** Commercial License Usage 11 | ** Licensees holding valid commercial Qt licenses may use this file in 12 | ** accordance with the commercial license agreement provided with the 13 | ** Software or, alternatively, in accordance with the terms contained in 14 | ** a written agreement between you and The Qt Company. For licensing terms 15 | ** and conditions see https://www.qt.io/terms-conditions. For further 16 | ** information use the contact form at https://www.qt.io/contact-us. 17 | ** 18 | ** GNU Lesser General Public License Usage 19 | ** Alternatively, this file may be used under the terms of the GNU Lesser 20 | ** General Public License version 3 as published by the Free Software 21 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the 22 | ** packaging of this file. Please review the following information to 23 | ** ensure the GNU Lesser General Public License version 3 requirements 24 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. 25 | ** 26 | ** GNU General Public License Usage 27 | ** Alternatively, this file may be used under the terms of the GNU 28 | ** General Public License version 2.0 or (at your option) the GNU General 29 | ** Public license version 3 or any later version approved by the KDE Free 30 | ** Qt Foundation. The licenses are as published by the Free Software 31 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 32 | ** included in the packaging of this file. Please review the following 33 | ** information to ensure the GNU General Public License requirements will 34 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and 35 | ** https://www.gnu.org/licenses/gpl-3.0.html. 36 | ** 37 | ** $QT_END_LICENSE$ 38 | ** 39 | ****************************************************************************/ 40 | 41 | "use strict"; 42 | 43 | var QWebChannelMessageTypes = { 44 | signal: 1, 45 | propertyUpdate: 2, 46 | init: 3, 47 | idle: 4, 48 | debug: 5, 49 | invokeMethod: 6, 50 | connectToSignal: 7, 51 | disconnectFromSignal: 8, 52 | setProperty: 9, 53 | response: 10, 54 | }; 55 | 56 | var QWebChannel = function(transport, initCallback) 57 | { 58 | if (typeof transport !== "object" || typeof transport.send !== "function") { 59 | console.error("The QWebChannel expects a transport object with a send function and onmessage callback property." + 60 | " Given is: transport: " + typeof(transport) + ", transport.send: " + typeof(transport.send)); 61 | return; 62 | } 63 | 64 | var channel = this; 65 | this.transport = transport; 66 | 67 | this.send = function(data) 68 | { 69 | if (typeof(data) !== "string") { 70 | data = JSON.stringify(data); 71 | } 72 | channel.transport.send(data); 73 | } 74 | 75 | this.transport.onmessage = function(message) 76 | { 77 | var data = message.data; 78 | if (typeof data === "string") { 79 | data = JSON.parse(data); 80 | } 81 | switch (data.type) { 82 | case QWebChannelMessageTypes.signal: 83 | channel.handleSignal(data); 84 | break; 85 | case QWebChannelMessageTypes.response: 86 | channel.handleResponse(data); 87 | break; 88 | case QWebChannelMessageTypes.propertyUpdate: 89 | channel.handlePropertyUpdate(data); 90 | break; 91 | default: 92 | console.error("invalid message received:", message.data); 93 | break; 94 | } 95 | } 96 | 97 | this.execCallbacks = {}; 98 | this.execId = 0; 99 | this.exec = function(data, callback) 100 | { 101 | if (!callback) { 102 | // if no callback is given, send directly 103 | channel.send(data); 104 | return; 105 | } 106 | if (channel.execId === Number.MAX_VALUE) { 107 | // wrap 108 | channel.execId = Number.MIN_VALUE; 109 | } 110 | if (data.hasOwnProperty("id")) { 111 | console.error("Cannot exec message with property id: " + JSON.stringify(data)); 112 | return; 113 | } 114 | data.id = channel.execId++; 115 | channel.execCallbacks[data.id] = callback; 116 | channel.send(data); 117 | }; 118 | 119 | this.objects = {}; 120 | 121 | this.handleSignal = function(message) 122 | { 123 | var object = channel.objects[message.object]; 124 | if (object) { 125 | object.signalEmitted(message.signal, message.args); 126 | } else { 127 | console.warn("Unhandled signal: " + message.object + "::" + message.signal); 128 | } 129 | } 130 | 131 | this.handleResponse = function(message) 132 | { 133 | if (!message.hasOwnProperty("id")) { 134 | console.error("Invalid response message received: ", JSON.stringify(message)); 135 | return; 136 | } 137 | channel.execCallbacks[message.id](message.data); 138 | delete channel.execCallbacks[message.id]; 139 | } 140 | 141 | this.handlePropertyUpdate = function(message) 142 | { 143 | for (var i in message.data) { 144 | var data = message.data[i]; 145 | var object = channel.objects[data.object]; 146 | if (object) { 147 | object.propertyUpdate(data.signals, data.properties); 148 | } else { 149 | console.warn("Unhandled property update: " + data.object + "::" + data.signal); 150 | } 151 | } 152 | channel.exec({type: QWebChannelMessageTypes.idle}); 153 | } 154 | 155 | this.debug = function(message) 156 | { 157 | channel.send({type: QWebChannelMessageTypes.debug, data: message}); 158 | }; 159 | 160 | channel.exec({type: QWebChannelMessageTypes.init}, function(data) { 161 | for (var objectName in data) { 162 | var object = new QObject(objectName, data[objectName], channel); 163 | } 164 | // now unwrap properties, which might reference other registered objects 165 | for (var objectName in channel.objects) { 166 | channel.objects[objectName].unwrapProperties(); 167 | } 168 | if (initCallback) { 169 | initCallback(channel); 170 | } 171 | channel.exec({type: QWebChannelMessageTypes.idle}); 172 | }); 173 | }; 174 | 175 | function QObject(name, data, webChannel) 176 | { 177 | this.__id__ = name; 178 | webChannel.objects[name] = this; 179 | 180 | // List of callbacks that get invoked upon signal emission 181 | this.__objectSignals__ = {}; 182 | 183 | // Cache of all properties, updated when a notify signal is emitted 184 | this.__propertyCache__ = {}; 185 | 186 | var object = this; 187 | 188 | // ---------------------------------------------------------------------- 189 | 190 | this.unwrapQObject = function(response) 191 | { 192 | if (response instanceof Array) { 193 | // support list of objects 194 | var ret = new Array(response.length); 195 | for (var i = 0; i < response.length; ++i) { 196 | ret[i] = object.unwrapQObject(response[i]); 197 | } 198 | return ret; 199 | } 200 | if (!response 201 | || !response["__QObject*__"] 202 | || response.id === undefined) { 203 | return response; 204 | } 205 | 206 | var objectId = response.id; 207 | if (webChannel.objects[objectId]) 208 | return webChannel.objects[objectId]; 209 | 210 | if (!response.data) { 211 | console.error("Cannot unwrap unknown QObject " + objectId + " without data."); 212 | return; 213 | } 214 | 215 | var qObject = new QObject( objectId, response.data, webChannel ); 216 | qObject.destroyed.connect(function() { 217 | if (webChannel.objects[objectId] === qObject) { 218 | delete webChannel.objects[objectId]; 219 | // reset the now deleted QObject to an empty {} object 220 | // just assigning {} though would not have the desired effect, but the 221 | // below also ensures all external references will see the empty map 222 | // NOTE: this detour is necessary to workaround QTBUG-40021 223 | var propertyNames = []; 224 | for (var propertyName in qObject) { 225 | propertyNames.push(propertyName); 226 | } 227 | for (var idx in propertyNames) { 228 | delete qObject[propertyNames[idx]]; 229 | } 230 | } 231 | }); 232 | // here we are already initialized, and thus must directly unwrap the properties 233 | qObject.unwrapProperties(); 234 | return qObject; 235 | } 236 | 237 | this.unwrapProperties = function() 238 | { 239 | for (var propertyIdx in object.__propertyCache__) { 240 | object.__propertyCache__[propertyIdx] = object.unwrapQObject(object.__propertyCache__[propertyIdx]); 241 | } 242 | } 243 | 244 | function addSignal(signalData, isPropertyNotifySignal) 245 | { 246 | var signalName = signalData[0]; 247 | var signalIndex = signalData[1]; 248 | object[signalName] = { 249 | connect: function(callback) { 250 | if (typeof(callback) !== "function") { 251 | console.error("Bad callback given to connect to signal " + signalName); 252 | return; 253 | } 254 | 255 | object.__objectSignals__[signalIndex] = object.__objectSignals__[signalIndex] || []; 256 | object.__objectSignals__[signalIndex].push(callback); 257 | 258 | if (!isPropertyNotifySignal && signalName !== "destroyed") { 259 | // only required for "pure" signals, handled separately for properties in propertyUpdate 260 | // also note that we always get notified about the destroyed signal 261 | webChannel.exec({ 262 | type: QWebChannelMessageTypes.connectToSignal, 263 | object: object.__id__, 264 | signal: signalIndex 265 | }); 266 | } 267 | }, 268 | disconnect: function(callback) { 269 | if (typeof(callback) !== "function") { 270 | console.error("Bad callback given to disconnect from signal " + signalName); 271 | return; 272 | } 273 | object.__objectSignals__[signalIndex] = object.__objectSignals__[signalIndex] || []; 274 | var idx = object.__objectSignals__[signalIndex].indexOf(callback); 275 | if (idx === -1) { 276 | console.error("Cannot find connection of signal " + signalName + " to " + callback.name); 277 | return; 278 | } 279 | object.__objectSignals__[signalIndex].splice(idx, 1); 280 | if (!isPropertyNotifySignal && object.__objectSignals__[signalIndex].length === 0) { 281 | // only required for "pure" signals, handled separately for properties in propertyUpdate 282 | webChannel.exec({ 283 | type: QWebChannelMessageTypes.disconnectFromSignal, 284 | object: object.__id__, 285 | signal: signalIndex 286 | }); 287 | } 288 | } 289 | }; 290 | } 291 | 292 | /** 293 | * Invokes all callbacks for the given signalname. Also works for property notify callbacks. 294 | */ 295 | function invokeSignalCallbacks(signalName, signalArgs) 296 | { 297 | var connections = object.__objectSignals__[signalName]; 298 | if (connections) { 299 | connections.forEach(function(callback) { 300 | callback.apply(callback, signalArgs); 301 | }); 302 | } 303 | } 304 | 305 | this.propertyUpdate = function(signals, propertyMap) 306 | { 307 | // update property cache 308 | for (var propertyIndex in propertyMap) { 309 | var propertyValue = propertyMap[propertyIndex]; 310 | object.__propertyCache__[propertyIndex] = propertyValue; 311 | } 312 | 313 | for (var signalName in signals) { 314 | // Invoke all callbacks, as signalEmitted() does not. This ensures the 315 | // property cache is updated before the callbacks are invoked. 316 | invokeSignalCallbacks(signalName, signals[signalName]); 317 | } 318 | } 319 | 320 | this.signalEmitted = function(signalName, signalArgs) 321 | { 322 | invokeSignalCallbacks(signalName, this.unwrapQObject(signalArgs)); 323 | } 324 | 325 | function addMethod(methodData) 326 | { 327 | var methodName = methodData[0]; 328 | var methodIdx = methodData[1]; 329 | object[methodName] = async function() { 330 | var args = []; 331 | var callback; 332 | var result; 333 | for (var i = 0; i < arguments.length; ++i) { 334 | var argument = arguments[i]; 335 | if (typeof argument === "function") 336 | callback = argument; 337 | else if (argument instanceof QObject && webChannel.objects[argument.__id__] !== undefined) 338 | args.push({ 339 | "id": argument.__id__ 340 | }); 341 | else 342 | args.push(argument); 343 | } 344 | 345 | await new Promise((resolve)=>{ 346 | webChannel.exec({ 347 | "type": QWebChannelMessageTypes.invokeMethod, 348 | "object": object.__id__, 349 | "method": methodIdx, 350 | "args": args 351 | }, function(response) { 352 | if (response !== undefined) { 353 | result = object.unwrapQObject(response); 354 | if (callback) { 355 | (callback)(result); 356 | } 357 | resolve(); 358 | } 359 | }); 360 | }); 361 | return result; 362 | }; 363 | } 364 | 365 | function bindGetterSetter(propertyInfo) 366 | { 367 | var propertyIndex = propertyInfo[0]; 368 | var propertyName = propertyInfo[1]; 369 | var notifySignalData = propertyInfo[2]; 370 | // initialize property cache with current value 371 | // NOTE: if this is an object, it is not directly unwrapped as it might 372 | // reference other QObject that we do not know yet 373 | object.__propertyCache__[propertyIndex] = propertyInfo[3]; 374 | 375 | if (notifySignalData) { 376 | if (notifySignalData[0] === 1) { 377 | // signal name is optimized away, reconstruct the actual name 378 | notifySignalData[0] = propertyName + "Changed"; 379 | } 380 | addSignal(notifySignalData, true); 381 | } 382 | 383 | Object.defineProperty(object, propertyName, { 384 | configurable: true, 385 | get: function () { 386 | var propertyValue = object.__propertyCache__[propertyIndex]; 387 | if (propertyValue === undefined) { 388 | // This shouldn't happen 389 | console.warn("Undefined value in property cache for property \"" + propertyName + "\" in object " + object.__id__); 390 | } 391 | 392 | return propertyValue; 393 | }, 394 | set: function(value) { 395 | if (value === undefined) { 396 | console.warn("Property setter for " + propertyName + " called with undefined value!"); 397 | return; 398 | } 399 | object.__propertyCache__[propertyIndex] = value; 400 | var valueToSend = value; 401 | if (valueToSend instanceof QObject && webChannel.objects[valueToSend.__id__] !== undefined) 402 | valueToSend = { "id": valueToSend.__id__ }; 403 | webChannel.exec({ 404 | "type": QWebChannelMessageTypes.setProperty, 405 | "object": object.__id__, 406 | "property": propertyIndex, 407 | "value": valueToSend 408 | }); 409 | } 410 | }); 411 | } 412 | 413 | // ---------------------------------------------------------------------- 414 | 415 | data.methods.forEach(addMethod); 416 | 417 | data.properties.forEach(bindGetterSetter); 418 | 419 | data.signals.forEach(function(signal) { addSignal(signal, false); }); 420 | 421 | for (var name in data.enums) { 422 | object[name] = data.enums[name]; 423 | } 424 | } 425 | 426 | //required for use with nodejs 427 | if (typeof module === 'object') { 428 | module.exports = { 429 | QWebChannel: QWebChannel 430 | }; 431 | } 432 | -------------------------------------------------------------------------------- /WebChannelCar/Web/src/webchannelCore.ts: -------------------------------------------------------------------------------- 1 | import { QWebChannel } from './qwebchannel'; 2 | 3 | type callback = () => void; 4 | export default class WebChannelCore { 5 | public static SDK: any = undefined; 6 | private static connectedCb: callback; 7 | private static disconnectedCb: callback; 8 | private static socket: WebSocket; 9 | public static initialize(connectedCb: callback = () => { }, disconnectedCb: callback = () => { }) { 10 | if (WebChannelCore.SDK != undefined) { 11 | return; 12 | } 13 | WebChannelCore.connectedCb = connectedCb; 14 | WebChannelCore.disconnectedCb = disconnectedCb; 15 | try { 16 | WebChannelCore.link( 17 | (socket) => { 18 | QWebChannel(socket, (channel: any) => { 19 | WebChannelCore.SDK = channel.objects; 20 | WebChannelCore.connectedCb(); 21 | }); 22 | } 23 | , (error) => { 24 | console.log("socket error", error); 25 | WebChannelCore.disconnectedCb(); 26 | }); 27 | } catch (error) { 28 | console.log("socket exception:", error); 29 | WebChannelCore.disconnectedCb(); 30 | WebChannelCore.SDK = undefined; 31 | } 32 | } 33 | 34 | private static link(resolve: (socket: WebSocket) => void, reject: (error: Event | CloseEvent) => void) { 35 | let baseUrl = "ws://localhost:12345"; 36 | if (window.location.search != "") { 37 | baseUrl = (/[?&]webChannelBaseUrl=([A-Za-z0-9\-:/\.]+)/.exec(window.location.search)![1]); 38 | } 39 | console.log("Connectiong to WebSocket server at: ", baseUrl); 40 | 41 | let socket = new WebSocket(baseUrl); 42 | WebChannelCore.socket = socket; 43 | socket.onopen = () => { 44 | resolve(socket); 45 | }; 46 | socket.onerror = (error) => { 47 | reject(error); 48 | }; 49 | socket.onclose = (error) => { 50 | reject(error); 51 | }; 52 | } 53 | } 54 | (window as any).SDK = WebChannelCore.SDK; 55 | -------------------------------------------------------------------------------- /WebChannelCar/Web/style/layout.css: -------------------------------------------------------------------------------- 1 | button#up { 2 | position: absolute; 3 | top: 10px; 4 | left: 100px; 5 | } 6 | button#left { 7 | position: absolute; 8 | top: 80px; 9 | left: 0px; 10 | } 11 | button#right { 12 | position: absolute; 13 | top: 80px; 14 | left: 200px; 15 | } 16 | button#down { 17 | position: absolute; 18 | top: 150px; 19 | left: 100px; 20 | } 21 | img#img { 22 | position: absolute; 23 | top: 75px; 24 | left: 100px; 25 | } -------------------------------------------------------------------------------- /WebChannelCar/Web/style/style.css: -------------------------------------------------------------------------------- 1 | .button::-moz-focus-inner{ 2 | border: 0; 3 | padding: 0; 4 | } 5 | 6 | .button{ 7 | display: inline-block; 8 | *display: inline; 9 | zoom: 1; 10 | padding: 6px 20px; 11 | margin: 0; 12 | cursor: pointer; 13 | border: 1px solid #bbb; 14 | overflow: visible; 15 | font: bold 13px arial, helvetica, sans-serif; 16 | text-decoration: none; 17 | white-space: nowrap; 18 | color: #555; 19 | 20 | background-color: #ddd; 21 | background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(255,255,255,1)), to(rgba(255,255,255,0))); 22 | background-image: -webkit-linear-gradient(top, rgba(255,255,255,1), rgba(255,255,255,0)); 23 | background-image: -moz-linear-gradient(top, rgba(255,255,255,1), rgba(255,255,255,0)); 24 | background-image: -ms-linear-gradient(top, rgba(255,255,255,1), rgba(255,255,255,0)); 25 | background-image: -o-linear-gradient(top, rgba(255,255,255,1), rgba(255,255,255,0)); 26 | background-image: linear-gradient(top, rgba(255,255,255,1), rgba(255,255,255,0)); 27 | 28 | -webkit-transition: background-color .2s ease-out; 29 | -moz-transition: background-color .2s ease-out; 30 | -ms-transition: background-color .2s ease-out; 31 | -o-transition: background-color .2s ease-out; 32 | transition: background-color .2s ease-out; 33 | background-clip: padding-box; /* Fix bleeding */ 34 | -moz-border-radius: 3px; 35 | -webkit-border-radius: 3px; 36 | border-radius: 3px; 37 | -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, .3), 0 2px 2px -1px rgba(0, 0, 0, .5), 0 1px 0 rgba(255, 255, 255, .3) inset; 38 | -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, .3), 0 2px 2px -1px rgba(0, 0, 0, .5), 0 1px 0 rgba(255, 255, 255, .3) inset; 39 | box-shadow: 0 1px 0 rgba(0, 0, 0, .3), 0 2px 2px -1px rgba(0, 0, 0, .5), 0 1px 0 rgba(255, 255, 255, .3) inset; 40 | text-shadow: 0 1px 0 rgba(255,255,255, .9); 41 | 42 | -webkit-touch-callout: none; 43 | -webkit-user-select: none; 44 | -khtml-user-select: none; 45 | -moz-user-select: none; 46 | -ms-user-select: none; 47 | user-select: none; 48 | } 49 | 50 | .button:hover{ 51 | background-color: #eee; 52 | color: #555; 53 | } 54 | 55 | .button:active{ 56 | background: #e9e9e9; 57 | position: relative; 58 | top: 1px; 59 | text-shadow: none; 60 | -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, .3) inset; 61 | -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .3) inset; 62 | box-shadow: 0 1px 1px rgba(0, 0, 0, .3) inset; 63 | } 64 | 65 | .button[disabled], .button[disabled]:hover, .button[disabled]:active{ 66 | border-color: #eaeaea; 67 | background: #fafafa; 68 | cursor: default; 69 | position: static; 70 | color: #999; 71 | /* Usually, !important should be avoided but here it's really needed :) */ 72 | -moz-box-shadow: none !important; 73 | -webkit-box-shadow: none !important; 74 | box-shadow: none !important; 75 | text-shadow: none !important; 76 | } 77 | 78 | /* Colored buttons styles */ 79 | 80 | .button.green, .button.red, .button.blue { 81 | color: #fff; 82 | text-shadow: 0 1px 0 rgba(0,0,0,.2); 83 | 84 | background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(255,255,255,.3)), to(rgba(255,255,255,0))); 85 | background-image: -webkit-linear-gradient(top, rgba(255,255,255,.3), rgba(255,255,255,0)); 86 | background-image: -moz-linear-gradient(top, rgba(255,255,255,.3), rgba(255,255,255,0)); 87 | background-image: -ms-linear-gradient(top, rgba(255,255,255,.3), rgba(255,255,255,0)); 88 | background-image: -o-linear-gradient(top, rgba(255,255,255,.3), rgba(255,255,255,0)); 89 | background-image: linear-gradient(top, rgba(255,255,255,.3), rgba(255,255,255,0)); 90 | } 91 | 92 | /* */ 93 | 94 | .button.green{ 95 | background-color: #57a957; 96 | border-color: #57a957; 97 | } 98 | 99 | .button.green:hover{ 100 | background-color: #62c462; 101 | } 102 | 103 | .button.green:active{ 104 | background: #57a957; 105 | } 106 | 107 | /* */ 108 | 109 | .button.red{ 110 | background-color: #ca3535; 111 | border-color: #c43c35; 112 | } 113 | 114 | .button.red:hover{ 115 | background-color: #ee5f5b; 116 | } 117 | 118 | .button.red:active{ 119 | background: #c43c35; 120 | } 121 | 122 | /* */ 123 | 124 | .button.blue{ 125 | background-color: #269CE9; 126 | border-color: #269CE9; 127 | } 128 | 129 | .button.blue:hover{ 130 | background-color: #70B9E8; 131 | } 132 | 133 | .button.blue:active{ 134 | background: #269CE9; 135 | } 136 | 137 | /* */ 138 | 139 | .green[disabled], .green[disabled]:hover, .green[disabled]:active{ 140 | border-color: #57A957; 141 | background: #57A957; 142 | color: #D2FFD2; 143 | } 144 | 145 | .red[disabled], .red[disabled]:hover, .red[disabled]:active{ 146 | border-color: #C43C35; 147 | background: #C43C35; 148 | color: #FFD3D3; 149 | } 150 | 151 | .blue[disabled], .blue[disabled]:hover, .blue[disabled]:active{ 152 | border-color: #269CE9; 153 | background: #269CE9; 154 | color: #93D5FF; 155 | } 156 | -------------------------------------------------------------------------------- /WebChannelCar/Web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | // "incremental": true, /* Enable incremental compilation */ 5 | "target": "ES5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ 6 | // "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 7 | "lib": ["ES2015", "dom"], /* Specify library files to be included in the compilation. */ 8 | // "allowJs": true, /* Allow javascript files to be compiled. */ 9 | // "checkJs": true, /* Report errors in .js files. */ 10 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 11 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 12 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 13 | "sourceMap": true, /* Generates corresponding '.map' file. */ 14 | // "outFile": "./", /* Concatenate and emit output to single file. */ 15 | "outDir": "./out", /* Redirect output structure to the directory. */ 16 | "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 17 | // "composite": true, /* Enable project compilation */ 18 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 19 | // "removeComments": true, /* Do not emit comments to output. */ 20 | // "noEmit": true, /* Do not emit outputs. */ 21 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 22 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 23 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 24 | 25 | /* Strict Type-Checking Options */ 26 | "strict": true, /* Enable all strict type-checking options. */ 27 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 28 | // "strictNullChecks": true, /* Enable strict null checks. */ 29 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 30 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 31 | "strictPropertyInitialization": false, /* Enable strict checking of property initialization in classes. */ 32 | "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 33 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 34 | 35 | /* Additional Checks */ 36 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 37 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 38 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 39 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 40 | 41 | /* Module Resolution Options */ 42 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 43 | "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 44 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 45 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 46 | // "typeRoots": [], /* List of folders to include type definitions from. */ 47 | // "types": [], /* Type declaration files to be included in compilation. */ 48 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 49 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 50 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 51 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 52 | 53 | /* Source Map Options */ 54 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 55 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 56 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 57 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 58 | 59 | /* Experimental Options */ 60 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 61 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 62 | 63 | /* Advanced Options */ 64 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 65 | }, 66 | "exclude": [ 67 | "node_modules" 68 | ] 69 | } 70 | -------------------------------------------------------------------------------- /WebChannelCar/Web/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "jsRules": {}, 7 | "rules": {}, 8 | "rulesDirectory": [] 9 | } -------------------------------------------------------------------------------- /WebChannelCar/Web/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | mode: 'development', 5 | entry: './src/main.ts', 6 | devtool: 'eval-source-map', 7 | module: { 8 | rules: [ 9 | { 10 | test: /\.tsx?$/, 11 | use: [{ 12 | loader: 'expose-loader', 13 | options: 'Library' 14 | }, { 15 | loader: 'ts-loader' 16 | }], 17 | exclude: /node_modules/ 18 | } 19 | ] 20 | }, 21 | resolve: { 22 | extensions: ['.tsx', '.ts', '.js'], 23 | }, 24 | output: { 25 | filename: 'main.js', 26 | path: path.resolve(__dirname, 'out') 27 | 28 | } 29 | }; -------------------------------------------------------------------------------- /WebChannelCar/WebChannelCar.pro: -------------------------------------------------------------------------------- 1 | QT += dbus widgets webchannel websockets 2 | 3 | HEADERS += car.h \ 4 | SocketWrap/websocketclientwrapper.h \ 5 | SocketWrap/websockettransport.h \ 6 | WebSDK.h 7 | 8 | SOURCES += car.cpp \ 9 | SocketWrap/websocketclientwrapper.cpp \ 10 | SocketWrap/websockettransport.cpp \ 11 | WebSDK.cpp \ 12 | main.cpp 13 | 14 | #DESTDIR = $$PWD/bin 15 | 16 | CONFIG(debug, debug|release) { 17 | DESTDIR = $$PWD/bin/debug 18 | } else { 19 | DESTDIR = $$PWD/bin/release 20 | } 21 | 22 | -------------------------------------------------------------------------------- /WebChannelCar/WebSDK.cpp: -------------------------------------------------------------------------------- 1 | #include "WebSDK.h" 2 | #include "SocketWrap/websockettransport.h" 3 | #include 4 | #include 5 | WebSDK::WebSDK(QWidget *pMainWindow, Car *car, QObject *parent) : QObject(parent), m_pMainWindow(pMainWindow), m_pCar(car) 6 | { 7 | m_server = new QWebSocketServer("EditorSocketServer", QWebSocketServer::NonSecureMode, this); 8 | const static quint16 portStart = 12345; 9 | quint16 retry = 0; 10 | bool connected = false; 11 | do { 12 | quint16 port = portStart + retry; 13 | connected = m_server->listen(QHostAddress::LocalHost, port); 14 | if (!connected) { 15 | qWarning() << "listen" << port << "failed: " << m_server->errorString(); 16 | retry++; 17 | } 18 | } while (!connected); 19 | 20 | m_channel.registerObject("car", m_pCar); 21 | 22 | m_server->setMaxPendingConnections(1); 23 | m_clientWraper = new WebSocketClientWrapper(m_server); 24 | connect(m_clientWraper, &WebSocketClientWrapper::clientConnected, &m_channel, &QWebChannel::connectTo); 25 | connect(m_clientWraper, &WebSocketClientWrapper::clientConnected, this, &WebSDK::onConnected); 26 | connect(m_clientWraper, &WebSocketClientWrapper::clientDisconnected, this, &WebSDK::onDisconnected); 27 | } 28 | 29 | WebSDK::~WebSDK() 30 | { 31 | if (m_clientWraper) { 32 | m_clientWraper->deleteLater(); 33 | m_clientWraper = nullptr; 34 | } 35 | if (m_server) { 36 | if (m_server->isListening()) { 37 | m_server->close(); 38 | } 39 | m_server->deleteLater(); 40 | m_server = nullptr; 41 | } 42 | } 43 | 44 | void WebSDK::openWeb(const QString &htmlFile, WebSDK::BrowserType type, const QUrlQuery &urlQuery) 45 | { 46 | const static QString browserArray[] = { "chrome", "firefox", "IExplore", "MicrosoftEdge" }; 47 | 48 | QUrl url = QUrl::fromLocalFile(htmlFile); 49 | 50 | QString browser = browserArray[static_cast(type)]; 51 | 52 | QUrlQuery query = urlQuery; 53 | query.addQueryItem("webChannelBaseUrl", m_server->serverUrl().toString()); 54 | url.setQuery(query); 55 | 56 | QString psCmd = QString("powershell -noprofile -command \"[void][System.Diagnostics.Process]::Start('%1', '%2')\"").arg(browser).arg(url.toString()); 57 | bool ok = QProcess::startDetached(psCmd); 58 | if (!ok) { 59 | qWarning() << "failed"; 60 | } 61 | } 62 | void WebSDK::onConnected() 63 | { 64 | m_pMainWindow->setWindowTitle("Qt Controlled Car (Connected)"); 65 | } 66 | void WebSDK::onDisconnected() 67 | { 68 | m_pMainWindow->setWindowTitle("Qt Controlled Car (DisConnected)"); 69 | } 70 | -------------------------------------------------------------------------------- /WebChannelCar/WebSDK.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "SocketWrap/websocketclientwrapper.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "car.h" 11 | class WebSDK : public QObject 12 | { 13 | Q_OBJECT 14 | 15 | 16 | public: 17 | enum class BrowserType : uint8_t 18 | { 19 | Chrome = 0, 20 | Firefox, 21 | IE, 22 | Edge 23 | }; 24 | explicit WebSDK(QWidget *pMainWindow, Car *car, QObject *parent = nullptr); 25 | virtual ~WebSDK() override; 26 | signals: 27 | public slots: 28 | void openWeb(const QString &htmlFile, BrowserType type = BrowserType::Chrome, const QUrlQuery &urlQuery = {}); 29 | protected slots: 30 | void onConnected(); 31 | void onDisconnected(); 32 | 33 | private: 34 | QWidget *m_pMainWindow = nullptr; 35 | Car *m_pCar = nullptr; 36 | QWebSocketServer *m_server = nullptr; 37 | QWebChannel m_channel; 38 | WebSocketClientWrapper *m_clientWraper = nullptr; 39 | }; 40 | -------------------------------------------------------------------------------- /WebChannelCar/car.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the examples of the Qt Toolkit. 7 | ** 8 | ** $QT_BEGIN_LICENSE:BSD$ 9 | ** Commercial License Usage 10 | ** Licensees holding valid commercial Qt licenses may use this file in 11 | ** accordance with the commercial license agreement provided with the 12 | ** Software or, alternatively, in accordance with the terms contained in 13 | ** a written agreement between you and The Qt Company. For licensing terms 14 | ** and conditions see https://www.qt.io/terms-conditions. For further 15 | ** information use the contact form at https://www.qt.io/contact-us. 16 | ** 17 | ** BSD License Usage 18 | ** Alternatively, you may use this file under the terms of the BSD license 19 | ** as follows: 20 | ** 21 | ** "Redistribution and use in source and binary forms, with or without 22 | ** modification, are permitted provided that the following conditions are 23 | ** met: 24 | ** * Redistributions of source code must retain the above copyright 25 | ** notice, this list of conditions and the following disclaimer. 26 | ** * Redistributions in binary form must reproduce the above copyright 27 | ** notice, this list of conditions and the following disclaimer in 28 | ** the documentation and/or other materials provided with the 29 | ** distribution. 30 | ** * Neither the name of The Qt Company Ltd nor the names of its 31 | ** contributors may be used to endorse or promote products derived 32 | ** from this software without specific prior written permission. 33 | ** 34 | ** 35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 46 | ** 47 | ** $QT_END_LICENSE$ 48 | ** 49 | ****************************************************************************/ 50 | 51 | #include "car.h" 52 | #include 53 | #include 54 | 55 | QRectF Car::boundingRect() const 56 | { 57 | return QRectF(-35, -81, 70, 115); 58 | } 59 | 60 | Car::Car(QGraphicsItem *parent) : QGraphicsObject(parent) 61 | { 62 | startTimer(1000 / 33); 63 | setFlag(QGraphicsItem::ItemIsMovable, true); 64 | setFlag(QGraphicsItem::ItemIsFocusable, true); 65 | } 66 | 67 | void Car::accelerate() 68 | { 69 | if (m_speed < 10) { 70 | ++m_speed; 71 | emit speedChanged(m_speed); 72 | } 73 | } 74 | 75 | void Car::decelerate() 76 | { 77 | if (m_speed > -10) { 78 | --m_speed; 79 | emit speedChanged(m_speed); 80 | } 81 | } 82 | 83 | void Car::turnLeft() 84 | { 85 | if (m_angle > -30) { 86 | m_angle -= 5; 87 | emit angleChanged(m_angle); 88 | } 89 | } 90 | 91 | void Car::turnRight() 92 | { 93 | if (m_angle < 30) { 94 | m_angle += 5; 95 | emit angleChanged(m_angle); 96 | } 97 | } 98 | 99 | const QColor &Car::getColor() const 100 | { 101 | return m_color; 102 | } 103 | 104 | void Car::setColor(const QColor &color) 105 | { 106 | m_color = color; 107 | } 108 | 109 | void Car::setSpeed(int32_t speed) 110 | { 111 | if (m_speed == speed) 112 | return; 113 | 114 | m_speed = speed; 115 | emit speedChanged(m_speed); 116 | } 117 | 118 | void Car::setAngle(int32_t angle) 119 | { 120 | if (m_angle == angle) 121 | return; 122 | 123 | m_angle = angle; 124 | emit angleChanged(m_angle); 125 | } 126 | 127 | void Car::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) 128 | { 129 | Q_UNUSED(option) 130 | Q_UNUSED(widget) 131 | 132 | painter->setBrush(Qt::gray); 133 | painter->drawRect(-20, -58, 40, 2); // front axel 134 | painter->drawRect(-20, 7, 40, 2); // rear axel 135 | 136 | painter->setBrush(m_color); 137 | painter->drawRect(-25, -79, 50, 10); // front wing 138 | 139 | painter->drawEllipse(-25, -48, 50, 20); // side pods 140 | painter->drawRect(-25, -38, 50, 35); // side pods 141 | painter->drawRect(-5, 9, 10, 10); // back pod 142 | 143 | painter->drawEllipse(-10, -81, 20, 100); // main body 144 | 145 | painter->drawRect(-17, 19, 34, 15); // rear wing 146 | 147 | painter->setBrush(Qt::black); 148 | painter->drawPie(-5, -51, 10, 15, 0, 180 * 16); 149 | painter->drawRect(-5, -44, 10, 10); // cocpit 150 | 151 | painter->save(); 152 | painter->translate(-20, -58); 153 | painter->rotate(m_angle); 154 | painter->drawRect(-10, -7, 10, 15); // front left 155 | painter->restore(); 156 | 157 | painter->save(); 158 | painter->translate(20, -58); 159 | painter->rotate(m_angle); 160 | painter->drawRect(0, -7, 10, 15); // front left 161 | painter->restore(); 162 | 163 | painter->drawRect(-30, 0, 12, 17); // rear left 164 | painter->drawRect(19, 0, 12, 17); // rear right 165 | } 166 | 167 | void Car::timerEvent(QTimerEvent *event) 168 | { 169 | Q_UNUSED(event) 170 | 171 | const qreal axelDistance = 54; 172 | qreal wheelsAngleRads = qDegreesToRadians(qreal(m_angle)); 173 | qreal turnDistance = ::cos(wheelsAngleRads) * axelDistance * 2; 174 | qreal turnRateRads = wheelsAngleRads / turnDistance; // rough estimate 175 | qreal turnRate = qRadiansToDegrees(turnRateRads); 176 | qreal rotation = m_speed * turnRate; 177 | 178 | setTransform(QTransform().rotate(rotation), true); 179 | setTransform(QTransform::fromTranslate(0, -m_speed), true); 180 | update(); 181 | } 182 | -------------------------------------------------------------------------------- /WebChannelCar/car.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the examples of the Qt Toolkit. 7 | ** 8 | ** $QT_BEGIN_LICENSE:BSD$ 9 | ** Commercial License Usage 10 | ** Licensees holding valid commercial Qt licenses may use this file in 11 | ** accordance with the commercial license agreement provided with the 12 | ** Software or, alternatively, in accordance with the terms contained in 13 | ** a written agreement between you and The Qt Company. For licensing terms 14 | ** and conditions see https://www.qt.io/terms-conditions. For further 15 | ** information use the contact form at https://www.qt.io/contact-us. 16 | ** 17 | ** BSD License Usage 18 | ** Alternatively, you may use this file under the terms of the BSD license 19 | ** as follows: 20 | ** 21 | ** "Redistribution and use in source and binary forms, with or without 22 | ** modification, are permitted provided that the following conditions are 23 | ** met: 24 | ** * Redistributions of source code must retain the above copyright 25 | ** notice, this list of conditions and the following disclaimer. 26 | ** * Redistributions in binary form must reproduce the above copyright 27 | ** notice, this list of conditions and the following disclaimer in 28 | ** the documentation and/or other materials provided with the 29 | ** distribution. 30 | ** * Neither the name of The Qt Company Ltd nor the names of its 31 | ** contributors may be used to endorse or promote products derived 32 | ** from this software without specific prior written permission. 33 | ** 34 | ** 35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 46 | ** 47 | ** $QT_END_LICENSE$ 48 | ** 49 | ****************************************************************************/ 50 | 51 | #ifndef CAR_H 52 | #define CAR_H 53 | 54 | #include 55 | #include 56 | 57 | class Car : public QGraphicsObject 58 | { 59 | Q_OBJECT 60 | Q_PROPERTY(int speed READ getSpeed WRITE setSpeed NOTIFY speedChanged) 61 | Q_PROPERTY(int angle READ getAngle WRITE setAngle NOTIFY angleChanged) 62 | public: 63 | Car(QGraphicsItem *parent = nullptr); 64 | 65 | QRectF boundingRect() const; 66 | 67 | public slots: 68 | int getSpeed() const 69 | { 70 | return m_speed; 71 | } 72 | 73 | int getAngle() const 74 | { 75 | return m_angle; 76 | } 77 | void setSpeed(int speed); 78 | 79 | void setAngle(int angle); 80 | 81 | public slots: 82 | void accelerate(); 83 | void decelerate(); 84 | void turnLeft(); 85 | void turnRight(); 86 | 87 | const QColor &getColor() const; 88 | 89 | void setColor(const QColor &color); 90 | 91 | signals: 92 | void speedChanged(int32_t speed); 93 | 94 | void angleChanged(int32_t angle); 95 | 96 | protected: 97 | void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr); 98 | void timerEvent(QTimerEvent *event); 99 | 100 | private: 101 | QColor m_color = Qt::green; 102 | int32_t m_speed = 0; 103 | int32_t m_angle = 0; 104 | }; 105 | 106 | #endif // CAR_H 107 | -------------------------------------------------------------------------------- /WebChannelCar/main.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the examples of the Qt Toolkit. 7 | ** 8 | ** $QT_BEGIN_LICENSE:BSD$ 9 | ** Commercial License Usage 10 | ** Licensees holding valid commercial Qt licenses may use this file in 11 | ** accordance with the commercial license agreement provided with the 12 | ** Software or, alternatively, in accordance with the terms contained in 13 | ** a written agreement between you and The Qt Company. For licensing terms 14 | ** and conditions see https://www.qt.io/terms-conditions. For further 15 | ** information use the contact form at https://www.qt.io/contact-us. 16 | ** 17 | ** BSD License Usage 18 | ** Alternatively, you may use this file under the terms of the BSD license 19 | ** as follows: 20 | ** 21 | ** "Redistribution and use in source and binary forms, with or without 22 | ** modification, are permitted provided that the following conditions are 23 | ** met: 24 | ** * Redistributions of source code must retain the above copyright 25 | ** notice, this list of conditions and the following disclaimer. 26 | ** * Redistributions in binary form must reproduce the above copyright 27 | ** notice, this list of conditions and the following disclaimer in 28 | ** the documentation and/or other materials provided with the 29 | ** distribution. 30 | ** * Neither the name of The Qt Company Ltd nor the names of its 31 | ** contributors may be used to endorse or promote products derived 32 | ** from this software without specific prior written permission. 33 | ** 34 | ** 35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 46 | ** 47 | ** $QT_END_LICENSE$ 48 | ** 49 | ****************************************************************************/ 50 | 51 | #include "car.h" 52 | #include "WebSDK.h" 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | int main(int argc, char *argv[]) 64 | { 65 | QApplication app(argc, argv); 66 | 67 | QMainWindow window; 68 | window.setWindowTitle("Qt Controlled Car"); 69 | window.resize(1024, 600); 70 | QGraphicsScene scene; 71 | scene.setSceneRect(-500, -500, 1000, 1000); 72 | scene.setItemIndexMethod(QGraphicsScene::NoIndex); 73 | 74 | Car car; 75 | scene.addItem(&car); 76 | 77 | QGraphicsView view(&scene, &window); 78 | view.setRenderHint(QPainter::Antialiasing); 79 | view.setBackgroundBrush(Qt::darkGray); 80 | view.resize(1024, 550); 81 | view.setGeometry(0, 50, 1024, 550); 82 | 83 | QPushButton resetButton("Reset", &window); 84 | resetButton.setGeometry(230, 0, 80, 30); 85 | 86 | QPushButton button("Controller", &window); 87 | button.setGeometry(320, 0, 80, 30); 88 | 89 | WebSDK sdk(&window, &car); 90 | QObject::connect(&button, &QPushButton::clicked, [&]() { sdk.openWeb(app.applicationDirPath() + "/page/index.html", WebSDK::BrowserType::Edge); }); 91 | QObject::connect(&resetButton, &QPushButton::clicked, [&]() { 92 | car.setSpeed(0); 93 | car.setAngle(0); 94 | car.setTransform({}); 95 | }); 96 | 97 | window.show(); 98 | return app.exec(); 99 | } 100 | -------------------------------------------------------------------------------- /WebQml/Qrc.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | 5 | 6 | -------------------------------------------------------------------------------- /WebQml/WebQml.pro: -------------------------------------------------------------------------------- 1 | QT += core gui quick qml webengine 2 | 3 | CONFIG += c++11 4 | 5 | SOURCES += \ 6 | main.cpp 7 | 8 | RESOURCES += \ 9 | Qrc.qrc 10 | 11 | CONFIG(debug, debug|release) { 12 | DESTDIR = $$PWD/bin/debug 13 | } else { 14 | DESTDIR = $$PWD/bin/release 15 | } 16 | -------------------------------------------------------------------------------- /WebQml/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | int main(int argc, char *argv[]) 5 | { 6 | QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); 7 | 8 | QGuiApplication a(argc, argv); 9 | QtWebEngine::initialize(); 10 | QQuickView view; 11 | view.setSource(QUrl("qrc:/main.qml")); 12 | view.show(); 13 | return a.exec(); 14 | } 15 | -------------------------------------------------------------------------------- /WebQml/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQuick.Controls 2.0 3 | import QtWebEngine 1.8 4 | Item { 5 | width: 800 6 | height: 600 7 | WebEngineView { 8 | anchors.fill: parent 9 | url: "https://www.zhihu.com" 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /WebWidget/WebWidget.pro: -------------------------------------------------------------------------------- 1 | QT += core gui webenginewidgets 2 | 3 | CONFIG += c++11 4 | 5 | SOURCES += \ 6 | main.cpp 7 | 8 | CONFIG(debug, debug|release) { 9 | DESTDIR = $$PWD/bin/debug 10 | } else { 11 | DESTDIR = $$PWD/bin/release 12 | } 13 | -------------------------------------------------------------------------------- /WebWidget/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | int main(int argc, char **argv) 4 | { 5 | QApplication app(argc, argv); 6 | 7 | QWebEngineView view; 8 | view.load(QUrl("https://www.zhihu.com/")); 9 | view.show(); 10 | 11 | return app.exec(); 12 | } 13 | --------------------------------------------------------------------------------