#include "project_workspace_controller.h" #include "hmi_editor_widget.h" #include "logic_editor_widget.h" #include "runtime_monitor_widget.h" #include "services/hmi_editor_service.h" #include "services/hmi_navigation_service.h" #include "services/logic_editor_service.h" #include "services/project_service.h" #include "services/runtime_mode_service.h" #include "ui_main_window.h" #include #include #include #include #include #include #include #include #include #include #include namespace { constexpr int kProjectTreeKindRole = Qt::UserRole; constexpr int kProjectTreeIdRole = Qt::UserRole + 1; QString fromUtf8(const std::string &value) { return QString::fromUtf8(value.data(), static_cast(value.size())); } std::string toUtf8(const QString &value) { const QByteArray bytes = value.toUtf8(); return std::string(bytes.constData(), static_cast(bytes.size())); } bool requestRequiredName( QWidget &parent, const QString &title, const QString &label, const QString &initial_value, QString *name) { QInputDialog dialog(&parent); dialog.setWindowTitle(title); dialog.setLabelText(label); dialog.setInputMode(QInputDialog::TextInput); dialog.setTextValue(initial_value); QDialogButtonBox *button_box = dialog.findChild(); QPushButton *ok_button = button_box != nullptr ? button_box->button(QDialogButtonBox::Ok) : nullptr; if (ok_button != nullptr) { ok_button->setEnabled(!initial_value.trimmed().isEmpty()); QObject::connect( &dialog, &QInputDialog::textValueChanged, &dialog, [ok_button](const QString &text) { ok_button->setEnabled(!text.trimmed().isEmpty()); }); } if (dialog.exec() != QDialog::Accepted) { return false; } *name = dialog.textValue().trimmed(); return true; } } // namespace ProjectWorkspaceController::ProjectWorkspaceController( QWidget &parent, Ui::MainWindow &ui, ProjectService &project_service, HmiEditorService &hmi_editor_service, LogicEditorService &logic_editor_service, RuntimeModeService &runtime_mode_service, HmiNavigationService &hmi_navigation_service, HmiEditorWidget &hmi_editor_widget, LogicEditorWidget &logic_editor_widget, RuntimeMonitorWidget &runtime_monitor_widget, std::string ¤t_hmi_page_id, std::string ¤t_logic_id, std::function show_control_properties, std::function show_logic_properties, ResultReporter result_reporter, StatusReporter status_reporter, EditStateChanged edit_state_changed) : parent_(parent), ui_(ui), project_service_(project_service), hmi_editor_service_(hmi_editor_service), logic_editor_service_(logic_editor_service), runtime_mode_service_(runtime_mode_service), hmi_navigation_service_(hmi_navigation_service), hmi_editor_widget_(hmi_editor_widget), logic_editor_widget_(logic_editor_widget), runtime_monitor_widget_(runtime_monitor_widget), current_hmi_page_id_(current_hmi_page_id), current_logic_id_(current_logic_id), show_control_properties_(std::move(show_control_properties)), show_logic_properties_(std::move(show_logic_properties)), result_reporter_(std::move(result_reporter)), status_reporter_(std::move(status_reporter)), edit_state_changed_(std::move(edit_state_changed)) { } void ProjectWorkspaceController::configure() { ui_.projectTree->setContextMenuPolicy(Qt::ActionsContextMenu); add_page_action_ = new QAction(QObject::tr("新建页面"), &parent_); add_page_action_->setObjectName(QStringLiteral("addPageAction")); add_logic_action_ = new QAction(QObject::tr("新建控制逻辑"), &parent_); add_logic_action_->setObjectName(QStringLiteral("addLogicAction")); rename_item_action_ = new QAction(QObject::tr("重命名"), &parent_); rename_item_action_->setObjectName(QStringLiteral("renameProjectItemAction")); delete_item_action_ = new QAction(QObject::tr("删除"), &parent_); delete_item_action_->setObjectName(QStringLiteral("deleteProjectItemAction")); move_item_up_action_ = new QAction(QObject::tr("上移"), &parent_); move_item_up_action_->setObjectName(QStringLiteral("moveProjectItemUpAction")); move_item_down_action_ = new QAction(QObject::tr("下移"), &parent_); move_item_down_action_->setObjectName( QStringLiteral("moveProjectItemDownAction")); set_initial_page_action_ = new QAction(QObject::tr("设为初始页面"), &parent_); set_initial_page_action_->setObjectName(QStringLiteral("setInitialPageAction")); toggle_logic_enabled_action_ = new QAction( QObject::tr("禁用控制逻辑"), &parent_); toggle_logic_enabled_action_->setObjectName( QStringLiteral("toggleLogicEnabledAction")); ui_.projectTree->addActions({ add_page_action_, add_logic_action_, rename_item_action_, delete_item_action_, move_item_up_action_, move_item_down_action_, set_initial_page_action_, toggle_logic_enabled_action_}); QObject::connect(add_page_action_, &QAction::triggered, &parent_, [this] { addPage(); }); QObject::connect(add_logic_action_, &QAction::triggered, &parent_, [this] { addLogic(); }); QObject::connect(rename_item_action_, &QAction::triggered, &parent_, [this] { renameSelectedItem(); }); QObject::connect(delete_item_action_, &QAction::triggered, &parent_, [this] { deleteSelectedItem(); }); QObject::connect(move_item_up_action_, &QAction::triggered, &parent_, [this] { moveSelectedItem(-1); }); QObject::connect(move_item_down_action_, &QAction::triggered, &parent_, [this] { moveSelectedItem(1); }); QObject::connect(set_initial_page_action_, &QAction::triggered, &parent_, [this] { setSelectedPageAsInitial(); }); QObject::connect(toggle_logic_enabled_action_, &QAction::triggered, &parent_, [this] { toggleSelectedLogicEnabled(); }); QObject::connect(ui_.projectTree, &QTreeWidget::currentItemChanged, &parent_, [this](QTreeWidgetItem *, QTreeWidgetItem *) { handleSelectionChanged(); }); updateActions(); } void ProjectWorkspaceController::refresh() { if (hmi_editor_service_.findPage(current_hmi_page_id_) == nullptr) { const std::string &initial_id = project_service_.project().initialHmiPageId; current_hmi_page_id_ = hmi_editor_service_.findPage(initial_id) != nullptr ? initial_id : hmi_editor_service_.firstPageId(); show_control_properties_({}); } if (logic_editor_service_.findLogic(current_logic_id_) == nullptr) { current_logic_id_ = logic_editor_service_.firstLogicId(); show_logic_properties_({}); } hmi_editor_widget_.setPageId(current_hmi_page_id_); logic_editor_widget_.setLogicId(current_logic_id_); if (runtime_mode_service_.mode() != ApplicationMode::Editing) { runtime_monitor_widget_.setProjectObjects( hmi_navigation_service_.currentPageId(), current_logic_id_); } runtime_mode_service_.refreshPlcPollAddresses(); const QSignalBlocker blocker(ui_.projectTree); ui_.projectTree->clear(); auto *hmi_root = new QTreeWidgetItem(ui_.projectTree, {QObject::tr("HMI 页面")}); hmi_root->setData(0, kProjectTreeKindRole, static_cast(ItemKind::Root)); QTreeWidgetItem *selected_item = nullptr; for (const HmiPage &page : project_service_.project().hmiPages) { const QString suffix = page.id == project_service_.project().initialHmiPageId ? QObject::tr("(初始)") : QString{}; auto *item = new QTreeWidgetItem( hmi_root, {fromUtf8(page.name) + suffix}); item->setData(0, kProjectTreeKindRole, static_cast(ItemKind::HmiPage)); item->setData(0, kProjectTreeIdRole, fromUtf8(page.id)); if (page.id == current_hmi_page_id_ && ui_.editorTabWidget->currentIndex() == 0) { selected_item = item; } } const HmiPage *current_page = hmi_editor_service_.findPage(current_hmi_page_id_); if (current_page != nullptr) { ui_.hmiPageTitleLabel->setText(fromUtf8(current_page->name)); ui_.hmiPageSizeLabel->setText( QObject::tr("%1 x %2").arg(current_page->width).arg(current_page->height)); } else { ui_.hmiPageTitleLabel->setText(QObject::tr("尚未创建 HMI 页面")); ui_.hmiPageSizeLabel->clear(); } auto *logic_root = new QTreeWidgetItem( ui_.projectTree, {QObject::tr("控制逻辑")}); logic_root->setData(0, kProjectTreeKindRole, static_cast(ItemKind::Root)); for (const ControlLogic &logic : project_service_.project().controlLogics) { const QString suffix = logic.enabled ? QString{} : QObject::tr("(已禁用)"); auto *item = new QTreeWidgetItem( logic_root, {fromUtf8(logic.name) + suffix}); item->setData( 0, kProjectTreeKindRole, static_cast(ItemKind::ControlLogic)); item->setData(0, kProjectTreeIdRole, fromUtf8(logic.id)); if (logic.id == current_logic_id_ && ui_.editorTabWidget->currentIndex() == 1) { selected_item = item; } } ui_.projectTree->expandAll(); if (selected_item != nullptr) { ui_.projectTree->setCurrentItem(selected_item); } updateActions(); } void ProjectWorkspaceController::handleSelectionChanged() { QTreeWidgetItem *item = ui_.projectTree->currentItem(); if (item == nullptr) { updateActions(); return; } const auto kind = static_cast( item->data(0, kProjectTreeKindRole).toInt()); const std::string id = toUtf8(item->data(0, kProjectTreeIdRole).toString()); if (kind == ItemKind::HmiPage && hmi_editor_service_.findPage(id) != nullptr) { current_hmi_page_id_ = id; hmi_editor_widget_.setPageId(id); hmi_editor_widget_.reloadPage(); ui_.editorTabWidget->setCurrentWidget(ui_.hmiEditorTab); show_control_properties_({}); const HmiPage *page = hmi_editor_service_.findPage(id); ui_.hmiPageTitleLabel->setText(fromUtf8(page->name)); ui_.hmiPageSizeLabel->setText( QObject::tr("%1 x %2").arg(page->width).arg(page->height)); } else if (kind == ItemKind::ControlLogic && logic_editor_service_.findLogic(id) != nullptr) { current_logic_id_ = id; logic_editor_widget_.setLogicId(id); logic_editor_widget_.reloadLogic(); ui_.editorTabWidget->setCurrentWidget(ui_.logicEditorTab); show_logic_properties_({}); } updateActions(); } void ProjectWorkspaceController::updateActions() { if (add_page_action_ == nullptr) { return; } QTreeWidgetItem *item = ui_.projectTree->currentItem(); const auto kind = item == nullptr ? ItemKind::Root : static_cast(item->data(0, kProjectTreeKindRole).toInt()); const bool page_selected = kind == ItemKind::HmiPage; const bool logic_selected = kind == ItemKind::ControlLogic; const bool editable = runtime_mode_service_.policy().allowsProjectEditing; add_page_action_->setEnabled(editable); add_logic_action_->setEnabled(editable); rename_item_action_->setEnabled(editable && (page_selected || logic_selected)); delete_item_action_->setEnabled(editable && (page_selected || logic_selected)); move_item_up_action_->setEnabled(editable && (page_selected || logic_selected)); move_item_down_action_->setEnabled(editable && (page_selected || logic_selected)); set_initial_page_action_->setEnabled(editable && page_selected); toggle_logic_enabled_action_->setEnabled(editable && logic_selected); if (logic_selected) { const std::string id = toUtf8(item->data(0, kProjectTreeIdRole).toString()); const ControlLogic *logic = logic_editor_service_.findLogic(id); toggle_logic_enabled_action_->setText( logic != nullptr && logic->enabled ? QObject::tr("禁用控制逻辑") : QObject::tr("启用控制逻辑")); } } void ProjectWorkspaceController::addPage() { QString name; if (!requestRequiredName( parent_, QObject::tr("新建页面"), QObject::tr("页面名称"), {}, &name)) { return; } const HmiEditorResult result = hmi_editor_service_.addPage(toUtf8(name)); if (!result.succeeded) { reportFailure(QObject::tr("新建页面"), result.message); return; } current_hmi_page_id_ = result.id; show_control_properties_({}); ui_.editorTabWidget->setCurrentWidget(ui_.hmiEditorTab); refresh(); hmi_editor_widget_.reloadPage(); edit_state_changed_(); } void ProjectWorkspaceController::addLogic() { QString name; if (!requestRequiredName( parent_, QObject::tr("新建控制逻辑"), QObject::tr("逻辑名称"), {}, &name)) { return; } const LogicEditorResult result = logic_editor_service_.addLogic(toUtf8(name)); if (!result.succeeded) { reportFailure(QObject::tr("新建控制逻辑"), result.message); return; } current_logic_id_ = result.id; show_logic_properties_({}); ui_.editorTabWidget->setCurrentWidget(ui_.logicEditorTab); refresh(); logic_editor_widget_.reloadLogic(); edit_state_changed_(); } void ProjectWorkspaceController::renameSelectedItem() { QTreeWidgetItem *item = ui_.projectTree->currentItem(); if (item == nullptr) { return; } const auto kind = static_cast( item->data(0, kProjectTreeKindRole).toInt()); const std::string id = toUtf8(item->data(0, kProjectTreeIdRole).toString()); QString current_name; if (kind == ItemKind::HmiPage) { const HmiPage *page = hmi_editor_service_.findPage(id); current_name = page == nullptr ? QString{} : fromUtf8(page->name); } else if (kind == ItemKind::ControlLogic) { const ControlLogic *logic = logic_editor_service_.findLogic(id); current_name = logic == nullptr ? QString{} : fromUtf8(logic->name); } else { return; } QString name; if (!requestRequiredName( parent_, QObject::tr("重命名"), QObject::tr("新名称"), current_name, &name)) { return; } if (kind == ItemKind::HmiPage) { const HmiEditorResult result = hmi_editor_service_.renamePage(id, toUtf8(name)); if (!result.succeeded) { reportFailure(QObject::tr("重命名页面"), result.message); return; } } else { const LogicEditorResult result = logic_editor_service_.renameLogic( id, toUtf8(name)); if (!result.succeeded) { reportFailure(QObject::tr("重命名控制逻辑"), result.message); return; } } refresh(); edit_state_changed_(); } void ProjectWorkspaceController::deleteSelectedItem() { QTreeWidgetItem *item = ui_.projectTree->currentItem(); if (item == nullptr) { return; } const auto kind = static_cast( item->data(0, kProjectTreeKindRole).toInt()); const std::string id = toUtf8(item->data(0, kProjectTreeIdRole).toString()); if (kind == ItemKind::HmiPage) { const HmiEditorResult result = hmi_editor_service_.removePage(id); if (!result.succeeded) { reportFailure(QObject::tr("删除页面"), result.message); return; } } else if (kind == ItemKind::ControlLogic) { const LogicEditorResult result = logic_editor_service_.removeLogic(id); if (!result.succeeded) { reportFailure(QObject::tr("删除控制逻辑"), result.message); return; } } else { return; } refresh(); show_control_properties_({}); show_logic_properties_({}); edit_state_changed_(); } void ProjectWorkspaceController::moveSelectedItem(int offset) { QTreeWidgetItem *item = ui_.projectTree->currentItem(); if (item == nullptr) { return; } const auto kind = static_cast( item->data(0, kProjectTreeKindRole).toInt()); const std::string id = toUtf8(item->data(0, kProjectTreeIdRole).toString()); if (kind == ItemKind::HmiPage) { const HmiEditorResult result = hmi_editor_service_.movePage(id, offset); if (!result.succeeded) { status_reporter_(fromUtf8(result.message), 4000); return; } } else if (kind == ItemKind::ControlLogic) { const LogicEditorResult result = logic_editor_service_.moveLogic(id, offset); if (!result.succeeded) { status_reporter_(fromUtf8(result.message), 4000); return; } } else { return; } refresh(); edit_state_changed_(); } void ProjectWorkspaceController::setSelectedPageAsInitial() { QTreeWidgetItem *item = ui_.projectTree->currentItem(); if (item == nullptr || static_cast(item->data(0, kProjectTreeKindRole).toInt()) != ItemKind::HmiPage) { return; } const HmiEditorResult result = hmi_editor_service_.setInitialPage( toUtf8(item->data(0, kProjectTreeIdRole).toString())); if (!result.succeeded) { reportFailure(QObject::tr("设置初始页面"), result.message); return; } refresh(); edit_state_changed_(); } void ProjectWorkspaceController::toggleSelectedLogicEnabled() { QTreeWidgetItem *item = ui_.projectTree->currentItem(); if (item == nullptr || static_cast(item->data(0, kProjectTreeKindRole).toInt()) != ItemKind::ControlLogic) { return; } const std::string id = toUtf8(item->data(0, kProjectTreeIdRole).toString()); const ControlLogic *logic = logic_editor_service_.findLogic(id); if (logic == nullptr) { return; } const LogicEditorResult result = logic_editor_service_.setLogicEnabled( id, !logic->enabled); if (!result.succeeded) { reportFailure(QObject::tr("启停控制逻辑"), result.message); return; } refresh(); edit_state_changed_(); } void ProjectWorkspaceController::reportFailure( const QString &action, const std::string &message) const { result_reporter_(action, fromUtf8(message), false); }