/* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include #include #include "channels.hpp" #include #include #include #include #include #include using std::make_shared; using std::map; using std::out_of_range; using std::shared_ptr; using std::unordered_set; using std::vector; using std::weak_ptr; using pv::data::SignalBase; using pv::data::Logic; using pv::data::LogicSegment; using sigrok::Channel; using sigrok::ChannelGroup; using sigrok::Device; namespace pv { namespace popups { Channels::Channels(Session &session, QWidget *parent) : Popup(parent), session_(session), updating_channels_(false), enable_all_channels_(tr("All"), this), disable_all_channels_(tr("All"), this), enable_all_logic_channels_(tr("Logic"), this), disable_all_logic_channels_(tr("Logic"), this), enable_all_analog_channels_(tr("Analog"), this), disable_all_analog_channels_(tr("Analog"), this), enable_all_named_channels_(tr("Named"), this), disable_all_unnamed_channels_(tr("Unnamed"), this), enable_all_changing_channels_(tr("Changing"), this), disable_all_non_changing_channels_(tr("Non-changing"), this), check_box_mapper_(this) { // Create the layout setLayout(&layout_); const shared_ptr device = session_.device()->device(); assert(device); // Collect a set of signals map, shared_ptr > signal_map; unordered_set< shared_ptr > sigs; for (const shared_ptr& b : session_.signalbases()) sigs.insert(b); for (const shared_ptr &sig : sigs) signal_map[sig->channel()] = sig; // Populate channel groups for (auto& entry : device->channel_groups()) { const shared_ptr group = entry.second; // Make a set of signals and remove these signals from the signal map vector< shared_ptr > group_sigs; for (auto& channel : group->channels()) { const auto iter = signal_map.find(channel); if (iter == signal_map.end()) break; group_sigs.push_back((*iter).second); signal_map.erase(iter); } populate_group(group, group_sigs); } // Make a vector of the remaining channels vector< shared_ptr > global_analog_sigs, global_logic_sigs; for (auto& channel : device->channels()) { const map, shared_ptr >:: const_iterator iter = signal_map.find(channel); if (iter != signal_map.end()) { const shared_ptr signal = (*iter).second; if (signal->type() == SignalBase::AnalogChannel) global_analog_sigs.push_back(signal); else global_logic_sigs.push_back(signal); } } // Create the groups for the ungrouped channels populate_group(nullptr, global_logic_sigs); populate_group(nullptr, global_analog_sigs); // Create the enable/disable all buttons connect(&enable_all_channels_, SIGNAL(clicked()), this, SLOT(enable_all_channels())); connect(&disable_all_channels_, SIGNAL(clicked()), this, SLOT(disable_all_channels())); connect(&enable_all_logic_channels_, SIGNAL(clicked()), this, SLOT(enable_all_logic_channels())); connect(&disable_all_logic_channels_, SIGNAL(clicked()), this, SLOT(disable_all_logic_channels())); connect(&enable_all_analog_channels_, SIGNAL(clicked()), this, SLOT(enable_all_analog_channels())); connect(&disable_all_analog_channels_, SIGNAL(clicked()), this, SLOT(disable_all_analog_channels())); connect(&enable_all_named_channels_, SIGNAL(clicked()), this, SLOT(enable_all_named_channels())); connect(&disable_all_unnamed_channels_, SIGNAL(clicked()), this, SLOT(disable_all_unnamed_channels())); connect(&enable_all_changing_channels_, SIGNAL(clicked()), this, SLOT(enable_all_changing_channels())); connect(&disable_all_non_changing_channels_, SIGNAL(clicked()), this, SLOT(disable_all_non_changing_channels())); QLabel *label1 = new QLabel(tr("Disable: ")); filter_buttons_bar_.addWidget(label1, 0, 0); filter_buttons_bar_.addWidget(&disable_all_channels_, 0, 1); filter_buttons_bar_.addWidget(&disable_all_logic_channels_, 0, 2); filter_buttons_bar_.addWidget(&disable_all_analog_channels_, 0, 3); filter_buttons_bar_.addWidget(&disable_all_unnamed_channels_, 0, 4); filter_buttons_bar_.addWidget(&disable_all_non_changing_channels_, 0, 5); QLabel *label2 = new QLabel(tr("Enable: ")); filter_buttons_bar_.addWidget(label2, 1, 0); filter_buttons_bar_.addWidget(&enable_all_channels_, 1, 1); filter_buttons_bar_.addWidget(&enable_all_logic_channels_, 1, 2); filter_buttons_bar_.addWidget(&enable_all_analog_channels_, 1, 3); filter_buttons_bar_.addWidget(&enable_all_named_channels_, 1, 4); filter_buttons_bar_.addWidget(&enable_all_changing_channels_, 1, 5); layout_.addItem(new QSpacerItem(0, 15, QSizePolicy::Expanding, QSizePolicy::Expanding)); layout_.addRow(&filter_buttons_bar_); // Connect the check-box signal mapper connect(&check_box_mapper_, SIGNAL(mapped(QWidget*)), this, SLOT(on_channel_checked(QWidget*))); } void Channels::set_all_channels(bool set) { updating_channels_ = true; for (auto& entry : check_box_signal_map_) { QCheckBox *cb = entry.first; const shared_ptr sig = entry.second; assert(sig); sig->set_enabled(set); cb->setChecked(set); } updating_channels_ = false; } void Channels::enable_channels_conditionally( function)> cond_func) { updating_channels_ = true; for (auto& entry : check_box_signal_map_) { QCheckBox *cb = entry.first; const shared_ptr sig = entry.second; assert(sig); if (cond_func(sig)) { sig->set_enabled(true); cb->setChecked(true); } } updating_channels_ = false; } void Channels::disable_channels_conditionally( function)> cond_func) { updating_channels_ = true; for (auto& entry : check_box_signal_map_) { QCheckBox *cb = entry.first; const shared_ptr sig = entry.second; assert(sig); if (cond_func(sig)) { sig->set_enabled(false); cb->setChecked(false); } } updating_channels_ = false; } void Channels::populate_group(shared_ptr group, const vector< shared_ptr > sigs) { using pv::binding::Device; // Only bind options if this is a group. We don't do it for general // options, because these properties are shown in the device config // popup. shared_ptr binding; if (group) binding = make_shared(group); QHBoxLayout* group_layout = new QHBoxLayout(); layout_.addRow(group_layout); // Create a title if the group is going to have any content if ((!sigs.empty() || (binding && !binding->properties().empty())) && group) { QLabel *label = new QLabel( QString("

%1

").arg(group->name().c_str())); group_layout->addWidget(label); group_label_map_[group] = label; } // Create the channel group grid int row = 0, col = 0; QGridLayout *const grid = new QGridLayout(); vector group_checkboxes, this_row; for (const shared_ptr& sig : sigs) { assert(sig); QCheckBox *const checkbox = new QCheckBox(sig->display_name()); check_box_mapper_.setMapping(checkbox, checkbox); connect(checkbox, SIGNAL(toggled(bool)), &check_box_mapper_, SLOT(map())); grid->addWidget(checkbox, row, col); group_checkboxes.push_back(checkbox); this_row.push_back(checkbox); check_box_signal_map_[checkbox] = sig; weak_ptr weak_sig(sig); connect(checkbox, &QCheckBox::toggled, [weak_sig](bool state) { auto sig = weak_sig.lock(); assert(sig); sig->set_enabled(state); }); if ((++col >= 8 || &sig == &sigs.back())) { // Show buttons if there's more than one row if (sigs.size() > 8) { QPushButton *row_enable_button = new QPushButton(tr("All"), this); grid->addWidget(row_enable_button, row, 8); connect(row_enable_button, &QPushButton::clicked, row_enable_button, [this_row]() { for (QCheckBox *box : this_row) box->setChecked(true); }); QPushButton *row_disable_button = new QPushButton(tr("None"), this); connect(row_disable_button, &QPushButton::clicked, row_disable_button, [this_row]() { for (QCheckBox *box : this_row) box->setChecked(false); }); grid->addWidget(row_disable_button, row, 9); } this_row.clear(); col = 0, row++; } } layout_.addRow(grid); if (sigs.size() > 1) { // Create enable all/none buttons QPushButton *btn_enable_all, *btn_disable_all; btn_enable_all = new QPushButton(tr("All")); btn_disable_all = new QPushButton(tr("None")); group_layout->addWidget(btn_enable_all); group_layout->addWidget(btn_disable_all); connect(btn_enable_all, &QPushButton::clicked, btn_enable_all, [group_checkboxes](){ for (QCheckBox *box: group_checkboxes) box->setChecked(true); }); connect(btn_disable_all, &QPushButton::clicked, btn_disable_all, [group_checkboxes](){ for (QCheckBox *box: group_checkboxes) box->setChecked(false); }); } // Create the channel group options if (binding) { binding->add_properties_to_form(&layout_, true); group_bindings_.push_back(binding); } } void Channels::showEvent(QShowEvent *event) { pv::widgets::Popup::showEvent(event); const shared_ptr device = session_.device()->device(); assert(device); // Update group labels for (auto& entry : device->channel_groups()) { const shared_ptr group = entry.second; try { QLabel* label = group_label_map_.at(group); label->setText(QString("

%1

").arg(group->name().c_str())); } catch (out_of_range&) { // Do nothing } } updating_channels_ = true; for (auto& entry : check_box_signal_map_) { QCheckBox *cb = entry.first; const shared_ptr sig = entry.second; assert(sig); // Update the check box cb->setChecked(sig->enabled()); cb->setText(sig->display_name()); } updating_channels_ = false; } void Channels::on_channel_checked(QWidget *widget) { if (updating_channels_) return; QCheckBox *const check_box = (QCheckBox*)widget; assert(check_box); // Look up the signal of this check-box map< QCheckBox*, shared_ptr >::const_iterator iter = check_box_signal_map_.find((QCheckBox*)check_box); assert(iter != check_box_signal_map_.end()); const shared_ptr s = (*iter).second; assert(s); s->set_enabled(check_box->isChecked()); } void Channels::enable_all_channels() { set_all_channels(true); } void Channels::disable_all_channels() { set_all_channels(false); } void Channels::enable_all_logic_channels() { enable_channels_conditionally([](const shared_ptr signal) { return signal->type() == SignalBase::LogicChannel; }); } void Channels::disable_all_logic_channels() { disable_channels_conditionally([](const shared_ptr signal) { return signal->type() == SignalBase::LogicChannel; }); } void Channels::enable_all_analog_channels() { enable_channels_conditionally([](const shared_ptr signal) { return signal->type() == SignalBase::AnalogChannel; }); } void Channels::disable_all_analog_channels() { disable_channels_conditionally([](const shared_ptr signal) { return signal->type() == SignalBase::AnalogChannel; }); } void Channels::enable_all_named_channels() { enable_channels_conditionally([](const shared_ptr signal) { return signal->name() != signal->internal_name(); }); } void Channels::disable_all_unnamed_channels() { disable_channels_conditionally([](const shared_ptr signal) { return signal->name() == signal->internal_name(); }); } void Channels::enable_all_changing_channels() { enable_channels_conditionally([](const shared_ptr signal) { // Never enable channels without sample data if (!signal->has_samples()) return false; // Non-logic channels are considered to always have a signal if (signal->type() != SignalBase::LogicChannel) return true; const shared_ptr logic = signal->logic_data(); assert(logic); // If any of the segments has edges, enable this channel for (shared_ptr segment : logic->logic_segments()) { vector edges; segment->get_subsampled_edges(edges, 0, segment->get_sample_count() - 1, LogicSegment::MipMapScaleFactor, signal->index()); if (edges.size() > 2) return true; } // No edges detected in any of the segments return false; }); } void Channels::disable_all_non_changing_channels() { disable_channels_conditionally([](const shared_ptr signal) { // Always disable channels without sample data if (!signal->has_samples()) return true; // Non-logic channels are considered to always have a signal if (signal->type() != SignalBase::LogicChannel) return false; const shared_ptr logic = signal->logic_data(); assert(logic); // If any of the segments has edges, leave this channel enabled for (shared_ptr segment : logic->logic_segments()) { vector edges; segment->get_subsampled_edges(edges, 0, segment->get_sample_count() - 1, LogicSegment::MipMapScaleFactor, signal->index()); if (edges.size() > 2) return false; } // No edges detected in any of the segments return true; }); } } // namespace popups } // namespace pv