cabana: use bootstrap icons (#26981)

* use bootstrap icons

* typo

* build into asset_obj

* add to files_common
pull/27000/head^2
Dean Lee 2 years ago committed by GitHub
parent 351d97ab5a
commit c21d9408a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      SConstruct
  2. 1
      release/files_common
  3. 1
      selfdrive/assets/assets.qrc
  4. 1
      selfdrive/ui/SConscript
  5. 37
      selfdrive/ui/qt/util.cc
  6. 1
      selfdrive/ui/qt/util.h
  7. 4
      tools/cabana/SConscript
  8. 13
      tools/cabana/chartswidget.cc
  9. 5
      tools/cabana/detailwidget.cc
  10. 6
      tools/cabana/signaledit.cc
  11. 15
      tools/cabana/videowidget.cc
  12. 1
      tools/cabana/videowidget.h

@ -282,7 +282,7 @@ Export('envCython')
# Qt build environment
qt_env = env.Clone()
qt_modules = ["Widgets", "Gui", "Core", "Network", "Concurrent", "Multimedia", "Quick", "Qml", "QuickWidgets", "Location", "Positioning", "DBus"]
qt_modules = ["Widgets", "Gui", "Core", "Network", "Concurrent", "Multimedia", "Quick", "Qml", "QuickWidgets", "Location", "Positioning", "DBus", "Xml"]
qt_libs = []
if arch == "Darwin":

@ -436,6 +436,7 @@ third_party/acados/larch64/**
third_party/acados/include/**
third_party/acados/acados_template/**
third_party/bootstrap/**
third_party/qt5/larch64/bin/**
scripts/update_now.sh

@ -1,5 +1,6 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file alias="bootstrap-icons.svg">../../third_party/bootstrap/bootstrap-icons.svg</file>
<file>img_continue_triangle.svg</file>
<file>img_circled_check.svg</file>
<file>img_circled_slash.svg</file>

@ -41,6 +41,7 @@ assets_src = "#selfdrive/assets/assets.qrc"
qt_env.Command(assets, assets_src, f"rcc $SOURCES -o $TARGET")
qt_env.Depends(assets, Glob('#selfdrive/assets/*', exclude=[assets, assets_src, "#selfdrive/assets/assets.o"]))
asset_obj = qt_env.Object("assets", assets)
Export('asset_obj')
# build soundd
qt_env.Program("soundd/_soundd", ["soundd/main.cc", "soundd/sound.cc"], LIBS=qt_libs)

@ -2,11 +2,14 @@
#include <QApplication>
#include <QFile>
#include <QHash>
#include <QJsonDocument>
#include <QJsonObject>
#include <QLayoutItem>
#include <QStyleOption>
#include <QPainterPath>
#include <QTextStream>
#include <QtXml/QDomDocument>
#include "common/params.h"
#include "common/swaglog.h"
@ -218,3 +221,37 @@ QColor interpColor(float xv, std::vector<float> xp, std::vector<QColor> fp) {
);
}
}
static QHash<QString, QByteArray> load_bootstrap_icons() {
QHash<QString, QByteArray> icons;
QFile f(":/bootstrap-icons.svg");
if (f.open(QIODevice::ReadOnly | QIODevice::Text)) {
QDomDocument xml;
xml.setContent(&f);
QDomNode n = xml.documentElement().firstChild();
while (!n.isNull()) {
QDomElement e = n.toElement();
if (!e.isNull() && e.hasAttribute("id")) {
QString svg_str;
QTextStream stream(&svg_str);
n.save(stream, 0);
svg_str.replace("<symbol", "<svg");
svg_str.replace("</symbol>", "</svg>");
icons[e.attribute("id")] = svg_str.toUtf8();
}
n = n.nextSibling();
}
}
return icons;
}
QPixmap bootstrapPixmap(const QString &id) {
static QHash<QString, QByteArray> icons = load_bootstrap_icons();
QPixmap pixmap;
if (auto it = icons.find(id); it != icons.end()) {
pixmap.loadFromData(it.value(), "svg");
}
return pixmap;
}

@ -22,6 +22,7 @@ void swagLogMessageHandler(QtMsgType type, const QMessageLogContext &context, co
void initApp(int argc, char *argv[]);
QWidget* topWidget (QWidget* widget);
QPixmap loadPixmap(const QString &fileName, const QSize &size = {}, Qt::AspectRatioMode aspectRatioMode = Qt::KeepAspectRatio);
QPixmap bootstrapPixmap(const QString &id);
QRect getTextRect(QPainter &p, int flags, const QString &text);
void drawRoundedRect(QPainter &painter, const QRectF &rect, qreal xRadiusTop, qreal yRadiusTop, qreal xRadiusBottom, qreal yRadiusBottom);

@ -1,6 +1,6 @@
import os
Import('env', 'qt_env', 'arch', 'common', 'messaging', 'visionipc', 'replay_lib',
'cereal', 'transformations', 'widgets', 'opendbc')
'cereal', 'transformations', 'widgets', 'opendbc', 'asset_obj')
base_frameworks = qt_env['FRAMEWORKS']
base_libs = [common, messaging, cereal, visionipc, transformations, 'zmq',
@ -22,7 +22,7 @@ prev_moc_path = cabana_env['QT_MOCHPREFIX']
cabana_env['QT_MOCHPREFIX'] = os.path.dirname(prev_moc_path) + '/cabana/moc_'
cabana_lib = cabana_env.Library("cabana_lib", ['mainwin.cc', 'binaryview.cc', 'chartswidget.cc', 'historylog.cc', 'videowidget.cc', 'signaledit.cc', 'dbcmanager.cc',
'canmessages.cc', 'commands.cc', 'messageswidget.cc', 'settings.cc', 'detailwidget.cc', 'tools/findsimilarbits.cc'], LIBS=cabana_libs, FRAMEWORKS=base_frameworks)
cabana_env.Program('_cabana', ['cabana.cc', cabana_lib], LIBS=cabana_libs, FRAMEWORKS=base_frameworks)
cabana_env.Program('_cabana', ['cabana.cc', cabana_lib, asset_obj], LIBS=cabana_libs, FRAMEWORKS=base_frameworks)
if GetOption('test'):
cabana_env.Program('tests/_test_cabana', ['tests/test_runner.cc', 'tests/test_cabana.cc', cabana_lib], LIBS=[cabana_libs])

@ -12,6 +12,8 @@
#include <QToolTip>
#include <QtConcurrent>
#include "selfdrive/ui/qt/util.h"
// ChartsWidget
ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) {
@ -19,14 +21,15 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) {
// toolbar
QToolBar *toolbar = new QToolBar(tr("Charts"), this);
toolbar->setIconSize({16, 16});
title_label = new QLabel();
title_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
toolbar->addWidget(title_label);
show_all_values_btn = toolbar->addAction("");
toolbar->addWidget(range_label = new QLabel());
reset_zoom_btn = toolbar->addAction("");
reset_zoom_btn = toolbar->addAction(bootstrapPixmap("arrow-counterclockwise"), "");
reset_zoom_btn->setToolTip(tr("Reset zoom (drag on chart to zoom X-Axis)"));
remove_all_btn = toolbar->addAction("");
remove_all_btn = toolbar->addAction(bootstrapPixmap("x"), "");
remove_all_btn->setToolTip(tr("Remove all charts"));
dock_btn = toolbar->addAction("");
main_layout->addWidget(toolbar);
@ -137,7 +140,7 @@ void ChartsWidget::updateToolBar() {
reset_zoom_btn->setEnabled(is_zoomed);
range_label->setText(is_zoomed ? tr("%1 - %2").arg(zoomed_range.first, 0, 'f', 2).arg(zoomed_range.second, 0, 'f', 2) : "");
title_label->setText(charts.size() > 0 ? tr("Charts (%1)").arg(charts.size()) : tr("Charts"));
dock_btn->setText(docking ? "" : "");
dock_btn->setIcon(bootstrapPixmap(docking ? "arrow-up-right" : "arrow-down-left"));
dock_btn->setToolTip(docking ? tr("Undock charts") : tr("Dock charts"));
}
@ -223,7 +226,7 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) {
chart->layout()->setContentsMargins(0, 0, 0, 0);
QToolButton *remove_btn = new QToolButton();
remove_btn->setText("X");
remove_btn->setIcon(bootstrapPixmap("x"));
remove_btn->setAutoRaise(true);
remove_btn->setToolTip(tr("Remove Chart"));
close_btn_proxy = new QGraphicsProxyWidget(chart);
@ -231,7 +234,7 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) {
close_btn_proxy->setZValue(chart->zValue() + 11);
QToolButton *manage_btn = new QToolButton();
manage_btn->setText("🔧");
manage_btn->setIcon(bootstrapPixmap("gear"));
manage_btn->setAutoRaise(true);
manage_btn->setToolTip(tr("Manage series"));
manage_btn_proxy = new QGraphicsProxyWidget(chart);

@ -36,6 +36,7 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart
// message title
toolbar = new QToolBar(this);
toolbar->setIconSize({16, 16});
toolbar->addWidget(new QLabel("time:"));
time_label = new QLabel(this);
time_label->setStyleSheet("font-weight:bold");
@ -45,8 +46,8 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart
name_label->setAlignment(Qt::AlignCenter);
name_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
toolbar->addWidget(name_label);
toolbar->addAction("🖍", this, &DetailWidget::editMsg)->setToolTip(tr("Edit Message"));
remove_msg_act = toolbar->addAction("X", this, &DetailWidget::removeMsg);
toolbar->addAction(bootstrapPixmap("pencil"), "", this, &DetailWidget::editMsg)->setToolTip(tr("Edit Message"));
remove_msg_act = toolbar->addAction(bootstrapPixmap("x-lg"), "", this, &DetailWidget::removeMsg);
remove_msg_act->setToolTip(tr("Remove Message"));
toolbar->setVisible(false);
frame_layout->addWidget(toolbar);

@ -6,6 +6,8 @@
#include <QHBoxLayout>
#include <QVBoxLayout>
#include "selfdrive/ui/qt/util.h"
// SignalForm
SignalForm::SignalForm(QWidget *parent) : QWidget(parent) {
@ -104,13 +106,13 @@ SignalEdit::SignalEdit(int index, QWidget *parent) : form_idx(index), QWidget(pa
title_layout->addWidget(title);
plot_btn = new QToolButton(this);
plot_btn->setText("📈");
plot_btn->setIcon(bootstrapPixmap("graph-up"));
plot_btn->setCheckable(true);
plot_btn->setAutoRaise(true);
title_layout->addWidget(plot_btn);
auto remove_btn = new QToolButton(this);
remove_btn->setAutoRaise(true);
remove_btn->setText("x");
remove_btn->setIcon(bootstrapPixmap("x"));
remove_btn->setToolTip(tr("Remove signal"));
title_layout->addWidget(remove_btn);
main_layout->addWidget(title_bar);

@ -13,6 +13,8 @@
#include <QVBoxLayout>
#include <QtConcurrent>
#include "selfdrive/ui/qt/util.h"
inline QString formatTime(int seconds) {
return QDateTime::fromTime_t(seconds).toString(seconds > 60 * 60 ? "hh:mm:ss" : "mm:ss");
}
@ -43,8 +45,7 @@ VideoWidget::VideoWidget(QWidget *parent) : QWidget(parent) {
// btn controls
QHBoxLayout *control_layout = new QHBoxLayout();
play_btn = new QPushButton("");
play_btn->setStyleSheet("font-weight:bold; height:16px");
play_btn = new QPushButton();
control_layout->addWidget(play_btn);
QButtonGroup *group = new QButtonGroup(this);
@ -68,12 +69,13 @@ VideoWidget::VideoWidget(QWidget *parent) : QWidget(parent) {
QObject::connect(cam_widget, &CameraWidget::clicked, []() { can->pause(!can->isPaused()); });
QObject::connect(play_btn, &QPushButton::clicked, []() { can->pause(!can->isPaused()); });
QObject::connect(can, &CANMessages::updated, this, &VideoWidget::updateState);
QObject::connect(can, &CANMessages::paused, [this]() { play_btn->setText(""); });
QObject::connect(can, &CANMessages::resume, [this]() { play_btn->setText(""); });
QObject::connect(can, &CANMessages::paused, this, &VideoWidget::updatePlayBtnState);
QObject::connect(can, &CANMessages::resume, this, &VideoWidget::updatePlayBtnState);
QObject::connect(can, &CANMessages::streamStarted, [this]() {
end_time_label->setText(formatTime(can->totalSeconds()));
slider->setRange(0, can->totalSeconds() * 1000);
});
updatePlayBtnState();
}
void VideoWidget::rangeChanged(double min, double max, bool is_zoomed) {
@ -90,6 +92,11 @@ void VideoWidget::updateState() {
slider->setValue(can->currentSec() * 1000);
}
void VideoWidget::updatePlayBtnState() {
play_btn->setIcon(bootstrapPixmap(can->isPaused() ? "play" : "pause"));
play_btn->setToolTip(can->isPaused() ? tr("Play") : tr("Pause"));
}
// Slider
Slider::Slider(QWidget *parent) : QSlider(Qt::Horizontal, parent) {
QTimer *timer = new QTimer(this);

@ -44,6 +44,7 @@ public:
protected:
void updateState();
void updatePlayBtnState();
CameraWidget *cam_widget;
QLabel *end_time_label;

Loading…
Cancel
Save