Render `col` of `tw` as an exclusive radio group (one QRadioButton per row), check `checked_row`, and wire each radio's click to emit_row(its current row). Idempotent: reuses existing radios and only syncs the checked state, so it can run on every data apply without flicker. Rows that shrink away have their cell widgets destroyed by QTableWidget::setRowCount, which auto-removes them from the QButt
| 180 | // QButtonGroup. `clicked` fires on user interaction only (NOT on setChecked), so |
| 181 | // the checked-state sync below never feeds back as a spurious event. |
| 182 | static void applyTableRadioColumn( |
| 183 | QTableWidget* tw, int col, int checked_row, const std::function<void(int)>& emit_row) { |
| 184 | if (col < 0 || col >= tw->columnCount()) { |
| 185 | return; |
| 186 | } |
| 187 | auto* group = tw->findChild<QButtonGroup*>(QStringLiteral("pj_radio_group"), Qt::FindDirectChildrenOnly); |
| 188 | if (group == nullptr) { |
| 189 | group = new QButtonGroup(tw); |
| 190 | group->setObjectName(QStringLiteral("pj_radio_group")); |
| 191 | group->setExclusive(true); |
| 192 | } |
| 193 | for (int r = 0; r < tw->rowCount(); ++r) { |
| 194 | auto* radio = qobject_cast<QRadioButton*>(tw->cellWidget(r, col)); |
| 195 | if (radio == nullptr) { |
| 196 | radio = new QRadioButton(tw); |
| 197 | radio->setStyleSheet(QStringLiteral("QRadioButton { margin-left: 8px; }")); |
| 198 | tw->setCellWidget(r, col, radio); |
| 199 | group->addButton(radio); |
| 200 | // Resolve the row at click time: rows renumber as the user adds/removes |
| 201 | // series, so a row index captured at creation would go stale. |
| 202 | QObject::connect(radio, &QRadioButton::clicked, radio, [emit_row, tw, col, radio]() { |
| 203 | for (int rr = 0; rr < tw->rowCount(); ++rr) { |
| 204 | if (tw->cellWidget(rr, col) == radio) { |
| 205 | emit_row(rr); |
| 206 | return; |
| 207 | } |
| 208 | } |
| 209 | }); |
| 210 | } |
| 211 | radio->setChecked(r == checked_row); |
| 212 | } |
| 213 | |
| 214 | // Keep the radio column just wide enough for the button, and stretch the first |
| 215 | // non-radio column instead. installTreeLikeHeader stretches column 0 by default, |
| 216 | // which would over-widen the radio when it is the first column. |
| 217 | auto* header = tw->horizontalHeader(); |
| 218 | header->setSectionResizeMode(col, QHeaderView::Fixed); |
| 219 | tw->setColumnWidth(col, 36); |
| 220 | for (int c = 0; c < tw->columnCount(); ++c) { |
| 221 | if (c != col) { |
| 222 | header->setSectionResizeMode(c, QHeaderView::Stretch); |
| 223 | break; |
| 224 | } |
| 225 | } |
| 226 | } |
| 227 | |
| 228 | // True when `tw`'s header labels already equal `headers`. |
| 229 | static bool tableMatchesHeaders(const QTableWidget* tw, const QStringList& headers) { |
no test coverage detected