This commit is contained in:
zhuzichu
2023-07-18 18:24:06 +08:00
parent 4b3548563b
commit 98e0aafb44
40 changed files with 269 additions and 568 deletions

View File

@ -9,7 +9,7 @@ Window {
flags: Qt.SplashScreen
Component.onCompleted: {
FluApp.init(app)
FluTheme.darkMode = FluDarkMode.System
FluTheme.darkMode = FluThemeType.System
FluTheme.enableAnimation = true
FluApp.routes = {
"/":"qrc:/example/qml/window/MainWindow.qml",

View File

@ -5,5 +5,5 @@ import QtQuick.Controls
import FluentUI
QtObject {
property int displayMode : FluNavigationView.Auto
property int displayMode : FluNavigationViewType.Auto
}

View File

@ -31,7 +31,7 @@ FluScrollablePage{
title:"友情提示"
message:"确定要退出程序么?"
negativeText:"取消"
buttonFlags: FluContentDialog.NegativeButton | FluContentDialog.PositiveButton
buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
onNegativeClicked:{
showSuccess("点击取消按钮")
}
@ -47,7 +47,7 @@ FluScrollablePage{
id:double_btn_dialog
title:"友情提示"
message:"确定要退出程序么?"
buttonFlags: FluContentDialog.NegativeButton | FluContentDialog.PositiveButton
buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
negativeText:"取消"
onNegativeClicked:{
showSuccess("点击取消按钮")
@ -80,7 +80,7 @@ FluScrollablePage{
title:"友情提示"
message:"确定要退出程序么?"
negativeText:"取消"
buttonFlags: FluContentDialog.NeutralButton | FluContentDialog.NegativeButton | FluContentDialog.PositiveButton
buttonFlags: FluContentDialogType.NeutralButton | FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
negativeText:"取消"
onNegativeClicked:{
showSuccess("点击取消按钮")
@ -101,7 +101,7 @@ FluScrollablePage{
id:triple_btn_dialog
title:"友情提示"
message:"确定要退出程序么?"
buttonFlags: FluContentDialog.NeutralButton | FluContentDialog.NegativeButton | FluContentDialog.PositiveButton
buttonFlags: FluContentDialogType.NeutralButton | FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
negativeText:"取消"
onNegativeClicked:{
showSuccess("点击取消按钮")

View File

@ -7,7 +7,7 @@ import FluentUI
FluScrollablePage{
launchMode: FluPage.SingleTask
launchMode: FluPageType.SingleTask
animDisabled: true
ListModel{

View File

@ -93,9 +93,9 @@ FluScrollablePage{
Layout.fillWidth: true
Layout.topMargin: -1
code:'FluWindow{
//launchMode: FluWindow.Standard
//launchMode: FluWindow.SingleTask
launchMode: FluWindow.SingleInstance
//launchMode: FluWindowType.Standard
//launchMode: FluWindowType.SingleTask
launchMode: FluWindowType.SingleInstance
}
'
}

View File

@ -6,7 +6,7 @@ import FluentUI
import "qrc:///example/qml/component"
FluPage{
launchMode: FluPage.SingleTop
launchMode: FluPageType.SingleTop
FluRemoteLoader{
anchors.fill: parent
source: "https://zhu-zichu.gitee.io/T_RemoteLoader.qml"

View File

@ -28,7 +28,7 @@ FluScrollablePage{
Layout.bottomMargin: 4
}
Repeater{
model: [{title:"System",mode:FluDarkMode.System},{title:"Light",mode:FluDarkMode.Light},{title:"Dark",mode:FluDarkMode.Dark}]
model: [{title:"System",mode:FluThemeType.System},{title:"Light",mode:FluThemeType.Light},{title:"Dark",mode:FluThemeType.Dark}]
delegate: FluRadioButton{
checked : FluTheme.darkMode === modelData.mode
text:modelData.title
@ -58,7 +58,7 @@ FluScrollablePage{
Layout.bottomMargin: 4
}
Repeater{
model: [{title:"Open",mode:FluNavigationView.Open},{title:"Compact",mode:FluNavigationView.Compact},{title:"Minimal",mode:FluNavigationView.Minimal},{title:"Auto",mode:FluNavigationView.Auto}]
model: [{title:"Open",mode:FluNavigationViewType.Open},{title:"Compact",mode:FluNavigationViewType.Compact},{title:"Minimal",mode:FluNavigationViewType.Minimal},{title:"Auto",mode:FluNavigationViewType.Auto}]
delegate: FluRadioButton{
checked : MainEvent.displayMode===modelData.mode
text:modelData.title

View File

@ -25,28 +25,28 @@ FluScrollablePage{
text:"Loading"
onClicked: {
btn_status_mode.text = text
status_view.statusMode = FluStatusView.Loading
status_view.statusMode = FluStatusViewType.Loading
}
}
FluMenuItem{
text:"Empty"
onClicked: {
btn_status_mode.text = text
status_view.statusMode = FluStatusView.Empty
status_view.statusMode = FluStatusViewType.Empty
}
}
FluMenuItem{
text:"Error"
onClicked: {
btn_status_mode.text = text
status_view.statusMode = FluStatusView.Error
status_view.statusMode = FluStatusViewType.Error
}
}
FluMenuItem{
text:"Success"
onClicked: {
btn_status_mode.text = text
status_view.statusMode = FluStatusView.Success
status_view.statusMode = FluStatusViewType.Success
}
}
}
@ -75,7 +75,7 @@ FluScrollablePage{
Layout.topMargin: -1
code:'FluStatusView{
anchors.fill: parent
statusMode: FluStatusView.Loading
statusMode: FluStatusViewType.Loading
Rectangle{
anchors.fill: parent
color:FluTheme.primaryColor.dark

View File

@ -44,21 +44,21 @@ FluScrollablePage{
text:"Equal"
onClicked: {
btn_tab_width_behavior.text = text
tab_view.tabWidthBehavior = FluTabView.Equal
tab_view.tabWidthBehavior = FluTabViewType.Equal
}
}
FluMenuItem{
text:"SizeToContent"
onClicked: {
btn_tab_width_behavior.text = text
tab_view.tabWidthBehavior = FluTabView.SizeToContent
tab_view.tabWidthBehavior = FluTabViewType.SizeToContent
}
}
FluMenuItem{
text:"Compact"
onClicked: {
btn_tab_width_behavior.text = text
tab_view.tabWidthBehavior = FluTabView.Compact
tab_view.tabWidthBehavior = FluTabViewType.Compact
}
}
}
@ -70,21 +70,21 @@ FluScrollablePage{
text:"Nerver"
onClicked: {
btn_close_button_visibility.text = text
tab_view.closeButtonVisibility = FluTabView.Nerver
tab_view.closeButtonVisibility = FluTabViewType.Nerver
}
}
FluMenuItem{
text:"Always"
onClicked: {
btn_close_button_visibility.text = text
tab_view.closeButtonVisibility = FluTabView.Always
tab_view.closeButtonVisibility = FluTabViewType.Always
}
}
FluMenuItem{
text:"OnHover"
onClicked: {
btn_close_button_visibility.text = text
tab_view.closeButtonVisibility = FluTabView.OnHover
tab_view.closeButtonVisibility = FluTabViewType.OnHover
}
}
}

View File

@ -7,7 +7,7 @@ import "qrc:///example/qml/component"
FluScrollablePage{
launchMode: FluPage.SingleInstance
launchMode: FluPageType.SingleInstance
title:"TextBox"
FluArea{

View File

@ -55,9 +55,9 @@ FluScrollablePage{
checked: FluTheme.dark
onClicked: {
if(FluTheme.dark){
FluTheme.darkMode = FluDarkMode.Light
FluTheme.darkMode = FluThemeType.Light
}else{
FluTheme.darkMode = FluDarkMode.Dark
FluTheme.darkMode = FluThemeType.Dark
}
}
}

View File

@ -8,7 +8,7 @@ import "qrc:///example/qml/component"
FluScrollablePage{
title:"TimePicker"
launchMode: FluPage.SingleInstance
launchMode: FluPageType.SingleInstance
FluArea{
Layout.fillWidth: true
Layout.topMargin: 20
@ -23,7 +23,7 @@ FluScrollablePage{
}
FluText{
text:"hourFormat=FluTimePicker.H"
text:"hourFormat=FluTimePickerType.H"
}
FluTimePicker{
@ -56,11 +56,11 @@ FluScrollablePage{
}
FluText{
text:"hourFormat=FluTimePicker.HH"
text:"hourFormat=FluTimePickerType.HH"
}
FluTimePicker{
hourFormat:FluTimePicker.HH
hourFormat:FluTimePickerType.HH
onCurrentChanged: {
showSuccess(current.toLocaleTimeString(Qt.locale("de_DE")))
}
@ -72,7 +72,7 @@ FluScrollablePage{
Layout.fillWidth: true
Layout.topMargin: -1
code:'FluTimePicker{
hourFormat:FluTimePicker.HH
hourFormat:FluTimePickerType.HH
}'
}

View File

@ -64,38 +64,38 @@ FluScrollablePage {
text:"None"
onClicked: {
btn_selection_model.text = text
tree_view.selectionMode = FluTabView.Equal
tree_view.selectionMode = FluTabViewType.Equal
}
}
FluMenuItem{
text:"Single"
onClicked: {
btn_selection_model.text = text
tree_view.selectionMode = FluTabView.SizeToContent
tree_view.selectionMode = FluTabViewType.SizeToContent
}
}
FluMenuItem{
text:"Muiltple"
onClicked: {
btn_selection_model.text = text
tree_view.selectionMode = FluTabView.Compact
tree_view.selectionMode = FluTabViewType.Compact
}
}
}
FluFilledButton{
text:"获取选中的数据"
onClicked: {
if(tree_view.selectionMode === FluTreeView.None){
if(tree_view.selectionMode === FluTreeViewType.None){
showError("当前非选择模式,没有选中的数据")
}
if(tree_view.selectionMode === FluTreeView.Single){
if(tree_view.selectionMode === FluTreeViewType.Single){
if(!tree_view.signleData()){
showError("没有选中数据")
return
}
showSuccess(tree_view.signleData().text)
}
if(tree_view.selectionMode === FluTreeView.Multiple){
if(tree_view.selectionMode === FluTreeViewType.Multiple){
if(tree_view.multipData().length===0){
showError("没有选中数据")
return

View File

@ -11,7 +11,7 @@ CustomWindow {
width: 600
height: 600
fixSize: true
launchMode: FluWindow.SingleTask
launchMode: FluWindowType.SingleTask
ColumnLayout{
anchors{

View File

@ -13,7 +13,7 @@ CustomWindow {
height: 600
minimumWidth: 520
minimumHeight: 200
launchMode: FluWindow.SingleTask
launchMode: FluWindowType.SingleTask
FileWatcher{
id:watcher
onFileChanged: {
@ -25,7 +25,7 @@ CustomWindow {
FluRemoteLoader{
id:loader
anchors.fill: parent
statusMode: FluStatusView.Success
statusMode: FluStatusViewType.Success
lazy: true
errorItem: Item{
FluText{
@ -43,7 +43,7 @@ CustomWindow {
text:"拖入qml文件"
font.pixelSize: 26
anchors.centerIn: parent
visible: !loader.itemLodaer().item && loader.statusMode === FluStatusView.Success
visible: !loader.itemLodaer().item && loader.statusMode === FluStatusViewType.Success
}
Rectangle{
radius: 4

View File

@ -18,7 +18,7 @@ CustomWindow {
minimumWidth: 520
minimumHeight: 200
appBarVisible: false
launchMode: FluWindow.SingleTask
launchMode: FluWindowType.SingleTask
closeFunc:function(event){
dialog_close.open()
@ -29,15 +29,6 @@ CustomWindow {
FluTools.setQuitOnLastWindowClosed(false)
}
Connections{
target: appInfo
function onActiveWindow(){
window.show()
window.raise()
window.requestActivate()
}
}
SystemTrayIcon {
id:system_tray
visible: true
@ -67,7 +58,7 @@ CustomWindow {
title:"退出"
message:"确定要退出程序吗?"
negativeText:"最小化"
buttonFlags: FluContentDialog.NeutralButton | FluContentDialog.NegativeButton | FluContentDialog.PositiveButton
buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.NeutralButton | FluContentDialogType.PositiveButton
onNegativeClicked:{
window.hide()
system_tray.showMessage("友情提示","FluentUI已隐藏至托盘,点击托盘可再次激活窗口");
@ -173,9 +164,9 @@ CustomWindow {
height: parent.height
z:999
//Stack模式每次切换都会将页面压入栈中随着栈的页面增多消耗的内存也越多内存消耗多就会卡顿这时候就需要按返回将页面pop掉释放内存。该模式可以配合FluPage中的launchMode属性设置页面的启动模式
// pageMode: FluNavigationView.Stack
// pageMode: FluNavigationViewType.Stack
//NoStack模式每次切换都会销毁之前的页面然后创建一个新的页面只需消耗少量内存推荐
// pageMode: FluNavigationView.NoStack
// pageMode: FluNavigationViewType.NoStack
items: ItemsOriginal
footerItems:ItemsFooter
topPadding:FluTools.isMacos() ? 20 : 5
@ -238,9 +229,9 @@ CustomWindow {
function changeDark(){
if(FluTheme.dark){
FluTheme.darkMode = FluDarkMode.Light
FluTheme.darkMode = FluThemeType.Light
}else{
FluTheme.darkMode = FluDarkMode.Dark
FluTheme.darkMode = FluThemeType.Dark
}
}

View File

@ -11,7 +11,7 @@ CustomWindow {
width: 500
height: 600
fixSize: true
launchMode: FluWindow.SingleInstance
launchMode: FluWindowType.SingleInstance
FluTextBox{
anchors{

View File

@ -11,7 +11,7 @@ CustomWindow {
width: 500
height: 600
fixSize: true
launchMode: FluWindow.SingleTask
launchMode: FluWindowType.SingleTask
FluText{
anchors.centerIn: parent

View File

@ -11,7 +11,7 @@ CustomWindow {
width: 500
height: 600
fixSize: true
launchMode: FluWindow.Standard
launchMode: FluWindowType.Standard
FluMenuBar {
FluMenu {

View File

@ -39,18 +39,3 @@ void AppInfo::changeLang(const QString& locale){
lang(new En());
}
}
bool AppInfo::isOwnerProcess(IPC *ipc){
QString activeWindowEvent = "activeWindow";
if(!ipc->isCurrentOwner()){
ipc->postEvent(activeWindowEvent,QString().toUtf8(),0);
return false;
}
if(ipc->isAttached()){
ipc->registerEventHandler(activeWindowEvent,[=](const QByteArray&){
Q_EMIT this->activeWindow();
return true;
});
}
return true;
}

View File

@ -3,7 +3,6 @@
#include <QObject>
#include <QQmlApplicationEngine>
#include "tool/IPC.h"
#include "lang/Lang.h"
#include "stdafx.h"
@ -15,9 +14,7 @@ class AppInfo : public QObject
public:
explicit AppInfo(QObject *parent = nullptr);
void init(QQmlApplicationEngine *engine);
bool isOwnerProcess(IPC *ipc);
Q_INVOKABLE void changeLang(const QString& locale);
Q_SIGNAL void activeWindow();
};
#endif // APPINFO_H

View File

@ -12,7 +12,7 @@
FRAMELESSHELPER_USE_NAMESPACE
int main(int argc, char *argv[])
int main(int argc, char *argv[])
{
//将样式设置为Basic不然会导致组件显示异常
qputenv("QT_QUICK_CONTROLS_STYLE","Basic");
@ -32,10 +32,6 @@ FRAMELESSHELPER_USE_NAMESPACE
FramelessConfig::instance()->set(Global::Option::ForceNonNativeBackgroundBlur,false);
#endif
AppInfo* appInfo = new AppInfo();
IPC ipc(0);
if(!appInfo->isOwnerProcess(&ipc)){
return 0;
}
QQmlApplicationEngine engine;
FramelessHelper::Quick::registerTypes(&engine);
#ifdef FLUENTUI_BUILD_STATIC_LIB

View File

@ -1,250 +0,0 @@
#include "IPC.h"
#include <QCoreApplication>
#include <QDebug>
#include <QThread>
#include <ctime>
#include <random>
IPC::IPC(uint32_t profileId)
: profileId{profileId}
, globalMemory{"ipc-" IPC_PROTOCOL_VERSION}
{
qRegisterMetaType<IPCEventHandler>("IPCEventHandler");
timer.setInterval(EVENT_TIMER_MS);
timer.setSingleShot(true);
connect(&timer, &QTimer::timeout, this, &IPC::processEvents);
std::default_random_engine randEngine((std::random_device())());
std::uniform_int_distribution<uint64_t> distribution;
globalId = distribution(randEngine);
qDebug() << "Our global IPC ID is " << globalId;
if (globalMemory.create(sizeof(IPCMemory))) {
if (globalMemory.lock()) {
IPCMemory* mem = global();
memset(mem, 0, sizeof(IPCMemory));
mem->globalId = globalId;
mem->lastProcessed = time(nullptr);
globalMemory.unlock();
} else {
qWarning() << "Couldn't lock to take ownership";
}
} else if (globalMemory.attach()) {
qDebug() << "Attaching to the global shared memory";
} else {
qDebug() << "Failed to attach to the global shared memory, giving up. Error:"
<< globalMemory.error();
return;
}
processEvents();
}
IPC::~IPC()
{
if (!globalMemory.lock()) {
qWarning() << "Failed to lock in ~IPC";
return;
}
if (isCurrentOwnerNoLock()) {
global()->globalId = 0;
}
globalMemory.unlock();
}
time_t IPC::postEvent(const QString& name, const QByteArray& data, uint32_t dest)
{
QByteArray binName = name.toUtf8();
if (binName.length() > (int32_t)sizeof(IPCEvent::name)) {
return 0;
}
if (data.length() > (int32_t)sizeof(IPCEvent::data)) {
return 0;
}
if (!globalMemory.lock()) {
qDebug() << "Failed to lock in postEvent()";
return 0;
}
IPCEvent* evt = nullptr;
IPCMemory* mem = global();
time_t result = 0;
for (uint32_t i = 0; !evt && i < EVENT_QUEUE_SIZE; ++i) {
if (mem->events[i].posted == 0) {
evt = &mem->events[i];
}
}
if (evt) {
memset(evt, 0, sizeof(IPCEvent));
memcpy(evt->name, binName.constData(), binName.length());
memcpy(evt->data, data.constData(), data.length());
mem->lastEvent = evt->posted = result = qMax(mem->lastEvent + 1, time(nullptr));
evt->dest = dest;
evt->sender = qApp->applicationPid();
qDebug() << "postEvent " << name << "to" << dest;
}
globalMemory.unlock();
return result;
}
bool IPC::isCurrentOwner()
{
if (globalMemory.lock()) {
const bool isOwner = isCurrentOwnerNoLock();
globalMemory.unlock();
return isOwner;
} else {
qWarning() << "isCurrentOwner failed to lock, returning false";
return false;
}
}
void IPC::registerEventHandler(const QString& name, IPCEventHandler handler)
{
eventHandlers[name] = handler;
}
bool IPC::isEventAccepted(time_t time)
{
bool result = false;
if (!globalMemory.lock()) {
return result;
}
if (difftime(global()->lastProcessed, time) > 0) {
IPCMemory* mem = global();
for (uint32_t i = 0; i < EVENT_QUEUE_SIZE; ++i) {
if (mem->events[i].posted == time && mem->events[i].processed) {
result = mem->events[i].accepted;
break;
}
}
}
globalMemory.unlock();
return result;
}
bool IPC::waitUntilAccepted(time_t postTime, int32_t timeout /*=-1*/)
{
bool result = false;
time_t start = time(nullptr);
forever
{
result = isEventAccepted(postTime);
if (result || (timeout > 0 && difftime(time(nullptr), start) >= timeout)) {
break;
}
qApp->processEvents();
QThread::msleep(0);
}
return result;
}
bool IPC::isAttached() const
{
return globalMemory.isAttached();
}
void IPC::setProfileId(uint32_t profileId)
{
this->profileId = profileId;
}
IPC::IPCEvent* IPC::fetchEvent()
{
IPCMemory* mem = global();
for (uint32_t i = 0; i < EVENT_QUEUE_SIZE; ++i) {
IPCEvent* evt = &mem->events[i];
if ((evt->processed && difftime(time(nullptr), evt->processed) > EVENT_GC_TIMEOUT)
|| (!evt->processed && difftime(time(nullptr), evt->posted) > EVENT_GC_TIMEOUT)) {
memset(evt, 0, sizeof(IPCEvent));
}
if (evt->posted && !evt->processed && evt->sender != qApp->applicationPid()
&& (evt->dest == profileId || (evt->dest == 0 && isCurrentOwnerNoLock()))) {
return evt;
}
}
return nullptr;
}
bool IPC::runEventHandler(IPCEventHandler handler, const QByteArray& arg)
{
bool result = false;
if (QThread::currentThread() == qApp->thread()) {
result = handler(arg);
} else {
QMetaObject::invokeMethod(this, "runEventHandler", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, result), Q_ARG(IPCEventHandler, handler),
Q_ARG(const QByteArray&, arg));
}
return result;
}
void IPC::processEvents()
{
if (!globalMemory.lock()) {
timer.start();
return;
}
IPCMemory* mem = global();
if (mem->globalId == globalId) {
mem->lastProcessed = time(nullptr);
} else {
if (difftime(time(nullptr), mem->lastProcessed) >= OWNERSHIP_TIMEOUT_S) {
qDebug() << "Previous owner timed out, taking ownership" << mem->globalId << "->"
<< globalId;
memset(mem, 0, sizeof(IPCMemory));
mem->globalId = globalId;
mem->lastProcessed = time(nullptr);
}
}
while (IPCEvent* evt = fetchEvent()) {
QString name = QString::fromUtf8(evt->name);
auto it = eventHandlers.find(name);
if (it != eventHandlers.end()) {
evt->accepted = runEventHandler(it.value(), evt->data);
qDebug() << "Processed event:" << name << "posted:" << evt->posted
<< "accepted:" << evt->accepted;
if (evt->dest == 0) {
if (evt->accepted) {
evt->processed = time(nullptr);
}
} else {
evt->processed = time(nullptr);
}
} else {
qDebug() << "Received event:" << name << "without handler";
qDebug() << "Available handlers:" << eventHandlers.keys();
}
}
globalMemory.unlock();
timer.start();
}
bool IPC::isCurrentOwnerNoLock()
{
const void* const data = globalMemory.data();
if (!data) {
qWarning() << "isCurrentOwnerNoLock failed to access the memory, returning false";
return false;
}
return (*static_cast<const uint64_t*>(data) == globalId);
}
IPC::IPCMemory* IPC::global()
{
return static_cast<IPCMemory*>(globalMemory.data());
}

View File

@ -1,75 +0,0 @@
#ifndef IPC_H
#define IPC_H
#include <QMap>
#include <QObject>
#include <QSharedMemory>
#include <QTimer>
#include <ctime>
#include <functional>
using IPCEventHandler = std::function<bool(const QByteArray&)>;
#define IPC_PROTOCOL_VERSION "1"
class IPC : public QObject
{
Q_OBJECT
protected:
static const int EVENT_TIMER_MS = 1000;
static const int EVENT_GC_TIMEOUT = 5;
static const int EVENT_QUEUE_SIZE = 32;
static const int OWNERSHIP_TIMEOUT_S = 5;
public:
IPC(uint32_t profileId);
~IPC();
struct IPCEvent
{
uint32_t dest;
int32_t sender;
char name[16];
char data[128];
time_t posted;
time_t processed;
uint32_t flags;
bool accepted;
bool global;
};
struct IPCMemory
{
uint64_t globalId;
time_t lastEvent;
time_t lastProcessed;
IPCEvent events[IPC::EVENT_QUEUE_SIZE];
};
time_t postEvent(const QString& name, const QByteArray& data = QByteArray(), uint32_t dest = 0);
bool isCurrentOwner();
void registerEventHandler(const QString& name, IPCEventHandler handler);
bool isEventAccepted(time_t time);
bool waitUntilAccepted(time_t time, int32_t timeout = -1);
bool isAttached() const;
public slots:
void setProfileId(uint32_t profileId);
private:
IPCMemory* global();
bool runEventHandler(IPCEventHandler handler, const QByteArray& arg);
IPCEvent* fetchEvent();
void processEvents();
bool isCurrentOwnerNoLock();
private:
QTimer timer;
uint64_t globalId;
uint32_t profileId;
QSharedMemory globalMemory;
QMap<QString, IPCEventHandler> eventHandlers;
};
#endif // IPC_H