Files
templates/tools/dsview/scope-channel.patch
T

153 lines
16 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
--- a/DSView/pv/view/view.h
+++ b/DSView/pv/view/view.h
@@ -64,4 +64,5 @@
namespace view {
+class ScopeTrace; // Виртуальные аналоговые дорожки принадлежат представлению и не меняют аппаратный захват.
class Header;
class DevMode;
@@ -439,4 +440,6 @@
pv::toolbars::SamplingBar *_sampling_bar;
+ std::vector<ScopeTrace*> _scope_traces; // Четыре демонстрационных канала сохраняют настройки видимости в текущей сессии.
+ QAction *_scope_action = nullptr; // Доступ к меню зависит от цифрового режима текущего прибора.
QWidget *_viewcenter;
ViewStatus *_viewbottom;
--- a/DSView/pv/view/view.cpp
+++ b/DSView/pv/view/view.cpp
@@ -42,4 +42,7 @@
#include "lissajoustrace.h"
#include "analogsignal.h"
+#include "scope_trace.h" // Аналоговая эмуляция Zynq отображается вместе с цифровыми каналами.
+#include <QToolButton> // Меню каналов встраивается в существующую прокручиваемую панель управления.
+#include <QMenu> // Флажки позволяют независимо добавить и убрать каждый канал осциллографа.
#include "../sigsession.h"
@@ -202,4 +205,28 @@
connect(_header, SIGNAL(header_updated()),this, SLOT(header_updated()));
+ _trace_view_map[ScopeTrace::Type] = TIME_VIEW; // Виртуальный осциллограф использует ту же шкалу времени и прокрутку, что цифровые сигналы.
+ auto scope_button = new QToolButton(_sampling_bar); // Кнопка принадлежит штатной панели DSView и работает также без SETGUI.
+ scope_button->setObjectName("scope_channel_button"); // Постоянное имя позволяет проверять настоящий интерфейс в нативном тесте.
+ scope_button->setText(QString::fromUtf8("Осциллограф +")); // Название явно обозначает добавление аналоговой дорожки к существующим сигналам.
+ scope_button->setToolTip(QString::fromUtf8("Добавить канал осциллографа: эмуляция Zynq на общей шкале времени")); // Пользователь видит происхождение данных до включения канала.
+ scope_button->setPopupMode(QToolButton::InstantPopup); // Одно нажатие сразу открывает выбор каналов.
+ auto scope_menu = new QMenu(scope_button); // Меню и его действия удаляются вместе с панелью управления.
+ scope_button->setMenu(scope_menu); // Выбор канала выполняется внутри существующего DSView без отдельного окна генератора.
+ for (unsigned channel = 0; channel < 4; ++channel) { // Эмулятор Zynq предоставляет четыре независимых канала CH1…CH4.
+ auto trace = new ScopeTrace(channel); // Сохраняем точные частоты, формы и амплитуды исходного эмулятора.
+ trace->setParent(this); // QObject освобождает дорожку при уничтожении представления без участия аппаратной сессии.
+ _scope_traces.push_back(trace); // Постоянный объект сохраняет имя, цвет и высоту при скрытии и повторном включении.
+ auto action = scope_menu->addAction(QString::fromUtf8("CH%1 · %2 · %3 Гц · ±%4 В").arg(channel + 1).arg((channel & 1) ? QString::fromUtf8("меандр") : QString::fromUtf8("синус")).arg(set_scope::hz[channel]).arg(set_scope::peak[channel])); // Подпись показывает параметры до добавления канала.
+ action->setObjectName(QString("scope_channel_%1").arg(channel + 1)); // Идентификатор связывает действие с конкретным каналом при проверке интерфейса.
+ action->setCheckable(true); // Флажок одновременно показывает состояние и позволяет убрать дорожку повторным нажатием.
+ connect(action, &QAction::toggled, this, [this, trace](bool active) { // Меняем только выбранный аналоговый канал, сохраняя цифровые сигналы и декодеры.
+ trace->set_active(active); // Источник не запускает отдельный захват и не открывает сетевое соединение.
+ signals_changed(nullptr); // Пересчитываем высоту дорожек и диапазон общей вертикальной прокрутки.
+ set_all_update(true); // Обновляем кэш диаграммы после изменения набора дорожек.
+ viewport_update(); // Новый канал появляется сразу, в том числе до первого аппаратного захвата.
+ }); // Контекст View отключает обработчик при закрытии его окна.
+ } // Каждый канал можно включать независимо от остальных трёх.
+ _scope_action = _sampling_bar->addWidget(scope_button); // Кнопка доступна рядом с обычными настройками захвата, включая отдельный запуск DSView.
+ _scope_action->setEnabled(_device_agent->have_instance() && _device_agent->get_work_mode() == LOGIC); // Добавление доступно для цифровых источников, без вмешательства в штатный DSO.
ADD_UI(this);
}
@@ -393,4 +420,8 @@
traces.push_back(t);
}
+
+ if (_device_agent->get_work_mode() == LOGIC && (type == ALL_VIEW || type == TIME_VIEW)) { // Виртуальные аналоги дополняют только цифровую временную область.
+ for (auto trace : _scope_traces) if (trace->enabled()) traces.push_back(trace); // Скрытые каналы не занимают место и не участвуют в обработке мыши.
+ } // Данные устройства, спектр и аппаратный осциллограф сохраняют собственные списки каналов.
auto lissajous = _session->get_lissajous_trace();
@@ -706,4 +737,5 @@
void View::signals_changed(const Trace* eventTrace)
{
+ if (_scope_action) _scope_action->setEnabled(_device_agent->have_instance() && _device_agent->get_work_mode() == LOGIC); // Меню следует за сменой прибора или режима без удаления выбранных демо-каналов.
double actualMargin = SignalMargin;
int total_rows = 0;
@@ -841,4 +873,9 @@
time_traces.push_back(t);
}
+
+ for (auto trace : _scope_traces) { // Аналоговые дорожки участвуют в общей сортировке после перемещения мышью.
+ if (!trace->enabled()) continue; // Скрытый демо-канал не занимает места в компоновке.
+ if (trace->get_view_index() == -1) time_traces.push_back(trace); else all_traces.push_back(trace); // Новый канал появляется сверху, перемещённый сохраняет выбранный порядок среди цифровых сигналов.
+ } // Переупорядочивание не требует добавлять эмуляцию в аппаратный список каналов.
sort(all_traces.begin(), all_traces.end(), compare_trace_view_index);
--- a/DSView/pv/mainwindow.cpp
+++ b/DSView/pv/mainwindow.cpp
@@ -612,4 +612,38 @@
}
+ void MainWindow::checkHostScope() // Проверяет добавление аналоговых дорожек отдельно от сценариев диалогов декодеров.
+ { // Отдельная точка запуска не требует Ethernet-патча и доступна в базовой сборке DSView.
+ if (qEnvironmentVariableIsSet("DSVIEW_HOST_CHECK_SCOPE")) { // Явный проверочный режим использует реальные действия меню, не подключая USB или Ethernet.
+ const auto original_signals = _session->get_signals(); // Копия указателей проверяет сохранность существующей цифровой записи.
+ std::vector<pv::view::Trace*> before; // Список позволяет обнаружить потерю или лишнее добавление любой дорожки.
+ _view->get_traces(ALL_VIEW, before); // Исходное число включает цифровые сигналы и уже добавленные декодеры.
+ const double scale = _view->scale(); // Включение осциллографа обязано сохранить выбранный масштаб времени.
+ QList<QAction*> channels; // Используем те же QAction, которые выбирает пользователь в меню осциллографа.
+ bool passed = true; // Любое несоответствие делает нативный сценарий неуспешным.
+ for (int channel = 1; channel <= 4; ++channel) { // Проверяем независимое включение всех четырёх источников Zynq.
+ auto action = _sampling_bar->findChild<QAction*>(QString("scope_channel_%1").arg(channel)); // Поиск идёт только в панели текущего экземпляра DSView.
+ if (!action) { passed = false; break; } // Отсутствие пользовательского элемента управления не маскируется проверкой только модели.
+ channels.append(action); // Сохраняем реальные действия для проверки удаления и повторного добавления.
+ action->setChecked(true); // Сигнал toggled выполняет производственный путь компоновки и обновления графика.
+ std::vector<pv::view::Trace*> current; // Новый список должен содержать ровно одну дополнительную дорожку на выбранный канал.
+ _view->get_traces(ALL_VIEW, current); // Проверяем состояние того же списка, который рисует Viewport.
+ passed = passed && current.size() == before.size() + channel && _view->scale() == scale; // Добавление не должно терять цифровые каналы или сбрасывать время.
+ } // Все четыре канала одновременно участвуют в итоговой компоновке.
+ for (auto action : channels) action->setChecked(false); // Убираем все добавленные дорожки через пользовательские действия.
+ std::vector<pv::view::Trace*> after; // Повторное чтение обнаруживает оставшиеся скрытые трассы в активном списке.
+ _view->get_traces(ALL_VIEW, after); // Исходная сессия должна восстановиться без перезагрузки захвата.
+ passed = passed && after == before && _session->get_signals() == original_signals; // Сравниваем идентичность объектов, а не только количество каналов.
+ if (channels.size() == 4) { channels[0]->setChecked(true); channels[1]->setChecked(true); } // Итоговый снимок показывает синус и меандр рядом с исходными цифровыми сигналами.
+ _view->set_scale_offset(2e-6, 0); // Две микросекунды на пиксель позволяют визуально проверить форму синуса 1 кГц и меандра 500,3 Гц.
+ QTimer::singleShot(200, this, [this, passed]() { // Даём нативному циклу событий завершить компоновку и перерисовку всех дорожек.
+ const QString screenshot = QString::fromUtf8(qgetenv("DSVIEW_HOST_SCREENSHOT")); // Артефакт сохраняется только по явно указанному тестом пути.
+ const bool rendered = _view->grab().save(screenshot); // Проверяем фактический рендер настоящего Qt-представления, включая цифровые каналы.
+ QFile report(QString::fromUtf8(qgetenv("DSVIEW_HOST_CHECK"))); // Тест получает результат через существующий диагностический канал DSView.
+ if (report.open(QIODevice::WriteOnly | QIODevice::Truncate)) report.write(passed && rendered ? "PASS scope add/remove/digital preservation/render\n" : "FAIL scope integration\n"); // Успех требует корректного поведения меню и сохранённого изображения.
+ }); // Захват и дочерний процесс продолжают работать до штатной команды CLOSE от теста.
+ return; // Проверка осциллографа не запускает посторонние сценарии диалогов декодеров.
+ } // В обычном запуске эта ветка полностью отключена.
+ } // В обычном запуске проверочная функция не вызывается.
+
void MainWindow::on_protocol(bool visible)
{
--- a/DSView/pv/view/header.cpp
+++ b/DSView/pv/view/header.cpp
@@ -316,11 +316,5 @@
std::vector<Trace*> traces;
- for (auto s : _view.session().get_decode_signals()){
- traces.push_back(s);
- }
-
- for (auto s : _view.session().get_signals()){
- traces.push_back(s);
- }
+ _view.get_traces(TIME_VIEW, traces); // Индексы перемещения включают также виртуальные аналоговые дорожки, отсутствующие в аппаратной сессии.
sort(traces.begin(), traces.end(), View::compare_trace_y);
--- a/DSView/pv/mainwindow.h
+++ b/DSView/pv/mainwindow.h
@@ -98,4 +98,5 @@
const char *openHostFile(const QString &path);
void checkHostDecoders();
+ void checkHostScope(); // Нативный сценарий проверяет реальные действия меню и сохранность цифровой записи.
public slots:
--- a/DSView/pv/mainframe.cpp
+++ b/DSView/pv/mainframe.cpp
@@ -769,5 +769,5 @@
QFrame::show();
if (!qgetenv("DSVIEW_HOST_CHECK").isEmpty())
- QTimer::singleShot(3500, _mainWindow, &MainWindow::checkHostDecoders);
+ QTimer::singleShot(3500, _mainWindow, qEnvironmentVariableIsSet("DSVIEW_HOST_CHECK_SCOPE") ? &MainWindow::checkHostScope : &MainWindow::checkHostDecoders); // Явный параметр выбирает проверку осциллографа, сохраняя прежние сценарии декодеров.
return true;
}