综合平台编程器项目的远程存储
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

800 lines
32 KiB

  1. #include "property_panel_controller.h"
  2. #include "domain/hmi_control_registry.h"
  3. #include "domain/project_limits.h"
  4. #include "hmi_editor_widget.h"
  5. #include "logic_editor_widget.h"
  6. #include "logic_instruction_dialog.h"
  7. #include "services/hmi_editor_service.h"
  8. #include "services/logic_editor_service.h"
  9. #include "services/project_service.h"
  10. #include "ui_main_window.h"
  11. #include <QApplication>
  12. #include <QCheckBox>
  13. #include <QColorDialog>
  14. #include <QComboBox>
  15. #include <QLabel>
  16. #include <QLineEdit>
  17. #include <QPushButton>
  18. #include <QDialog>
  19. #include <QObject>
  20. #include <QSpinBox>
  21. #include <QStackedWidget>
  22. #include <QWidget>
  23. #include <algorithm>
  24. #include <cstdint>
  25. #include <utility>
  26. #include <vector>
  27. namespace {
  28. QString fromUtf8(const std::string &value)
  29. {
  30. return QString::fromUtf8(value.data(), static_cast<int>(value.size()));
  31. }
  32. std::string toUtf8(const QString &value)
  33. {
  34. const QByteArray bytes = value.toUtf8();
  35. return std::string(bytes.constData(), static_cast<std::size_t>(bytes.size()));
  36. }
  37. int defaultFontPointSize(HmiControlType type)
  38. {
  39. int point_size = QApplication::font().pointSize();
  40. if (type == HmiControlType::AlarmList)
  41. {
  42. point_size -= ProjectLimits::kAlarmDefaultFontPointReduction;
  43. }
  44. return std::clamp(
  45. point_size,
  46. ProjectLimits::kMinimumHmiFontPointSize,
  47. ProjectLimits::kMaximumHmiFontPointSize);
  48. }
  49. } // namespace
  50. PropertyPanelController::PropertyPanelController(
  51. QWidget &parent,
  52. Ui::MainWindow &ui,
  53. ProjectService &project_service,
  54. HmiEditorService &hmi_editor_service,
  55. LogicEditorService &logic_editor_service,
  56. std::function<std::string()> current_page_id,
  57. std::function<std::string()> current_logic_id,
  58. std::string &selected_control_id,
  59. std::string &selected_logic_node_id,
  60. std::function<void()> refresh_project_ui,
  61. ResultReporter result_reporter,
  62. StatusReporter status_reporter)
  63. : parent_(parent),
  64. ui_(ui),
  65. project_service_(project_service),
  66. hmi_editor_service_(hmi_editor_service),
  67. logic_editor_service_(logic_editor_service),
  68. current_page_id_(std::move(current_page_id)),
  69. current_logic_id_(std::move(current_logic_id)),
  70. selected_control_id_(selected_control_id),
  71. selected_logic_node_id_(selected_logic_node_id),
  72. refresh_project_ui_(std::move(refresh_project_ui)),
  73. result_reporter_(std::move(result_reporter)),
  74. status_reporter_(std::move(status_reporter))
  75. {
  76. }
  77. void PropertyPanelController::configure()
  78. {
  79. ui_.pageWidthSpinBox->setRange(
  80. ProjectLimits::kMinimumHmiPageWidth,
  81. ProjectLimits::kMaximumHmiPageWidth);
  82. ui_.pageHeightSpinBox->setRange(
  83. ProjectLimits::kMinimumHmiPageHeight,
  84. ProjectLimits::kMaximumHmiPageHeight);
  85. ui_.fontSizeSpinBox->setRange(
  86. ProjectLimits::kMinimumHmiFontPointSize,
  87. ProjectLimits::kMaximumHmiFontPointSize);
  88. ui_.bindingAreaComboBox->setItemData(0, -1);
  89. ui_.bindingAreaComboBox->setItemData(1, 0);
  90. ui_.bindingAreaComboBox->setItemData(2, 1);
  91. ui_.buttonOperationComboBox->setItemData(
  92. 0, static_cast<int>(HmiButtonOperation::SetOn));
  93. ui_.buttonOperationComboBox->setItemData(
  94. 1, static_cast<int>(HmiButtonOperation::SetOff));
  95. ui_.buttonOperationComboBox->setItemData(
  96. 2, static_cast<int>(HmiButtonOperation::Toggle));
  97. ui_.buttonOperationComboBox->setItemData(
  98. 3, static_cast<int>(HmiButtonOperation::MomentaryOn));
  99. for (int index = 0; index < ui_.logicComparisonComboBox->count(); ++index)
  100. {
  101. ui_.logicComparisonComboBox->setItemData(index, index);
  102. }
  103. QObject::connect(ui_.applyPropertiesButton, &QPushButton::clicked,
  104. &parent_, [this] { applySelectedControlProperties(); });
  105. QObject::connect(ui_.applyPagePropertiesButton, &QPushButton::clicked,
  106. &parent_, [this] { applySelectedPageProperties(); });
  107. QObject::connect(ui_.applyLogicPropertiesButton, &QPushButton::clicked,
  108. &parent_, [this] { applySelectedLogicNodeProperties(); });
  109. QObject::connect(ui_.textColorButton, &QPushButton::clicked,
  110. &parent_, [this] { chooseTextColor(); });
  111. ui_.targetPageLabel->setVisible(false);
  112. ui_.targetPageComboBox->setVisible(false);
  113. showControlProperties({});
  114. }
  115. void PropertyPanelController::bindEditorWidgets(
  116. HmiEditorWidget &hmi_editor_widget,
  117. LogicEditorWidget &logic_editor_widget)
  118. {
  119. hmi_editor_widget_ = &hmi_editor_widget;
  120. logic_editor_widget_ = &logic_editor_widget;
  121. QObject::connect(&hmi_editor_widget, &HmiEditorWidget::controlSelected,
  122. &parent_, [this](const QString &id)
  123. {
  124. showControlProperties(toUtf8(id));
  125. });
  126. QObject::connect(&hmi_editor_widget, &HmiEditorWidget::controlChanged,
  127. &parent_, [this](const QString &id)
  128. {
  129. showControlProperties(toUtf8(id));
  130. refresh_project_ui_();
  131. });
  132. QObject::connect(&hmi_editor_widget, &HmiEditorWidget::editorError,
  133. &parent_, [this](const QString &message)
  134. {
  135. handleHmiEditorError(message);
  136. });
  137. QObject::connect(&logic_editor_widget, &LogicEditorWidget::nodeSelected,
  138. &parent_, [this](const QString &id)
  139. {
  140. showLogicNodeProperties(toUtf8(id));
  141. });
  142. QObject::connect(&logic_editor_widget, &LogicEditorWidget::graphChanged,
  143. &parent_, [this]
  144. {
  145. refresh_project_ui_();
  146. });
  147. QObject::connect(&logic_editor_widget, &LogicEditorWidget::editorError,
  148. &parent_, [this](const QString &message)
  149. {
  150. handleLogicEditorError(message);
  151. });
  152. }
  153. void PropertyPanelController::showControlProperties(const std::string &control_id)
  154. {
  155. if (control_id.empty())
  156. {
  157. showPageProperties(current_page_id_());
  158. return;
  159. }
  160. ui_.propertyStack->setCurrentWidget(ui_.hmiPropertiesPage);
  161. selected_control_id_ = control_id;
  162. const HmiControl *control = hmi_editor_service_.findControl(
  163. current_page_id_(), control_id);
  164. const bool has_control = control != nullptr;
  165. const HmiControlDescriptor *descriptor = has_control
  166. ? findHmiControlDescriptor(control->type) : nullptr;
  167. ui_.selectionValueLabel->setText(
  168. has_control ? fromUtf8(control->id)
  169. + (control->isConfigured() ? QString{}
  170. : control->type == HmiControlType::PageJump
  171. ? QObject::tr("(未配置)") : QObject::tr("(未绑定)"))
  172. : QObject::tr("未选择"));
  173. for (QWidget *widget : {static_cast<QWidget *>(ui_.controlIdEdit),
  174. static_cast<QWidget *>(ui_.controlTextEdit),
  175. static_cast<QWidget *>(ui_.controlXSpinBox),
  176. static_cast<QWidget *>(ui_.controlYSpinBox),
  177. static_cast<QWidget *>(ui_.controlWidthSpinBox),
  178. static_cast<QWidget *>(ui_.controlHeightSpinBox),
  179. static_cast<QWidget *>(ui_.bindingAreaComboBox),
  180. static_cast<QWidget *>(ui_.bindingIndexSpinBox),
  181. static_cast<QWidget *>(ui_.targetPageComboBox),
  182. static_cast<QWidget *>(ui_.buttonOperationComboBox),
  183. static_cast<QWidget *>(ui_.progressMinimumSpinBox),
  184. static_cast<QWidget *>(ui_.progressMaximumSpinBox),
  185. static_cast<QWidget *>(ui_.progressShowValueCheckBox),
  186. static_cast<QWidget *>(ui_.textColorEdit),
  187. static_cast<QWidget *>(ui_.textColorButton),
  188. static_cast<QWidget *>(ui_.fontSizeSpinBox),
  189. static_cast<QWidget *>(ui_.fontBoldCheckBox),
  190. static_cast<QWidget *>(ui_.fontItalicCheckBox),
  191. static_cast<QWidget *>(ui_.applyPropertiesButton)})
  192. {
  193. widget->setEnabled(has_control);
  194. }
  195. if (!has_control)
  196. {
  197. ui_.controlTextEdit->setMaxLength(
  198. static_cast<int>(ProjectLimits::kMaximumHmiControlTextCharacters));
  199. ui_.bindingAreaLabel->setVisible(false);
  200. ui_.bindingAreaComboBox->setVisible(false);
  201. ui_.bindingIndexLabel->setVisible(false);
  202. ui_.bindingIndexSpinBox->setVisible(false);
  203. ui_.targetPageLabel->setVisible(false);
  204. ui_.targetPageComboBox->setVisible(false);
  205. ui_.buttonOperationLabel->setVisible(false);
  206. ui_.buttonOperationComboBox->setVisible(false);
  207. ui_.progressMinimumLabel->setVisible(false);
  208. ui_.progressMinimumSpinBox->setVisible(false);
  209. ui_.progressMaximumLabel->setVisible(false);
  210. ui_.progressMaximumSpinBox->setVisible(false);
  211. ui_.progressShowValueLabel->setVisible(false);
  212. ui_.progressShowValueCheckBox->setVisible(false);
  213. ui_.textColorEdit->clear();
  214. ui_.textColorButton->setStyleSheet(QString{});
  215. ui_.fontSizeSpinBox->setValue(
  216. std::clamp(
  217. QApplication::font().pointSize(),
  218. ProjectLimits::kMinimumHmiFontPointSize,
  219. ProjectLimits::kMaximumHmiFontPointSize));
  220. ui_.fontBoldCheckBox->setChecked(false);
  221. ui_.fontItalicCheckBox->setChecked(false);
  222. return;
  223. }
  224. ui_.controlTextEdit->setMaxLength(
  225. static_cast<int>(ProjectLimits::kMaximumHmiControlTextCharacters));
  226. ui_.controlIdEdit->setText(fromUtf8(control->id));
  227. ui_.controlTextEdit->setText(fromUtf8(control->text));
  228. ui_.controlXSpinBox->setValue(control->bounds.x);
  229. ui_.controlYSpinBox->setValue(control->bounds.y);
  230. ui_.controlWidthSpinBox->setValue(control->bounds.width);
  231. ui_.controlHeightSpinBox->setValue(control->bounds.height);
  232. ui_.bindingAreaComboBox->setCurrentIndex(!control->binding.has_value() ? 0
  233. : control->binding->area() == RegisterArea::M ? 1 : 2);
  234. ui_.bindingIndexSpinBox->setValue(
  235. control->binding.has_value() ? control->binding->index() : 0);
  236. const bool has_binding = descriptor != nullptr
  237. && descriptor->bindingKind != HmiBindingKind::None;
  238. ui_.bindingAreaLabel->setVisible(has_binding);
  239. ui_.bindingAreaComboBox->setVisible(has_binding);
  240. ui_.bindingIndexLabel->setVisible(has_binding);
  241. ui_.bindingIndexSpinBox->setVisible(has_binding);
  242. const bool is_button = control->type == HmiControlType::Button;
  243. ui_.buttonOperationLabel->setVisible(is_button);
  244. ui_.buttonOperationComboBox->setVisible(is_button);
  245. ui_.buttonOperationComboBox->setEnabled(is_button);
  246. ui_.buttonOperationComboBox->setCurrentIndex(
  247. ui_.buttonOperationComboBox->findData(
  248. static_cast<int>(control->buttonOperation)));
  249. const bool is_page_jump = control->type == HmiControlType::PageJump;
  250. ui_.targetPageLabel->setVisible(is_page_jump);
  251. ui_.targetPageComboBox->setVisible(is_page_jump);
  252. ui_.targetPageComboBox->setEnabled(is_page_jump);
  253. ui_.targetPageComboBox->clear();
  254. if (is_page_jump)
  255. {
  256. for (const HmiPage &page : project_service_.project().hmiPages)
  257. {
  258. ui_.targetPageComboBox->addItem(
  259. fromUtf8(page.name), fromUtf8(page.id));
  260. }
  261. const std::string target_id = control->pageJump.has_value()
  262. ? control->pageJump->targetPageId : std::string{};
  263. ui_.targetPageComboBox->setCurrentIndex(
  264. ui_.targetPageComboBox->findData(fromUtf8(target_id)));
  265. }
  266. const bool is_progress_bar = control->type == HmiControlType::ProgressBar;
  267. ui_.progressMinimumLabel->setVisible(is_progress_bar);
  268. ui_.progressMinimumSpinBox->setVisible(is_progress_bar);
  269. ui_.progressMaximumLabel->setVisible(is_progress_bar);
  270. ui_.progressMaximumSpinBox->setVisible(is_progress_bar);
  271. ui_.progressShowValueLabel->setVisible(is_progress_bar);
  272. ui_.progressShowValueCheckBox->setVisible(is_progress_bar);
  273. ui_.progressMinimumSpinBox->setEnabled(is_progress_bar);
  274. ui_.progressMaximumSpinBox->setEnabled(is_progress_bar);
  275. ui_.progressShowValueCheckBox->setEnabled(is_progress_bar);
  276. const HmiProgressBarConfig progress_config = control->progressBar.value_or(
  277. HmiProgressBarConfig{});
  278. ui_.progressMinimumSpinBox->setValue(progress_config.minimumValue);
  279. ui_.progressMaximumSpinBox->setValue(progress_config.maximumValue);
  280. ui_.progressShowValueCheckBox->setChecked(progress_config.showValue);
  281. const auto text_color = control->properties.find(
  282. HmiAppearanceProperty::kTextColor);
  283. ui_.textColorEdit->setText(text_color == control->properties.cend()
  284. ? QString{}
  285. : fromUtf8(text_color->second));
  286. const QColor swatch_color(ui_.textColorEdit->text());
  287. ui_.textColorButton->setStyleSheet(
  288. swatch_color.isValid()
  289. ? QStringLiteral("background-color: %1;").arg(
  290. swatch_color.name(QColor::HexRgb))
  291. : QString{});
  292. const int default_font_size = defaultFontPointSize(control->type);
  293. int font_size = default_font_size;
  294. const auto font_size_property = control->properties.find(
  295. HmiAppearanceProperty::kFontSize);
  296. if (font_size_property != control->properties.cend())
  297. {
  298. bool ok = false;
  299. const int parsed = fromUtf8(font_size_property->second).toInt(&ok);
  300. if (ok)
  301. {
  302. font_size = parsed;
  303. }
  304. }
  305. ui_.fontSizeSpinBox->setValue(std::clamp(
  306. font_size,
  307. ProjectLimits::kMinimumHmiFontPointSize,
  308. ProjectLimits::kMaximumHmiFontPointSize));
  309. const auto font_bold = control->properties.find(
  310. HmiAppearanceProperty::kFontBold);
  311. ui_.fontBoldCheckBox->setChecked(
  312. font_bold != control->properties.cend() && font_bold->second == "true");
  313. const auto font_italic = control->properties.find(
  314. HmiAppearanceProperty::kFontItalic);
  315. ui_.fontItalicCheckBox->setChecked(
  316. font_italic != control->properties.cend()
  317. && font_italic->second == "true");
  318. }
  319. void PropertyPanelController::showPageProperties(const std::string &page_id)
  320. {
  321. ui_.propertyStack->setCurrentWidget(ui_.pagePropertiesPage);
  322. selected_control_id_.clear();
  323. const HmiPage *page = hmi_editor_service_.findPage(page_id);
  324. const bool has_page = page != nullptr;
  325. ui_.pageWidthSpinBox->setRange(
  326. ProjectLimits::kMinimumHmiPageWidth,
  327. ProjectLimits::kMaximumHmiPageWidth);
  328. ui_.pageHeightSpinBox->setRange(
  329. ProjectLimits::kMinimumHmiPageHeight,
  330. ProjectLimits::kMaximumHmiPageHeight);
  331. ui_.selectionValueLabel->setText(
  332. has_page ? fromUtf8(page->name) : QObject::tr("未选择"));
  333. ui_.pageWidthSpinBox->setEnabled(has_page);
  334. ui_.pageHeightSpinBox->setEnabled(has_page);
  335. ui_.applyPagePropertiesButton->setEnabled(has_page);
  336. if (!has_page)
  337. {
  338. ui_.pageWidthSpinBox->setValue(ProjectLimits::kDefaultHmiPageWidth);
  339. ui_.pageHeightSpinBox->setValue(ProjectLimits::kDefaultHmiPageHeight);
  340. return;
  341. }
  342. ui_.pageWidthSpinBox->setValue(page->width);
  343. ui_.pageHeightSpinBox->setValue(page->height);
  344. }
  345. void PropertyPanelController::showLogicNodeProperties(const std::string &node_id)
  346. {
  347. ui_.propertyStack->setCurrentWidget(ui_.logicPropertiesPage);
  348. selected_logic_node_id_ = node_id;
  349. const LogicNode *node = logic_editor_service_.findNode(
  350. current_logic_id_(), node_id);
  351. const bool has_node = node != nullptr;
  352. ui_.selectionValueLabel->setText(
  353. has_node ? fromUtf8(node->id) : QObject::tr("未选择"));
  354. for (QWidget *widget : {static_cast<QWidget *>(ui_.logicNodeIdEdit),
  355. static_cast<QWidget *>(ui_.logicAddressSpinBox),
  356. static_cast<QWidget *>(ui_.logicModeComboBox),
  357. static_cast<QWidget *>(ui_.logicComparisonComboBox),
  358. static_cast<QWidget *>(ui_.logicValueSpinBox),
  359. static_cast<QWidget *>(ui_.logicPresetSpinBox),
  360. static_cast<QWidget *>(ui_.applyLogicPropertiesButton)})
  361. {
  362. widget->setEnabled(has_node);
  363. }
  364. ui_.logicNodeTypeLabel->setEnabled(has_node);
  365. ui_.logicAddressAreaLabel->setEnabled(has_node);
  366. if (!has_node)
  367. {
  368. ui_.logicNodeIdEdit->clear();
  369. ui_.logicNodeTypeLabel->clear();
  370. ui_.logicAddressAreaLabel->clear();
  371. ui_.logicModeComboBox->clear();
  372. ui_.logicPresetSpinBox->setValue(TonNodeConfig::kMinimumPresetMs);
  373. return;
  374. }
  375. ui_.logicNodeIdEdit->setText(fromUtf8(node->id));
  376. ui_.logicModeComboBox->clear();
  377. ui_.logicComparisonComboBox->setEnabled(false);
  378. ui_.logicValueSpinBox->setEnabled(false);
  379. ui_.logicPresetSpinBox->setEnabled(false);
  380. if (const auto *contact = std::get_if<ContactNodeConfig>(&node->config))
  381. {
  382. ui_.logicNodeTypeLabel->setText(QObject::tr("触点"));
  383. ui_.logicAddressAreaLabel->setText(QStringLiteral("M"));
  384. ui_.logicAddressSpinBox->setValue(contact->address.index());
  385. ui_.logicModeComboBox->addItem(
  386. QObject::tr("常开"), static_cast<int>(ContactMode::NormallyOpen));
  387. ui_.logicModeComboBox->addItem(
  388. QObject::tr("常闭"), static_cast<int>(ContactMode::NormallyClosed));
  389. ui_.logicModeComboBox->setCurrentIndex(
  390. ui_.logicModeComboBox->findData(static_cast<int>(contact->mode)));
  391. ui_.logicModeComboBox->setEnabled(true);
  392. }
  393. else if (const auto *edge = std::get_if<EdgeContactNodeConfig>(&node->config))
  394. {
  395. ui_.logicNodeTypeLabel->setText(QObject::tr("边沿触点"));
  396. ui_.logicAddressAreaLabel->setText(QStringLiteral("M"));
  397. ui_.logicAddressSpinBox->setValue(edge->address.index());
  398. ui_.logicModeComboBox->addItem(
  399. QObject::tr("上升沿"), static_cast<int>(EdgeMode::Rising));
  400. ui_.logicModeComboBox->addItem(
  401. QObject::tr("下降沿"), static_cast<int>(EdgeMode::Falling));
  402. ui_.logicModeComboBox->setCurrentIndex(
  403. ui_.logicModeComboBox->findData(static_cast<int>(edge->mode)));
  404. ui_.logicModeComboBox->setEnabled(true);
  405. }
  406. else if (const auto *timer = std::get_if<TimerContactNodeConfig>(&node->config))
  407. {
  408. ui_.logicNodeTypeLabel->setText(QObject::tr("T 触点"));
  409. ui_.logicAddressAreaLabel->setText(QStringLiteral("T"));
  410. ui_.logicAddressSpinBox->setValue(timer->address.index());
  411. ui_.logicModeComboBox->addItem(
  412. QObject::tr("常开"), static_cast<int>(ContactMode::NormallyOpen));
  413. ui_.logicModeComboBox->addItem(
  414. QObject::tr("常闭"), static_cast<int>(ContactMode::NormallyClosed));
  415. ui_.logicModeComboBox->setCurrentIndex(
  416. ui_.logicModeComboBox->findData(static_cast<int>(timer->mode)));
  417. ui_.logicModeComboBox->setEnabled(true);
  418. }
  419. else if (const auto *counter_contact =
  420. std::get_if<CounterContactNodeConfig>(&node->config))
  421. {
  422. ui_.logicNodeTypeLabel->setText(QObject::tr("C 触点"));
  423. ui_.logicAddressAreaLabel->setText(QStringLiteral("C"));
  424. ui_.logicAddressSpinBox->setValue(counter_contact->address.index());
  425. ui_.logicModeComboBox->addItem(
  426. QObject::tr("常开"), static_cast<int>(ContactMode::NormallyOpen));
  427. ui_.logicModeComboBox->addItem(
  428. QObject::tr("常闭"), static_cast<int>(ContactMode::NormallyClosed));
  429. ui_.logicModeComboBox->setCurrentIndex(ui_.logicModeComboBox->findData(
  430. static_cast<int>(counter_contact->mode)));
  431. ui_.logicModeComboBox->setEnabled(true);
  432. }
  433. else if (const auto *coil = std::get_if<CoilNodeConfig>(&node->config))
  434. {
  435. ui_.logicNodeTypeLabel->setText(QObject::tr("线圈"));
  436. ui_.logicAddressAreaLabel->setText(QStringLiteral("M"));
  437. ui_.logicAddressSpinBox->setValue(coil->address.index());
  438. ui_.logicModeComboBox->addItem(
  439. QObject::tr("普通"), static_cast<int>(CoilMode::Normal));
  440. ui_.logicModeComboBox->addItem(
  441. QObject::tr("置位"), static_cast<int>(CoilMode::Set));
  442. ui_.logicModeComboBox->addItem(
  443. QObject::tr("复位"), static_cast<int>(CoilMode::Reset));
  444. ui_.logicModeComboBox->setCurrentIndex(
  445. ui_.logicModeComboBox->findData(static_cast<int>(coil->mode)));
  446. ui_.logicModeComboBox->setEnabled(true);
  447. }
  448. else if (const auto *compare = std::get_if<CompareNodeConfig>(&node->config))
  449. {
  450. ui_.logicNodeTypeLabel->setText(QObject::tr("D 值比较"));
  451. ui_.logicAddressAreaLabel->setText(QStringLiteral("D"));
  452. ui_.logicAddressSpinBox->setValue(compare->address.index());
  453. ui_.logicModeComboBox->addItem(QObject::tr("不适用"));
  454. ui_.logicModeComboBox->setEnabled(false);
  455. ui_.logicComparisonComboBox->setEnabled(true);
  456. ui_.logicComparisonComboBox->setCurrentIndex(
  457. ui_.logicComparisonComboBox->findData(
  458. static_cast<int>(compare->comparison)));
  459. ui_.logicValueSpinBox->setEnabled(true);
  460. ui_.logicValueSpinBox->setValue(compare->value);
  461. }
  462. else if (const auto *ton = std::get_if<TonNodeConfig>(&node->config))
  463. {
  464. ui_.logicNodeTypeLabel->setText(QObject::tr("TON"));
  465. ui_.logicAddressAreaLabel->setText(QStringLiteral("T"));
  466. ui_.logicAddressSpinBox->setValue(ton->address.index());
  467. ui_.logicModeComboBox->addItem(QObject::tr("非保持接通延时"));
  468. ui_.logicModeComboBox->setEnabled(false);
  469. ui_.logicPresetSpinBox->setEnabled(true);
  470. ui_.logicPresetSpinBox->setValue(ton->presetMs);
  471. }
  472. else if (const auto *counter = std::get_if<CounterNodeConfig>(&node->config))
  473. {
  474. ui_.logicNodeTypeLabel->setText(
  475. counter->mode == CounterMode::Up ? QStringLiteral("CTU")
  476. : QStringLiteral("CTD"));
  477. ui_.logicAddressAreaLabel->setText(QStringLiteral("C"));
  478. ui_.logicAddressSpinBox->setValue(counter->address.index());
  479. ui_.logicAddressSpinBox->setEnabled(false);
  480. ui_.logicModeComboBox->addItem(QObject::tr("使用配置对话框编辑"));
  481. ui_.logicModeComboBox->setEnabled(false);
  482. }
  483. else if (const auto *move = std::get_if<MoveNodeConfig>(&node->config))
  484. {
  485. ui_.logicNodeTypeLabel->setText(QStringLiteral("MOVE"));
  486. ui_.logicAddressAreaLabel->setText(QStringLiteral("D"));
  487. ui_.logicAddressSpinBox->setValue(move->destination.index());
  488. ui_.logicAddressSpinBox->setEnabled(false);
  489. ui_.logicModeComboBox->addItem(QObject::tr("使用配置对话框编辑"));
  490. ui_.logicModeComboBox->setEnabled(false);
  491. }
  492. else
  493. {
  494. const auto &arithmetic = std::get<ArithmeticNodeConfig>(node->config);
  495. ui_.logicNodeTypeLabel->setText(
  496. arithmetic.operation == ArithmeticOperation::Add
  497. ? QStringLiteral("ADD") : QStringLiteral("SUB"));
  498. ui_.logicAddressAreaLabel->setText(QStringLiteral("D"));
  499. ui_.logicAddressSpinBox->setValue(arithmetic.destination.index());
  500. ui_.logicAddressSpinBox->setEnabled(false);
  501. ui_.logicModeComboBox->addItem(QObject::tr("使用配置对话框编辑"));
  502. ui_.logicModeComboBox->setEnabled(false);
  503. }
  504. if (!node->configured)
  505. {
  506. ui_.logicNodeTypeLabel->setText(QObject::tr("待配置:%1").arg(
  507. ui_.logicNodeTypeLabel->text()));
  508. }
  509. }
  510. void PropertyPanelController::addHmiControl(HmiControlType type)
  511. {
  512. const HmiEditorResult result = hmi_editor_service_.addControl(
  513. current_page_id_(), type);
  514. if (!result.succeeded)
  515. {
  516. reportFailure(QObject::tr("添加控件"), result.message);
  517. return;
  518. }
  519. hmi_editor_widget_->reloadPage();
  520. hmi_editor_widget_->selectControl(result.id);
  521. showControlProperties(result.id);
  522. refresh_project_ui_();
  523. const HmiControlDescriptor *descriptor = findHmiControlDescriptor(type);
  524. const QString display_name = descriptor == nullptr
  525. ? QObject::tr("未知控件")
  526. : QString::fromUtf8(descriptor->displayName);
  527. status_reporter_(QObject::tr("已添加%1").arg(display_name), 3000);
  528. }
  529. void PropertyPanelController::deleteSelectedControl()
  530. {
  531. if (hmi_editor_widget_ == nullptr)
  532. {
  533. return;
  534. }
  535. const std::vector<std::string> selected_ids =
  536. hmi_editor_widget_->selectedControlIds();
  537. if (selected_ids.empty())
  538. {
  539. return;
  540. }
  541. const HmiEditorResult result = hmi_editor_service_.removeControls(
  542. current_page_id_(), selected_ids);
  543. if (!result.succeeded)
  544. {
  545. reportFailure(QObject::tr("删除控件"), result.message);
  546. return;
  547. }
  548. selected_control_id_.clear();
  549. hmi_editor_widget_->reloadPage();
  550. showControlProperties({});
  551. refresh_project_ui_();
  552. }
  553. void PropertyPanelController::applySelectedControlProperties()
  554. {
  555. const HmiControl *old_control = hmi_editor_service_.findControl(
  556. current_page_id_(), selected_control_id_);
  557. if (old_control == nullptr)
  558. {
  559. return;
  560. }
  561. HmiControl control = *old_control;
  562. control.id = toUtf8(ui_.controlIdEdit->text());
  563. control.text = toUtf8(ui_.controlTextEdit->text());
  564. control.bounds.x = ui_.controlXSpinBox->value();
  565. control.bounds.y = ui_.controlYSpinBox->value();
  566. control.bounds.width = ui_.controlWidthSpinBox->value();
  567. control.bounds.height = ui_.controlHeightSpinBox->value();
  568. const HmiControlDescriptor *descriptor =
  569. findHmiControlDescriptor(control.type);
  570. if (descriptor == nullptr
  571. || descriptor->bindingKind == HmiBindingKind::None)
  572. {
  573. control.binding.reset();
  574. }
  575. else
  576. {
  577. const int binding_area = ui_.bindingAreaComboBox->currentData().toInt();
  578. if (binding_area < 0)
  579. {
  580. control.binding.reset();
  581. }
  582. else
  583. {
  584. const RegisterArea area = binding_area == 0
  585. ? RegisterArea::M : RegisterArea::D;
  586. control.binding = RegisterAddress{
  587. area, ui_.bindingIndexSpinBox->value()};
  588. }
  589. }
  590. if (control.type == HmiControlType::Button)
  591. {
  592. control.buttonOperation = static_cast<HmiButtonOperation>(
  593. ui_.buttonOperationComboBox->currentData().toInt());
  594. }
  595. if (control.type == HmiControlType::PageJump)
  596. {
  597. control.pageJump = HmiPageJumpConfig{
  598. toUtf8(ui_.targetPageComboBox->currentData().toString())};
  599. }
  600. if (control.type == HmiControlType::ProgressBar)
  601. {
  602. control.progressBar = HmiProgressBarConfig{
  603. static_cast<std::int16_t>(ui_.progressMinimumSpinBox->value()),
  604. static_cast<std::int16_t>(ui_.progressMaximumSpinBox->value()),
  605. ui_.progressShowValueCheckBox->isChecked()};
  606. }
  607. const std::string text_color = toUtf8(ui_.textColorEdit->text());
  608. if (text_color.empty())
  609. {
  610. control.properties.erase(HmiAppearanceProperty::kTextColor);
  611. }
  612. else
  613. {
  614. control.properties[HmiAppearanceProperty::kTextColor] = text_color;
  615. }
  616. control.properties[HmiAppearanceProperty::kFontSize] = std::to_string(
  617. ui_.fontSizeSpinBox->value());
  618. control.properties[HmiAppearanceProperty::kFontBold] =
  619. ui_.fontBoldCheckBox->isChecked() ? "true" : "false";
  620. control.properties[HmiAppearanceProperty::kFontItalic] =
  621. ui_.fontItalicCheckBox->isChecked() ? "true" : "false";
  622. const HmiEditorResult result = hmi_editor_service_.updateControl(
  623. current_page_id_(), selected_control_id_, control);
  624. if (!result.succeeded)
  625. {
  626. reportFailure(QObject::tr("应用属性"), result.message);
  627. return;
  628. }
  629. const std::string updated_control_id = result.id;
  630. hmi_editor_widget_->reloadPage();
  631. selected_control_id_ = updated_control_id;
  632. hmi_editor_widget_->selectControl(updated_control_id);
  633. showControlProperties(updated_control_id);
  634. refresh_project_ui_();
  635. status_reporter_(QObject::tr("控件属性已更新"), 3000);
  636. }
  637. void PropertyPanelController::applySelectedPageProperties()
  638. {
  639. const std::string page_id = current_page_id_();
  640. if (hmi_editor_service_.findPage(page_id) == nullptr)
  641. {
  642. return;
  643. }
  644. const HmiEditorResult result = hmi_editor_service_.resizePage(
  645. page_id,
  646. ui_.pageWidthSpinBox->value(),
  647. ui_.pageHeightSpinBox->value());
  648. if (!result.succeeded)
  649. {
  650. reportFailure(QObject::tr("应用页面尺寸"), result.message);
  651. return;
  652. }
  653. if (hmi_editor_widget_ != nullptr)
  654. {
  655. hmi_editor_widget_->reloadPage();
  656. }
  657. showPageProperties(page_id);
  658. refresh_project_ui_();
  659. status_reporter_(QObject::tr("页面尺寸已更新"), 3000);
  660. }
  661. void PropertyPanelController::chooseTextColor()
  662. {
  663. QColor initial_color(ui_.textColorEdit->text());
  664. if (!initial_color.isValid())
  665. {
  666. initial_color = QColor(QStringLiteral("#24313b"));
  667. }
  668. const QColor color = QColorDialog::getColor(
  669. initial_color, &parent_, QObject::tr("选择字体颜色"));
  670. if (color.isValid())
  671. {
  672. ui_.textColorEdit->setText(color.name(QColor::HexRgb));
  673. ui_.textColorButton->setStyleSheet(
  674. QStringLiteral("background-color: %1;").arg(
  675. color.name(QColor::HexRgb)));
  676. }
  677. }
  678. void PropertyPanelController::applySelectedLogicNodeProperties()
  679. {
  680. const LogicNode *node = logic_editor_service_.findNode(
  681. current_logic_id_(), selected_logic_node_id_);
  682. if (node == nullptr)
  683. {
  684. return;
  685. }
  686. LogicNodeConfig config = node->config;
  687. if (std::holds_alternative<ContactNodeConfig>(config))
  688. {
  689. config = ContactNodeConfig{
  690. RegisterAddress{RegisterArea::M, ui_.logicAddressSpinBox->value()},
  691. static_cast<ContactMode>(ui_.logicModeComboBox->currentData().toInt())};
  692. }
  693. else if (std::holds_alternative<EdgeContactNodeConfig>(config))
  694. {
  695. config = EdgeContactNodeConfig{
  696. RegisterAddress{RegisterArea::M, ui_.logicAddressSpinBox->value()},
  697. static_cast<EdgeMode>(ui_.logicModeComboBox->currentData().toInt())};
  698. }
  699. else if (std::holds_alternative<TimerContactNodeConfig>(config))
  700. {
  701. config = TimerContactNodeConfig{
  702. TimerAddress{ui_.logicAddressSpinBox->value()},
  703. static_cast<ContactMode>(ui_.logicModeComboBox->currentData().toInt())};
  704. }
  705. else if (std::holds_alternative<CounterContactNodeConfig>(config))
  706. {
  707. config = CounterContactNodeConfig{
  708. CounterAddress{ui_.logicAddressSpinBox->value()},
  709. static_cast<ContactMode>(ui_.logicModeComboBox->currentData().toInt())};
  710. }
  711. else if (std::holds_alternative<CoilNodeConfig>(config))
  712. {
  713. config = CoilNodeConfig{
  714. RegisterAddress{RegisterArea::M, ui_.logicAddressSpinBox->value()},
  715. static_cast<CoilMode>(ui_.logicModeComboBox->currentData().toInt())};
  716. }
  717. else if (std::holds_alternative<CompareNodeConfig>(config))
  718. {
  719. config = CompareNodeConfig{
  720. RegisterAddress{RegisterArea::D, ui_.logicAddressSpinBox->value()},
  721. static_cast<ComparisonOperator>(
  722. ui_.logicComparisonComboBox->currentData().toInt()),
  723. static_cast<std::int16_t>(ui_.logicValueSpinBox->value())};
  724. }
  725. else if (std::holds_alternative<TonNodeConfig>(config))
  726. {
  727. config = TonNodeConfig{
  728. TimerAddress{ui_.logicAddressSpinBox->value()},
  729. ui_.logicPresetSpinBox->value()};
  730. }
  731. else
  732. {
  733. LogicInstructionDialog dialog(node->config, &parent_);
  734. if (dialog.exec() != QDialog::Accepted)
  735. {
  736. return;
  737. }
  738. config = dialog.config();
  739. }
  740. const LogicEditorResult result = logic_editor_service_.updateNodeConfig(
  741. current_logic_id_(), selected_logic_node_id_, config);
  742. if (!result.succeeded)
  743. {
  744. reportFailure(QObject::tr("应用逻辑属性"), result.message);
  745. return;
  746. }
  747. logic_editor_widget_->reloadLogic();
  748. logic_editor_widget_->selectNode(selected_logic_node_id_);
  749. showLogicNodeProperties(selected_logic_node_id_);
  750. refresh_project_ui_();
  751. status_reporter_(QObject::tr("逻辑节点属性已更新,节点已配置"), 3000);
  752. }
  753. void PropertyPanelController::handleHmiEditorError(const QString &message) const
  754. {
  755. status_reporter_(message, 5000);
  756. }
  757. void PropertyPanelController::handleLogicEditorError(const QString &message) const
  758. {
  759. status_reporter_(message, 5000);
  760. }
  761. void PropertyPanelController::reportFailure(
  762. const QString &action, const std::string &message) const
  763. {
  764. result_reporter_(action, fromUtf8(message), false);
  765. }