LVGLBuilder/properties/LVGLPropertySeries.cpp

343 lines
10 KiB
C++

#include "LVGLPropertySeries.h"
#include <QDialogButtonBox>
#include <QGridLayout>
#include <QListWidget>
#include <QPushButton>
#include <QToolButton>
#include <QPixmap>
#include <QColorDialog>
#include <QStyledItemDelegate>
#include <QSpinBox>
#include <QDialog>
#include <QJsonArray>
#include "LVGLCore.h"
#include "LVGLObject.h"
struct LVGLChartSeries
{
QColor color;
QVector<lv_coord_t> points;
lv_chart_series_t *ser;
};
class LVGLPropertySeriesDialog : public QDialog
{
public:
LVGLPropertySeriesDialog(QWidget *parent = nullptr);
QListWidget *m_seriesList;
QListWidget *m_dataList;
QVector<LVGLChartSeries> m_series;
friend class LVGLPropertySeries;
};
class LVGLPropertySeriesDelegate : public QStyledItemDelegate
{
LVGLPropertySeriesDialog *m_dialog;
public:
LVGLPropertySeriesDelegate(LVGLPropertySeriesDialog *dialog) : m_dialog(dialog) {}
QWidget *createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override
{
Q_UNUSED(option)
if (index.isValid()) {
QSpinBox *widget = new QSpinBox(parent);
widget->setRange(0, std::min(lvgl.width(), lvgl.height()));
return widget;
}
return nullptr;
}
void setEditorData(QWidget* editor, const QModelIndex& index) const override
{
if (index.isValid()) {
QSpinBox *widget = qobject_cast<QSpinBox*>(editor);
widget->setValue(index.data().toInt());
}
}
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override
{
if (index.isValid()) {
QSpinBox *widget = qobject_cast<QSpinBox*>(editor);
model->setData(index, widget->value());
int idx = m_dialog->m_dataList->currentRow();
if (idx > -1)
m_dialog->m_series[m_dialog->m_seriesList->currentRow()].points[idx] = static_cast<lv_coord_t>(widget->value());
}
}
};
LVGLPropertySeriesDialog::LVGLPropertySeriesDialog(QWidget *parent)
: QDialog(parent)
{
m_seriesList = new QListWidget;
m_dataList = new QListWidget;
m_dataList->setItemDelegate(new LVGLPropertySeriesDelegate(this));
QToolButton *color = new QToolButton;
QPushButton *add1 = new QPushButton("Add series");
QPushButton *rem1 = new QPushButton("Remove series");
QPushButton *add2 = new QPushButton("Add point");
QPushButton *rem2 = new QPushButton("Remove point");
QPixmap pix(16, 16);
pix.fill(Qt::red);
color->setIcon(QIcon(pix));
QDialogButtonBox *box = new QDialogButtonBox(QDialogButtonBox::Ok |
QDialogButtonBox::Cancel);
connect(box, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(box, &QDialogButtonBox::rejected, this, &QDialog::reject);
QGridLayout *layout1 = new QGridLayout;
layout1->addWidget(add1, 0, 0);
layout1->addWidget(rem1, 1, 0);
layout1->addWidget(m_seriesList, 2, 0, 2, 1);
layout1->addWidget(add2, 0, 1);
layout1->addWidget(rem2, 1, 1);
layout1->addWidget(color, 2, 1);
layout1->addWidget(m_dataList, 3, 1);
layout1->addWidget(box, 4, 0, 1, 2);
add2->setEnabled(false);
rem2->setEnabled(false);
color->setEnabled(false);
connect(add1, &QPushButton::clicked, [this](bool) {
QList<int> numbers;
for (int i = 0; i < m_seriesList->count(); ++i) {
QString text = m_seriesList->item(i)->text();
int num = text.mid(text.indexOf(' ') + 1).toInt();
auto ind = std::lower_bound(numbers.begin(), numbers.end(), num);
numbers.insert(ind, num);
}
int id = 1;
for (int num:numbers) {
if (num == id)
id++;
else
break;
}
LVGLChartSeries series;
series.color = Qt::red;
m_series << series;
m_seriesList->addItem(QString("Series %1").arg(id));
});
connect(rem1, &QPushButton::clicked, [this](bool) {
int idx = m_seriesList->currentRow();
if (idx > -1) {
m_seriesList->takeItem(idx);
m_series.removeAt(idx);
}
});
connect(m_seriesList, &QListWidget::currentRowChanged, [=](int idx) {
const bool isValid = (idx >= 0);
add2->setEnabled(isValid);
rem2->setEnabled(isValid);
color->setEnabled(isValid);
if (isValid) {
QPixmap pix(16, 16);
pix.fill(m_series[idx].color);
color->setIcon(QIcon(pix));
m_dataList->clear();
for (const auto &p:m_series[idx].points) {
QListWidgetItem *item = new QListWidgetItem(QString::number(p));
item->setFlags(item->flags() | Qt::ItemIsEditable);
m_dataList->addItem(item);
}
}
});
connect(color, &QPushButton::clicked, [this,color](bool) {
QColorDialog dialog(this);
int idx = m_seriesList->currentRow();
dialog.setCurrentColor(m_series[idx].color);
if (dialog.exec() == QDialog::Accepted) {
QPixmap pix(16, 16);
pix.fill(dialog.selectedColor());
color->setIcon(QIcon(pix));
m_series[idx].color = dialog.selectedColor();
}
});
connect(add2, &QPushButton::clicked, [this](bool) {
m_series[m_seriesList->currentRow()].points << 0;
QListWidgetItem *item = new QListWidgetItem("0");
item->setFlags(item->flags() | Qt::ItemIsEditable);
m_dataList->addItem(item);
});
connect(rem2, &QPushButton::clicked, [this](bool) {
int idx = m_seriesList->currentRow();
if (idx > -1) {
m_dataList->takeItem(idx);
m_series[m_seriesList->currentRow()].points.removeAt(idx);
}
});
setLayout(layout1);
}
LVGLPropertySeries::LVGLPropertySeries(LVGLProperty *parent)
: LVGLProperty(parent)
{
}
QString LVGLPropertySeries::name() const
{
return "Series";
}
bool LVGLPropertySeries::hasEditor() const
{
return true;
}
QWidget *LVGLPropertySeries::editor(QWidget *parent)
{
m_widget = new LVGLPropertySeriesDialog(parent);
return m_widget;
}
void LVGLPropertySeries::updateEditor(LVGLObject *obj)
{
lv_obj_t *chart = obj->obj();
Q_ASSERT(lv_debug_check_obj_type(chart, "lv_chart"));
lv_chart_ext_t *ext = reinterpret_cast<lv_chart_ext_t*>(lv_obj_get_ext_attr(chart));
lv_chart_series_t *ser;
size_t i = 1;
for (ser = reinterpret_cast<lv_chart_series_t*>(lv_ll_get_tail(&ext->series_ll));
ser != nullptr;
ser = reinterpret_cast<lv_chart_series_t*>(lv_ll_get_prev(&ext->series_ll, ser)), ++i)
{
LVGLChartSeries series;
series.color = lvgl.toColor(ser->color);
const uint16_t ctx = ser->start_point == 0 ? lv_chart_get_point_cnt(chart) : ser->start_point;
for (uint16_t j = 0; j < ctx; ++j)
series.points << ser->points[j];
m_widget->m_series << series;
m_widget->m_seriesList->addItem(QString("Series %1").arg(i));
}
}
void LVGLPropertySeries::updateWidget(LVGLObject *obj)
{
lv_obj_t *chart = obj->obj();
Q_ASSERT(lv_debug_check_obj_type(chart, "lv_chart"));
lv_chart_ext_t *ext = reinterpret_cast<lv_chart_ext_t*>(lv_obj_get_ext_attr(chart));
int maxPoints = 0;
for (const auto &s:m_widget->m_series)
maxPoints = std::max(maxPoints, s.points.size());
// clear the chart series (maybe lvgl should add a function for this)
lv_chart_series_t *ser;
for (ser = reinterpret_cast<lv_chart_series_t*>(lv_ll_get_tail(&ext->series_ll));
ser != nullptr;
ser = reinterpret_cast<lv_chart_series_t*>(lv_ll_get_prev(&ext->series_ll, ser)))
{
lv_mem_free(ser->points);
lv_mem_free(ser);
}
lv_ll_clear(&ext->series_ll);
// add new charts
lv_chart_set_point_count(chart, static_cast<uint16_t>(maxPoints));
for (const auto &s:m_widget->m_series) {
lv_chart_series_t *ser = lv_chart_add_series(chart, lvgl.fromColor(s.color));
for (int i = 0; i < s.points.size(); ++i)
lv_chart_set_next(chart, ser, s.points[i]);
}
}
QStringList LVGLPropertySeries::function(LVGLObject *obj) const
{
QStringList ret;
lv_obj_t *chart = obj->obj();
Q_ASSERT(lv_debug_check_obj_type(chart, "lv_chart"));
lv_chart_ext_t *ext = reinterpret_cast<lv_chart_ext_t*>(lv_obj_get_ext_attr(chart));
lv_chart_series_t *ser;
size_t i = 1;
for (ser = reinterpret_cast<lv_chart_series_t*>(lv_ll_get_tail(&ext->series_ll));
ser != nullptr;
ser = reinterpret_cast<lv_chart_series_t*>(lv_ll_get_prev(&ext->series_ll, ser)), ++i)
{
const QString codeSerName = QString("ser_%1_%2").arg(obj->codeName()).arg(i);
const QString color = QVariant(lvgl.toColor(ser->color)).toString().replace("#", "0x");
ret << QString("lv_chart_series_t * %1 = lv_chart_add_series(%2, lv_color_hex(%3));").arg(codeSerName).arg(obj->codeName()).arg(color);
const uint16_t ctx = ser->start_point == 0 ? lv_chart_get_point_cnt(chart) : ser->start_point;
for (uint16_t j = 0; j < ctx; ++j)
ret << QString("lv_chart_set_next(%1, %2, %3);").arg(obj->codeName()).arg(codeSerName).arg(ser->points[j]);
ret << "";
}
return ret;
}
QJsonValue LVGLPropertySeries::toJson(LVGLObject *obj) const
{
QJsonArray ret;
lv_obj_t *chart = obj->obj();
Q_ASSERT(lv_debug_check_obj_type(chart, "lv_chart"));
lv_chart_ext_t *ext = reinterpret_cast<lv_chart_ext_t*>(lv_obj_get_ext_attr(chart));
lv_chart_series_t *ser;
for (ser = reinterpret_cast<lv_chart_series_t*>(lv_ll_get_tail(&ext->series_ll));
ser != nullptr;
ser = reinterpret_cast<lv_chart_series_t*>(lv_ll_get_prev(&ext->series_ll, ser)))
{
QJsonArray values;
const uint16_t ctx = ser->start_point == 0 ? lv_chart_get_point_cnt(chart) : ser->start_point;
for (uint16_t j = 0; j < ctx; ++j)
values.append(ser->points[j]);
QJsonObject series({{"color", lvgl.colorToJson(ser->color)},
{"values", values}});
ret.append(series);
}
return ret;
}
void LVGLPropertySeries::setValue(LVGLObject *obj, QVariant value)
{
lv_obj_t *chart = obj->obj();
Q_ASSERT(lv_debug_check_obj_type(chart, "lv_chart"));
if (value.type() == QVariant::List) {
QVariantList series = value.toList();
QVector<LVGLChartSeries> seriesList;
int maxPoints = 0;
for (const QVariant &s:series) {
QVariantMap map = s.toMap();
if (!map.contains("color") || !map.contains("values"))
continue;
LVGLChartSeries item;
item.ser = lv_chart_add_series(chart, lvgl.fromColor(map["color"]));
for (const QVariant &v:map["values"].toList()) {
item.points << static_cast<lv_coord_t>(v.toInt());
//lv_chart_set_next(chart, ser, static_cast<lv_coord_t>(v.toInt()));
}
maxPoints = std::max(maxPoints, item.points.size());
seriesList << item;
}
lv_chart_set_point_count(chart, static_cast<uint16_t>(maxPoints));
for (const auto &s:seriesList) {
for (int i = 0; i < s.points.size(); ++i)
lv_chart_set_next(chart, s.ser, s.points[i]);
}
}
}