├── .clang-format
├── .gitattributes
├── .gitignore
├── .qmake.stash
├── DemoImages
├── main.png
└── sub.png
├── LICENSE
├── Main
├── Main.pro
├── Qml.qrc
├── Qml
│ ├── Comp
│ │ ├── CButton.qml
│ │ └── CDragArea.qml
│ └── Main.qml
└── Src
│ ├── Common.h
│ ├── IPC.cpp
│ ├── IPC.h
│ ├── ProcessMgr.cpp
│ ├── ProcessMgr.h
│ ├── TabMgr.cpp
│ ├── TabMgr.h
│ └── main.cpp
├── MulitProcessTab.pro
├── README.md
├── Sub
├── Qml.qrc
├── Qml
│ └── Main.qml
├── Src
│ └── main.cpp
└── Sub.pro
├── appveyor.yml
└── common.pri
/.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: true
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 | # 大括号换行,只有当BreakBeforeBraces设置为Custom时才有效
45 | BraceWrapping:
46 | # class定义后面
47 | AfterClass: true
48 | # 控制语句后面
49 | AfterControlStatement: false
50 | # enum定义后面
51 | AfterEnum: true
52 | # 函数定义后面
53 | AfterFunction: true
54 | # 命名空间定义后面
55 | AfterNamespace: true
56 | # ObjC定义后面
57 | AfterObjCDeclaration: false
58 | # struct定义后面
59 | AfterStruct: true
60 | # union定义后面
61 | AfterUnion: true
62 | # extern 定义后面
63 | AfterExternBlock: true
64 | # catch之前
65 | BeforeCatch: false
66 | # else 之前
67 | BeforeElse: false
68 | # 缩进大括号
69 | IndentBraces: false
70 |
71 | SplitEmptyFunction: true
72 |
73 | SplitEmptyRecord: true
74 |
75 | SplitEmptyNamespace: true
76 | # 在二元运算符前换行: None(在操作符后换行), NonAssignment(在非赋值的操作符前换行), All(在操作符前换行)
77 | BreakBeforeBinaryOperators: All
78 | # 在大括号前换行: Attach(始终将大括号附加到周围的上下文), Linux(除函数、命名空间和类定义,与Attach类似),
79 | # Mozilla(除枚举、函数、记录定义,与Attach类似), Stroustrup(除函数定义、catch、else,与Attach类似),
80 | # Allman(总是在大括号前换行), GNU(总是在大括号前换行,并对于控制语句的大括号增加额外的缩进), WebKit(在函数前换行), Custom
81 | # 注:这里认为语句块也属于函数
82 | BreakBeforeBraces: Allman
83 | # 继承列表的逗号前换行
84 | BreakBeforeInheritanceComma: false
85 | # 在三元运算符前换行
86 | BreakBeforeTernaryOperators: true
87 | # 在构造函数的初始化列表的逗号前换行
88 | BreakConstructorInitializersBeforeComma: false
89 | # 初始化列表前换行
90 | BreakConstructorInitializers: BeforeComma
91 | # Java注解后换行
92 | BreakAfterJavaFieldAnnotations: false
93 |
94 | BreakStringLiterals: true
95 | # 每行字符的限制,0表示没有限制
96 | ColumnLimit: 160
97 | # 描述具有特殊意义的注释的正则表达式,它不应该被分割为多行或以其它方式改变
98 | CommentPragmas: '^ IWYU pragma:'
99 | # 紧凑 命名空间
100 | CompactNamespaces: false
101 | # 构造函数的初始化列表要么都在同一行,要么都各自一行
102 | ConstructorInitializerAllOnOneLineOrOnePerLine: true
103 | # 构造函数的初始化列表的缩进宽度
104 | ConstructorInitializerIndentWidth: 4
105 | # 延续的行的缩进宽度
106 | ContinuationIndentWidth: 4
107 | # 去除C++11的列表初始化的大括号{后和}前的空格
108 | Cpp11BracedListStyle: false
109 | # 继承最常用的指针和引用的对齐方式
110 | DerivePointerAlignment: false
111 | # 关闭格式化
112 | DisableFormat: false
113 | # 自动检测函数的调用和定义是否被格式为每行一个参数(Experimental)
114 | ExperimentalAutoDetectBinPacking: false
115 | # 固定命名空间注释
116 | FixNamespaceComments: true
117 | # 需要被解读为foreach循环而不是函数调用的宏
118 | ForEachMacros:
119 | - foreach
120 | - Q_FOREACH
121 | - BOOST_FOREACH
122 |
123 | IncludeBlocks: Preserve
124 | # 对#include进行排序,匹配了某正则表达式的#include拥有对应的优先级,匹配不到的则默认优先级为INT_MAX(优先级越小排序越靠前),
125 | # 可以定义负数优先级从而保证某些#include永远在最前面
126 | IncludeCategories:
127 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/'
128 | Priority: 2
129 | - Regex: '^(<|"(gtest|gmock|isl|json)/)'
130 | Priority: 3
131 | - Regex: '.*'
132 | Priority: 1
133 | IncludeIsMainRegex: '(Test)?$'
134 | # 缩进case标签
135 | IndentCaseLabels: true
136 |
137 | IndentPPDirectives: None
138 | # 缩进宽度
139 | IndentWidth: 4
140 | # 函数返回类型换行时,缩进函数声明或函数定义的函数名
141 | IndentWrappedFunctionNames: false
142 |
143 | JavaScriptQuotes: Leave
144 |
145 | JavaScriptWrapImports: true
146 | # 保留在块开始处的空行
147 | KeepEmptyLinesAtTheStartOfBlocks: true
148 | # 开始一个块的宏的正则表达式
149 | MacroBlockBegin: ''
150 | # 结束一个块的宏的正则表达式
151 | MacroBlockEnd: ''
152 | # 连续空行的最大数量
153 | MaxEmptyLinesToKeep: 1
154 |
155 | # 命名空间的缩进: None, Inner(缩进嵌套的命名空间中的内容), All
156 | NamespaceIndentation: Inner
157 | # 使用ObjC块时缩进宽度
158 | ObjCBlockIndentWidth: 4
159 | # 在ObjC的@property后添加一个空格
160 | ObjCSpaceAfterProperty: true
161 | # 在ObjC的protocol列表前添加一个空格
162 | ObjCSpaceBeforeProtocolList: true
163 |
164 | PenaltyBreakAssignment: 2
165 |
166 | PenaltyBreakBeforeFirstCallParameter: 19
167 | # 在一个注释中引入换行的penalty
168 | PenaltyBreakComment: 300
169 | # 第一次在<<前换行的penalty
170 | PenaltyBreakFirstLessLess: 120
171 | # 在一个字符串字面量中引入换行的penalty
172 | PenaltyBreakString: 1000
173 | # 对于每个在行字符数限制之外的字符的penalty
174 | PenaltyExcessCharacter: 1000000
175 | # 将函数的返回类型放到它自己的行的penalty
176 | PenaltyReturnTypeOnItsOwnLine: 60
177 | # 指针和引用的对齐: Left, Right, Middle
178 | PointerAlignment: Right
179 |
180 | #RawStringFormats:
181 | # - Delimiter: pb
182 | # Language: TextProto
183 | # BasedOnStyle: google
184 | # 允许重新排版注释
185 | ReflowComments: true
186 | # 允许排序#include
187 | SortIncludes: true
188 |
189 | SortUsingDeclarations: true
190 | # 在C风格类型转换后添加空格
191 | SpaceAfterCStyleCast: false
192 | # 模板关键字后面添加空格
193 | SpaceAfterTemplateKeyword: true
194 | # 在赋值运算符之前添加空格
195 | SpaceBeforeAssignmentOperators: true
196 | # 开圆括号之前添加一个空格: Never, ControlStatements, Always
197 | SpaceBeforeParens: ControlStatements
198 | # 在空的圆括号中添加空格
199 | SpaceInEmptyParentheses: false
200 | # 在尾随的评论前添加的空格数(只适用于//)
201 | SpacesBeforeTrailingComments: 1
202 | # 在尖括号的<后和>前添加空格
203 | SpacesInAngles: false
204 | # 在容器(ObjC和JavaScript的数组和字典等)字面量中添加空格
205 | SpacesInContainerLiterals: true
206 | # 在C风格类型转换的括号中添加空格
207 | SpacesInCStyleCastParentheses: false
208 | # 在圆括号的(后和)前添加空格
209 | SpacesInParentheses: false
210 | # 在方括号的[后和]前添加空格,lamda表达式和未指明大小的数组的声明不受影响
211 | SpacesInSquareBrackets: false
212 | # 标准: Cpp03, Cpp11, Auto
213 | Standard: Cpp11
214 | # tab宽度
215 | TabWidth: 4
216 | # 使用tab字符: Never, ForIndentation, ForContinuationAndIndentation, Always
217 | UseTab: Never
218 | ...
219 |
220 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.qmake.stash:
--------------------------------------------------------------------------------
1 | QMAKE_LICHECK_TIMESTAMP = "Tue Oct 16"
2 | QMAKE_CXX.INCDIRS = \
3 | "C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE" \
4 | "C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\ATLMFC\\INCLUDE" \
5 | "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.17134.0\\ucrt" \
6 | "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.6.1\\include\\um" \
7 | "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.17134.0\\shared" \
8 | "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.17134.0\\um" \
9 | "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.17134.0\\winrt"
10 | QMAKE_CXX.LIBDIRS = \
11 | "C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\LIB\\amd64" \
12 | "C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\ATLMFC\\LIB\\amd64" \
13 | "C:\\Program Files (x86)\\Windows Kits\\10\\lib\\10.0.17134.0\\ucrt\\x64" \
14 | "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.6.1\\lib\\um\\x64" \
15 | "C:\\Program Files (x86)\\Windows Kits\\10\\lib\\10.0.17134.0\\um\\x64"
16 | QMAKE_CXX.QT_COMPILER_STDCXX = 199711L
17 | QMAKE_CXX.QMAKE_MSC_VER = 1900
18 | QMAKE_CXX.QMAKE_MSC_FULL_VER = 190024215
19 | QMAKE_CXX.COMPILER_MACROS = \
20 | QT_COMPILER_STDCXX \
21 | QMAKE_MSC_VER \
22 | QMAKE_MSC_FULL_VER
23 |
--------------------------------------------------------------------------------
/DemoImages/main.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaredtao/MulitProcessTab/2ea82eb144836778c9af21a6e8d9331bba5ac051/DemoImages/main.png
--------------------------------------------------------------------------------
/DemoImages/sub.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaredtao/MulitProcessTab/2ea82eb144836778c9af21a6e8d9331bba5ac051/DemoImages/sub.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 贾文涛
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 |
--------------------------------------------------------------------------------
/Main/Main.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = app
2 | TARGET= Main
3 | include($$PWD/../common.pri)
4 | LIBS += -luser32
5 | SOURCES += \
6 | Src/main.cpp \
7 | Src/TabMgr.cpp \
8 | Src/ProcessMgr.cpp \
9 | Src/IPC.cpp
10 |
11 | RESOURCES += \
12 | Qml.qrc
13 |
14 | HEADERS += \
15 | Src/TabMgr.h \
16 | Src/ProcessMgr.h \
17 | Src/IPC.h \
18 | Src/Common.h
19 |
--------------------------------------------------------------------------------
/Main/Qml.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | Qml/Main.qml
4 | Qml/Comp/CButton.qml
5 | Qml/Comp/CDragArea.qml
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Main/Qml/Comp/CButton.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.9
2 | import QtQuick.Controls 2.2
3 | Rectangle {
4 | id: root
5 | implicitWidth: 50
6 | implicitHeight: 40
7 | property alias text: btnText.text
8 | signal clicked()
9 | color: btnArea.pressed ? "red" : (btnArea.containsMouse ? "darkGray" : "white")
10 | property alias pressed: btnArea.pressed
11 | property alias containsMouse: btnArea.containsMouse
12 | Text {
13 | id: btnText
14 | anchors.centerIn: parent
15 | }
16 | MouseArea {
17 | id: btnArea
18 | anchors.fill: parent
19 | hoverEnabled: true
20 | onClicked: root.clicked()
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Main/Qml/Comp/CDragArea.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.9
2 |
3 | MouseArea {
4 | anchors.fill: parent
5 | hoverEnabled: true
6 | property real lastX: 0
7 | property real lastY: 0
8 | property var dragTarget
9 | onContainsMouseChanged: {
10 | if (containsMouse) {
11 | cursorShape = Qt.OpenHandCursor
12 | } else {
13 | cursorShape = Qt.ArrowCursor
14 | }
15 | }
16 | onPressedChanged: {
17 | if (containsPress) {
18 | lastX = mouseX;
19 | lastY = mouseY;
20 | }
21 | }
22 | onPositionChanged: {
23 | if (pressed && dragTarget) {
24 | dragTarget.x += mouseX - lastX
25 | dragTarget.y += mouseY - lastY
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Main/Qml/Main.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.9
2 | import QtQuick.Controls 2.2
3 | import QtQuick.Layouts 1.3
4 | import "./Comp"
5 | Item {
6 | id: root
7 | Rectangle {
8 | id: titleRect
9 | objectName: "titleRect"
10 | width: parent.width
11 | height: 40
12 | color: "blue"
13 | CDragArea {
14 | anchors.fill: parent
15 | dragTarget: rootView
16 | }
17 | CButton {
18 | id: mainPageBtn
19 | text: "首页"
20 | width: 150
21 | anchors.left: parent.left
22 | color: pressed ? "red" :
23 | (tabMgr.currentTab === "main" ? "green" :
24 | (containsMouse ? "darkGray" : "white"))
25 | onClicked: {
26 | tabMgr.activeTab("main")
27 | }
28 | }
29 | ListView {
30 | id: tabListView
31 | height: parent.height
32 | anchors.left: mainPageBtn.right
33 | anchors.right: toolBtnRow.left
34 | model: tabMgr.tabList
35 | orientation: Qt.Horizontal
36 | interactive: false;
37 | delegate: CButton {
38 | height: tabListView.height
39 | width: tabListView.count > 4 ? tabListView.width / tabListView.count : 150
40 | color: pressed ? "red" : ( tabMgr.currentTab === modelData ? "green":
41 | (containsMouse ? "darkGray" : "white"))
42 | text: modelData
43 | onClicked: {
44 | tabMgr.activeTab(modelData)
45 | }
46 | CButton {
47 | width: 8
48 | height: 8
49 | anchors.right: parent.right
50 | anchors.top: parent.top
51 | text: "X"
52 | color: pressed ? "red" : "gray"
53 | onClicked: {
54 | tabMgr.closeTab(modelData)
55 | }
56 | }
57 | }
58 | }
59 | Row {
60 | id: toolBtnRow
61 | anchors.right: parent.right
62 | CButton {
63 | text: "一"
64 | onClicked: {
65 | tabMgr.showMinimized()
66 | }
67 | }
68 | CButton {
69 | text: "口"
70 | property bool isMaxed: false
71 | onClicked: {
72 | if (isMaxed)
73 | {
74 | tabMgr.showNormal();
75 | }
76 | else
77 | {
78 | tabMgr.showMaximized();
79 | }
80 |
81 | isMaxed = ! isMaxed;
82 | }
83 | }
84 | CButton {
85 | text: "X"
86 | onClicked: {
87 | Qt.quit()
88 | }
89 | }
90 | }
91 |
92 | }
93 | Rectangle {
94 | id: contentRect
95 | objectName: "contentRect"
96 | width: parent.width
97 | anchors.top: titleRect.bottom
98 | anchors.bottom: parent.bottom
99 | color: "gray"
100 | GridLayout {
101 | anchors.fill: parent
102 | anchors.margins: 20
103 | columns: 4
104 | rows: 4
105 | Repeater {
106 | model: 16
107 | CButton {
108 | implicitWidth: 200
109 | implicitHeight: 150
110 | color: randColor()
111 | text: index
112 | onClicked: {
113 | tabMgr.activeTab(index)
114 | }
115 | }
116 | }
117 | }
118 |
119 | }
120 | function randColor() {
121 | return Qt.rgba(Math.random(), Math.random(), Math.random(), 1)
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/Main/Src/Common.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 | #include
4 |
5 | namespace std {
6 | template<>
7 | struct hash
8 | {
9 | std::size_t operator ()(const QString &str) const noexcept
10 | {
11 | return qHash(str);
12 | }
13 | };
14 | }
15 |
--------------------------------------------------------------------------------
/Main/Src/IPC.cpp:
--------------------------------------------------------------------------------
1 | #include "IPC.h"
2 | #include
3 | #include
4 | #include
5 | #include
6 | IPC::IPC(QObject *parent) : QObject(parent)
7 | {
8 | }
9 |
10 | IPC::~IPC()
11 | {
12 | m_server.close();
13 | m_socketMap.clear();
14 | }
15 |
16 | void IPC::Init()
17 | {
18 | connect(&m_server, &QLocalServer::newConnection, this, &IPC::onNewConnection);
19 | m_server.listen(QUuid::createUuid().toString());
20 | }
21 |
22 | void IPC::sendData(const QString &socketName, const QByteArray &data)
23 | {
24 | auto itor = m_socketMap.find(socketName);
25 | if (itor != m_socketMap.end())
26 | {
27 | itor->second->write(data);
28 | }
29 | }
30 |
31 | void IPC::onNewConnection()
32 | {
33 | while (m_server.hasPendingConnections())
34 | {
35 | QLocalSocket *socket = m_server.nextPendingConnection();
36 | connect(socket, QOverload::of(&QLocalSocket::error), this, &IPC::onError);
37 | connect(socket, &QLocalSocket::readyRead, this, &IPC::onReadyRead);
38 | connect(socket, &QLocalSocket::disconnected, this, &IPC::onDisconnected);
39 | //这里不直接存储socket指针,收到第一次数据时,才保存
40 | }
41 | }
42 |
43 | void IPC::onError(QLocalSocket::LocalSocketError socketError)
44 | {
45 | Q_UNUSED(socketError)
46 | QLocalSocket *socket = qobject_cast(sender());
47 | Q_ASSERT(socket);
48 | qWarning() << "socket error " << socket->errorString();
49 | }
50 |
51 | void IPC::onReadyRead()
52 | {
53 | QLocalSocket *socket = qobject_cast(sender());
54 | Q_ASSERT(socket);
55 | QByteArray data = socket->readAll();
56 | if (socket->objectName().isEmpty())
57 | {
58 | QJsonDocument doc = QJsonDocument::fromJson(data);
59 | QJsonObject obj = doc.object();
60 | if (obj.contains("processName"))
61 | {
62 | QString socketName = obj["processName"].toString();
63 | socket->setObjectName(socketName);
64 | m_socketMap.insert({socketName, socket});
65 | }
66 | }
67 | else
68 | {
69 | emit readyRead(socket->objectName(), data);
70 | }
71 | }
72 |
73 | void IPC::onDisconnected()
74 | {
75 | QLocalSocket *socket = qobject_cast(sender());
76 | Q_ASSERT(socket);
77 | qInfo() << "socket disconnected";
78 | auto itor = m_socketMap.find(socket->objectName());
79 | if (itor != m_socketMap.end())
80 | {
81 | m_socketMap.erase(itor);
82 | }
83 | // socket->deleteLater();
84 | }
85 |
--------------------------------------------------------------------------------
/Main/Src/IPC.h:
--------------------------------------------------------------------------------
1 | #ifndef IPC_H
2 | #define IPC_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include "Common.h"
9 | class IPC : public QObject
10 | {
11 | Q_OBJECT
12 | public:
13 | explicit IPC(QObject *parent = nullptr);
14 | ~IPC();
15 | //初始化
16 | void Init();
17 | QString GetServerName() const
18 | {
19 | return m_server.serverName();
20 | }
21 | void sendData(const QString &socketName, const QByteArray &data);
22 | signals:
23 | void readyRead(const QString &socketName, const QByteArray &data);
24 | private slots:
25 | void onNewConnection();
26 | void onError(QLocalSocket::LocalSocketError socketError);
27 | void onReadyRead();
28 | void onDisconnected();
29 | private:
30 | QLocalServer m_server;
31 | std::unordered_map m_socketMap;
32 | };
33 |
34 | #endif // IPC_H
35 |
--------------------------------------------------------------------------------
/Main/Src/ProcessMgr.cpp:
--------------------------------------------------------------------------------
1 | #include "ProcessMgr.h"
2 | #include
3 | #include
4 |
5 | ProcessMgr::ProcessMgr(QObject *parent) : QObject(parent) {}
6 |
7 | ProcessMgr::~ProcessMgr() {}
8 |
9 | void ProcessMgr::createProcess(const QString &program, const QStringList &arguments)
10 | {
11 | //创建新的QProcess,注意设置parent为this(可以让指针自动释放;可以在当前进程结束时,让子进程自动关闭)。
12 | QProcess *pro = new QProcess(this);
13 | pro->setProcessEnvironment(QProcessEnvironment::systemEnvironment());
14 | pro->setWorkingDirectory(qApp->applicationDirPath());
15 | connectProcess(pro);
16 | pro->start(program, arguments);
17 | pro->waitForStarted();
18 | }
19 |
20 | void ProcessMgr::connectProcess(QProcess *process)
21 | {
22 | connect(process, &QProcess::errorOccurred, this, &ProcessMgr::onErrorOccurred);
23 | connect(process, &QProcess::readyReadStandardOutput, this, &ProcessMgr::onReadyReadStandardOutput);
24 | connect(process, &QProcess::readyReadStandardError, this, &ProcessMgr::onReadyReadStandardError);
25 | connect(process, QOverload::of(&QProcess::finished), this, &ProcessMgr::onFinished);
26 | }
27 |
28 | void ProcessMgr::onFinished(int exitCode)
29 | {
30 | QProcess *pro = qobject_cast(sender());
31 | Q_ASSERT(pro);
32 | qInfo() << pro->program() << pro->processId() << " finished with exitcode " << exitCode;
33 | pro->deleteLater();
34 | }
35 |
36 | void ProcessMgr::onErrorOccurred(QProcess::ProcessError error)
37 | {
38 | Q_UNUSED(error)
39 | QProcess *pro = qobject_cast(sender());
40 | Q_ASSERT(pro);
41 | qWarning() << pro->program() << pro->processId() << " error: " << pro->errorString();
42 | }
43 |
44 | void ProcessMgr::onReadyReadStandardOutput()
45 | {
46 | QProcess *pro = qobject_cast(sender());
47 | Q_ASSERT(pro);
48 | qInfo() << pro->program() << pro->processId() << pro->readAllStandardOutput();
49 | }
50 |
51 | void ProcessMgr::onReadyReadStandardError()
52 | {
53 | QProcess *pro = qobject_cast(sender());
54 | Q_ASSERT(pro);
55 | qWarning() << pro->program() << pro->processId() << pro->readAllStandardError();
56 | }
57 |
--------------------------------------------------------------------------------
/Main/Src/ProcessMgr.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | class ProcessMgr : public QObject
6 | {
7 | Q_OBJECT
8 | public:
9 | explicit ProcessMgr(QObject *parent = nullptr);
10 | ~ProcessMgr();
11 | void createProcess(const QString &program, const QStringList &arguments);
12 | signals:
13 |
14 | public slots:
15 |
16 | private slots:
17 | void onFinished(int exitCode);
18 | void onErrorOccurred(QProcess::ProcessError error);
19 | void onReadyReadStandardOutput();
20 | void onReadyReadStandardError();
21 | private:
22 | void connectProcess(QProcess *process);
23 |
24 | };
25 |
--------------------------------------------------------------------------------
/Main/Src/TabMgr.cpp:
--------------------------------------------------------------------------------
1 | #include "TabMgr.h"
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 |
15 | std::string GetLastErrorAsString()
16 | {
17 | // Get the error message, if any.
18 | DWORD errorMessageID = ::GetLastError();
19 | if (errorMessageID == 0)
20 | return std::string("errorMessageID is 0"); // No error message has been recorded
21 |
22 | LPSTR messageBuffer = nullptr;
23 | size_t size = ::FormatMessageA(
24 | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
25 | nullptr,
26 | errorMessageID,
27 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
28 | (LPSTR)(&messageBuffer),
29 | 0,
30 | nullptr);
31 | std::string message(messageBuffer, size);
32 |
33 | // Free the buffer.
34 | ::LocalFree(messageBuffer);
35 |
36 | return message;
37 | }
38 |
39 | TabMgr::TabMgr(QObject *parent) : QObject(parent)
40 | {
41 | m_view.resize(1200, 800);
42 | m_view.setResizeMode(QQuickView::SizeRootObjectToView);
43 | m_view.setFlags(Qt::FramelessWindowHint | Qt::Dialog);
44 | m_view.rootContext()->setContextProperty("rootView", &m_view);
45 | m_view.rootContext()->setContextProperty("tabMgr", this);
46 | m_view.setSource(QUrl("qrc:/Qml/Main.qml"));
47 | connect(&m_view, &QQuickView::widthChanged, this, &TabMgr::syncSize);
48 | connect(&m_view, &QQuickView::heightChanged, this, &TabMgr::syncSize);
49 | connect(m_view.engine(), &QQmlEngine::quit, qApp, &QApplication::quit);
50 | m_view.show();
51 | m_ipc.Init();
52 | connect(&m_ipc, &IPC::readyRead, this, &TabMgr::onReadyReay);
53 | }
54 |
55 | TabMgr::~TabMgr()
56 | {
57 | for (auto name : m_tabList)
58 | {
59 | closeTab(name);
60 | }
61 | }
62 |
63 | void TabMgr::activeTab(const QString &name)
64 | {
65 | if (name == s_mainStr)
66 | {
67 | setCurrentTab(name);
68 | return;
69 | }
70 | auto it = m_processMap.find(name);
71 | if (it == m_processMap.end())
72 | {
73 | //找不到则创建进程
74 | QStringList args;
75 | args << m_ipc.GetServerName() << name;
76 | {
77 | QQuickItem *titleItem = m_view.rootObject()->findChild("titleRect");
78 | Q_ASSERT(titleItem);
79 | QQuickItem *contentItem = m_view.rootObject()->findChild("contentRect");
80 | Q_ASSERT(contentItem);
81 | QJsonObject obj{ { "x", m_view.position().x() },
82 | { "y", m_view.position().y() + titleItem->height() },
83 | { "w", contentItem->width() },
84 | { "h", contentItem->height() } };
85 | args << QString(QJsonDocument(obj).toJson());
86 | }
87 |
88 | m_processMgr.createProcess(qApp->applicationDirPath() + "/Sub.exe", args);
89 |
90 | m_processMap[name] = 0;
91 | m_tabList.append(name);
92 | emit tabListChanged();
93 | }
94 | setCurrentTab(name);
95 | }
96 |
97 | void TabMgr::closeTab(const QString &name)
98 | {
99 | if (name == s_mainStr)
100 | {
101 | return;
102 | }
103 | if (name == currentTab())
104 | {
105 | setCurrentTab(s_mainStr);
106 | }
107 | m_tabList.removeOne(name);
108 | emit tabListChanged();
109 | m_processMap.erase(name);
110 | QJsonObject obj{ { "operator", "quit" } };
111 | m_ipc.sendData(name, QJsonDocument(obj).toJson());
112 | }
113 |
114 | const QStringList &TabMgr::tabList() const
115 | {
116 | return m_tabList;
117 | }
118 |
119 | const QString &TabMgr::currentTab() const
120 | {
121 | return m_currentTab;
122 | }
123 |
124 | void TabMgr::showMaximized()
125 | {
126 | m_view.showMaximized();
127 | }
128 |
129 | void TabMgr::showMinimized()
130 | {
131 | m_view.showMinimized();
132 | }
133 |
134 | void TabMgr::showNormal()
135 | {
136 | m_view.showNormal();
137 | }
138 |
139 | void TabMgr::setCurrentTab(const QString ¤tTab)
140 | {
141 | if (m_currentTab == currentTab)
142 | {
143 | return;
144 | }
145 | //新的页面不是main则raise,否则不处理
146 | if (currentTab != s_mainStr)
147 | {
148 | raiseSubProcess(currentTab);
149 | }
150 | //旧的页面不是main则lower,否则不处理
151 | if (m_currentTab != s_mainStr)
152 | {
153 | lowerSubProcess(m_currentTab);
154 | }
155 | m_currentTab = currentTab;
156 | emit currentTabChanged();
157 | }
158 |
159 | void TabMgr::onReadyReay(const QString &socketName, const QByteArray &data)
160 | {
161 | QJsonParseError error;
162 | QJsonDocument doc = QJsonDocument::fromJson(data, &error);
163 | if (doc.isNull())
164 | {
165 | qWarning() << "parseError " << error.errorString();
166 | return;
167 | }
168 | QJsonObject obj = doc.object();
169 | for (auto key : obj.keys())
170 | {
171 | if (key == QStringLiteral("winid"))
172 | {
173 | quint64 winid = obj[key].toString().toULongLong();
174 | qWarning() << "received winid" << winid;
175 | m_processMap[socketName] = winid;
176 | auto hw = ::SetParent(reinterpret_cast(winid), reinterpret_cast(m_view.winId()));
177 | if (nullptr == hw)
178 | {
179 | std::cout << GetLastErrorAsString() << std::endl;
180 | continue;
181 | }
182 | QQuickItem *contentItem = m_view.rootObject()->findChild("contentRect");
183 | Q_ASSERT(contentItem);
184 | QQuickItem *titleItem = m_view.rootObject()->findChild("titleRect");
185 | Q_ASSERT(titleItem);
186 | ::MoveWindow(reinterpret_cast(winid),
187 | 0,
188 | static_cast(titleItem->height()),
189 | static_cast(contentItem->width()),
190 | static_cast(contentItem->height()),
191 | true);
192 | }
193 | }
194 | }
195 |
196 | void TabMgr::syncSize()
197 | {
198 | QQuickItem *contentItem = m_view.rootObject()->findChild("contentRect");
199 | Q_ASSERT(contentItem);
200 | QQuickItem *titleItem = m_view.rootObject()->findChild("titleRect");
201 | Q_ASSERT(titleItem);
202 | QJsonObject obj{
203 | { "operator", "resize" },
204 | { "x", 0},
205 | { "y", titleItem->height()},
206 | { "w", contentItem->width()},
207 | { "h", contentItem->height()},
208 | };
209 | QByteArray json = QJsonDocument(obj).toJson();
210 | for (auto tab : m_tabList) {
211 | m_ipc.sendData(tab, json);
212 | }
213 | }
214 |
215 | void TabMgr::raiseSubProcess(const QString &subProcessName)
216 | {
217 | // auto itor = m_processMap.find(subProcessName);
218 | // if (itor != m_processMap.end())
219 | // {
220 | // HWND hwnd = (HWND)(itor->second);
221 | // ::ShowWindow(hwnd, SW_SHOW);
222 |
223 | // QQuickItem *contentItem = m_view.rootObject()->findChild("contentRect");
224 | // Q_ASSERT(contentItem);
225 | // QQuickItem *titleItem = m_view.rootObject()->findChild("titleRect");
226 | // Q_ASSERT(titleItem);
227 | // ::MoveWindow(hwnd, 0, titleItem->height(), contentItem->width(), contentItem->height(), true);
228 |
229 | // qWarning() << "raise" << itor->second;
230 | // }
231 | QJsonObject obj{ { "operator", "show" } };
232 | m_ipc.sendData(subProcessName, QJsonDocument(obj).toJson());
233 | }
234 |
235 | void TabMgr::lowerSubProcess(const QString &subProcessName)
236 | {
237 | // auto itor = m_processMap.find(subProcessName);
238 | // if (itor != m_processMap.end())
239 | // {
240 | // HWND hwnd = (HWND)(itor->second);
241 |
242 | // QQuickItem *contentItem = m_view.rootObject()->findChild("contentRect");
243 | // Q_ASSERT(contentItem);
244 |
245 | // ::MoveWindow(hwnd, 2000, 2000, contentItem->width(), contentItem->height(), true);
246 |
247 | // ::ShowWindow(hwnd, SW_HIDE);
248 | // qWarning() << "lower" << itor->second;
249 | // }
250 | QJsonObject obj{ { "operator", "hide" } };
251 | m_ipc.sendData(subProcessName, QJsonDocument(obj).toJson());
252 | }
253 |
--------------------------------------------------------------------------------
/Main/Src/TabMgr.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include