qt/LedOK/program/pagelistitem.cpp
2023-04-28 16:02:14 +08:00

523 lines
18 KiB
C++
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.

#include "pagelistitem.h"
#include "cfg.h"
#include "tools.h"
#include "base/ffutil.h"
#include "program/wplanlist.h"
#include "base/lodateselector.h"
#include "program/ebase.h"
#include "program/etext.h"
#include "program/ephoto.h"
#include "program/evideo.h"
#include "program/eaudio.h"
#include "program/egif.h"
#include "program/edclock.h"
#include "program/eaclock.h"
#include "program/eenviron.h"
#include "program/eweb.h"
#include "program/etimer.h"
#include "program/emultiwin.h"
#include <QMessageBox>
#include <QJsonArray>
#include <QDir>
#include <QBoxLayout>
#include <QSpinBox>
#include <QLineEdit>
#include <QDateEdit>
class PageScene : public QGraphicsScene {
public:
PageScene(qreal x, qreal y, qreal width, qreal height, QObject *parent = nullptr) : QGraphicsScene(x, y, width, height, parent) {};
void drawBackground(QPainter *painter, const QRectF &rect) {
QGraphicsScene::drawBackground(painter, rect);
painter->fillRect(sceneRect(), Qt::black);
}
};
PageListItem::PageListItem(const QJsonObject &attr, const QString &pageDir) : mAttr(attr), mPageDir(pageDir) {
setSizeHint(QSize(0,80));
mScene = new PageScene(0, 0, gProgItem->mWidth, gProgItem->mHeight, this);
auto elements = mAttr["elements"].toArray();
foreach(auto ele, elements) {
QString type = ele["elementType"].toString();
EBase *element = 0;
if(type=="Text") element = new EText(ele.toObject());
else if(type=="Photo") element = EPhoto::create(ele.toObject(), this);
else if(type=="Gif") element = EGif::create(ele.toObject(), this);
else if(type=="Movie") element = EVideo::create(ele.toObject(), this);
else if(type=="Audio") element = EAudio::create(ele.toObject(), this);
else if(type=="DClock") element = new EDClock(ele.toObject());
else if(type=="AClock") element = new EAClock(ele.toObject());
else if(type=="Temp") element = new EEnviron(ele.toObject());
else if(type=="Web") element = new EWeb(ele.toObject());
else if(type=="Timer") element = new ETimer(ele.toObject());
else if(type=="Window") element = new EMultiWin(ele.toObject(), this);
if(element) mScene->addItem(element);
}
}
//更新页属性参数到page.json
void PageListItem::updateJson() {
if(mAttrWgt==0) return; //没有点开该页面进行编辑
QJsonArray elements;
auto items = mScene->items();
foreach(auto item, items) {
auto element = static_cast<EBase*>(item);
if(element->mMultiWin == nullptr) elements.append(element->attrJson());
}
mAttr.insert("elements", elements);
QJsonArray audios;
auto cnt = mAudiosList->count();
for(int i=0; i<cnt; i++) {
auto info = mAudiosList->item(i)->data(Qt::UserRole).value<AudioInfo>();
audios.append(QJsonObject{
{"dir", info.dir},
{"name", info.name},
{"dur", info.dur},
{"vol", info.vol}
});
}
mAttr.insert("audios", audios);
}
bool PageListItem::saveFiles() {
mPageDir = gProgItem->mProgDir + "/" + mAttr["name"].toString();
QDir pageQDir(mPageDir);
if(! pageQDir.exists() && ! pageQDir.mkpath(mPageDir)) return false;
QJsonArray elements;
auto items = mScene->items();
foreach(auto item, items) {
auto element = static_cast<EBase*>(item);
if(element->mMultiWin == nullptr && element->save(mPageDir)) elements.append(element->attrJson());
}
mAttr.insert("elements", elements);
if(mAttrWgt!=0) {
QJsonArray audios;
auto cnt = mAudiosList->count();
for(int i=0; i<cnt; i++) {
auto info = mAudiosList->item(i)->data(Qt::UserRole).value<AudioInfo>();
QString oldFile = info.dir + PAGEDEL_SUFFIX + "/" + info.name;
if(QFileInfo::exists(oldFile)) ;
else if(QFileInfo::exists(oldFile = info.dir + "/" + info.name)) ;
else continue;
QFile::copy(oldFile, mPageDir + "/" + info.name);
info.dir = mPageDir;
audios.append(QJsonObject{
{"dir", info.dir},
{"name", info.name},
{"dur", info.dur},
{"vol", info.vol}
});
}
mAttr.insert("audios", audios);
}
QFile file(mPageDir + "/page.json");
if(! file.open(QIODevice::WriteOnly)) {
QMessageBox::critical(gMainWin, "Write Error", mPageDir + "/page.json "+file.errorString());
return false;
}
file.write(QJsonDocument(mAttr).toJson());
file.close();
return true;
}
QWidget *PageListItem::itemWgt() {
auto wgtPage = new QWidget();
auto hBox = new QHBoxLayout(wgtPage);
mGraView = new QGraphicsView();
mGraView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
mGraView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
mGraView->setEnabled(false);
mGraView->setFrameShape(QFrame::NoFrame);
mGraView->setFixedSize(120, 60);
mGraView->setStyleSheet("QGraphicsView{background-color:transparent;}");
mGraView->setScene(mScene);
qreal scale = qMin(mGraView->width() / mScene->width(), mGraView->height() / mScene->height());
mGraView->scale(scale, scale);
hBox->addWidget(mGraView);
fdPlayTimes = new QLabel(QString::number(mAttr["repeat"].toInt()));
hBox->addWidget(fdPlayTimes);
hBox->addWidget(new QLabel(tr("times")));
hBox->addStretch();
return wgtPage;
}
QWidget *PageListItem::attrWgt() {
if(mAttrWgt!=0) return mAttrWgt;
mAttrWgt = new QWidget();
mAttrWgt->setStyleSheet(R"rrr(
QPushButton {
padding-left: 4px;
padding-right: 4px;
padding-top: 2px;
padding-bottom: 2px;
border-radius: 4px;
}
QPushButton:hover {
background-color: #dddddd;
}
QPushButton#bnAddPlan {
image: url(:/res/program/AddPlan.png);
width: 24;
height: 24;
}
QPushButton#bnClearPlan {
image: url(:/res/program/Clean.png);
width: 24;
height: 24;
}
QPushButton#bnDel {
image: url(:/res/program/Delete.png);
width: 18;
height: 18;
}
QPushButton#bnAddPlan:pressed,
QPushButton#bnClearPlan:pressed,
QPushButton#bnDel:pressed {
margin-top: 1px;
margin-left: 1px;
margin-bottom: -1px;
margin-right: -1px;
}
QPushButton[ssName="weeklySelector"] {
border: 1px solid #6A838F;
width: 24;
height: 24;
}
QPushButton[ssName="weeklySelector"]:checked {
background-color: #0099cc;
}
)rrr");
auto vBox = new QVBoxLayout(mAttrWgt);
vBox->setContentsMargins(6, 0, 6, 0);
vBox->setSpacing(4);
auto hBox = new QHBoxLayout();
auto lb1 = new QLabel(tr("Page name"));
lb1->setMinimumWidth(80);
hBox->addWidget(lb1);
auto fdPageName = new QLineEdit(mAttr["name"].toString());
connect(fdPageName, &QLineEdit::textEdited, this, [this, fdPageName](const QString &text) {
bool isDupli = false;
auto listWgt = listWidget();
if(nullptr != listWgt) {
int n = listWgt->count();
for(int i=0; i<n; i++) {
auto item = static_cast<PageListItem*>(listWgt->item(i));
if(text == item->mAttr["name"].toString()) {
isDupli = true;
break;
}
}
}
if(isDupli) {
QString newName = tr("New") + QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz");
mAttr["name"] = newName;
fdPageName->setText(newName);
} else mAttr["name"] = text;
});
hBox->addWidget(fdPageName);
vBox->addLayout(hBox);
hBox = new QHBoxLayout();
lb1 = new QLabel(tr("Play times"));
lb1->setMinimumWidth(80);
hBox->addWidget(lb1);
auto fdPlayTimes = new QSpinBox();
fdPlayTimes->setRange(1, 9999);
fdPlayTimes->setValue(mAttr["repeat"].toInt());
connect(fdPlayTimes, (void(QSpinBox::*)(int))&QSpinBox::valueChanged, this, [this](int value) {
mAttr["repeat"] = value;
this->fdPlayTimes->setText(QString::number(value));
});
hBox->addWidget(fdPlayTimes);
hBox->addSpacing(20);
auto fdLoop = new QCheckBox(tr("Sources Repeat"));
fdLoop->setChecked(mAttr["loop"].toBool());
connect(fdLoop, &QCheckBox::toggled, this, [this](bool checked) {
mAttr["loop"] = checked;
});
hBox->addWidget(fdLoop);
hBox->addStretch();
vBox->addLayout(hBox);
auto line = new QFrame();
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Sunken);
vBox->addWidget(line);
hBox = new QHBoxLayout();
auto lb = new QLabel();
lb->setPixmap(QPixmap(":/res/program/Audio.png").scaledToHeight(24, Qt::SmoothTransformation));
hBox->addWidget(lb);
hBox->addWidget(new QLabel(tr("Audios")));
hBox->addStretch();
hBox->addWidget(new QLabel(tr("Total Dur")));
auto fdTtlDur = new QLabel("0");
hBox->addWidget(fdTtlDur);
hBox->addWidget(new QLabel(tr("s")));
hBox->addStretch();
auto btnAdd = new QPushButton();
btnAdd->setIcon(QIcon(":/res/program/Add.png"));
btnAdd->setProperty("style","multiTool");
mAudiosList = new QListWidget();
connect(btnAdd, &QPushButton::clicked, this, [this, fdTtlDur] {
auto files = QFileDialog::getOpenFileNames(gMainWin, tr("Select File"), gFileHome, EAudio::filters());
int durs = fdTtlDur->text().toInt();
for(int i=0; i<files.count(); i++) {
int64_t dur;
QString err = audioInfo(files[i].toUtf8(), &dur);
if(! err.isEmpty()) {
QMessageBox::critical(gMainWin, "Audio Error", err+"\n"+files[i]);
continue;
}
QFileInfo fInfo(files[i]);
AudioInfo info{fInfo.absolutePath(), fInfo.fileName(), (int)(dur/1000000)};
auto item = new QListWidgetItem(QIcon(":/res/program/Audio.png"), info.name);
item->setData(Qt::UserRole, QVariant::fromValue(info));
mAudiosList->addItem(item);
if(i == files.count()-1) {
mAudiosList->setCurrentItem(item);
gFileHome = fInfo.absolutePath();
}
durs += info.dur;
mAudiosList->addItem(item);
}
fdTtlDur->setText(QString::number(durs));
});
hBox->addWidget(btnAdd);
auto btnDel = new QPushButton();
btnDel->setIcon(QIcon(":/res/program/Delete.png"));
btnDel->setProperty("style","multiTool");
connect(btnDel, &QPushButton::clicked, this, [this, fdTtlDur] {
auto row = mAudiosList->currentRow();
if(row < 0) return;
int durs = fdTtlDur->text().toInt();
auto item = mAudiosList->takeItem(row);
auto info = static_cast<AudioInfo>(item->data(Qt::UserRole).value<AudioInfo>());
durs -= info.dur;
delete item;
fdTtlDur->setText(QString::number(durs));
});
hBox->addWidget(btnDel);
auto btnGoUp = new QPushButton();
btnGoUp->setIcon(QIcon(":/res/program/GoUp.png"));
btnGoUp->setProperty("style","multiTool");
connect(btnGoUp, &QPushButton::clicked, this, [this] {
int row = mAudiosList->currentRow();
if(row < 1) return;
mAudiosList->insertItem(row-1, mAudiosList->takeItem(row));
mAudiosList->setCurrentRow(row-1);
});
hBox->addWidget(btnGoUp);
auto btnGoDown = new QPushButton();
btnGoDown->setIcon(QIcon(":/res/program/GoDown.png"));
btnGoDown->setProperty("style","multiTool");
connect(btnGoDown, &QPushButton::clicked, this, [this] {
int row = mAudiosList->currentRow();
if(row < 0 || row > mAudiosList->count() - 2) return;
mAudiosList->insertItem(row+1, mAudiosList->takeItem(row));
mAudiosList->setCurrentRow(row+1);
});
hBox->addWidget(btnGoDown);
vBox->addLayout(hBox);
mAudiosList->setMinimumHeight(120);
mAudiosList->setIconSize(QSize(20, 20));
vBox->addWidget(mAudiosList, 1);
auto audios = mAttr["audios"].toArray();
int durs = 0;
foreach(auto audio, audios) {
AudioInfo info{audio["dir"].toString(), audio["name"].toString(), audio["dur"].toInt(), audio["vol"].toInt()};
if(info.dir.isEmpty() || info.name.isEmpty() || info.dur==0) continue;
auto item = new QListWidgetItem(QIcon(":/res/program/Audio.png"), info.name);
item->setData(Qt::UserRole, QVariant::fromValue(info));
durs += info.dur;
mAudiosList->addItem(item);
}
fdTtlDur->setText(QString::number(durs));
auto wgtAudioInfo = new QWidget();
wgtAudioInfo->hide();
hBox = new QHBoxLayout(wgtAudioInfo);
hBox->setContentsMargins(0,0,0,0);
hBox->setSpacing(2);
hBox->addWidget(new QLabel(tr("Duration")));
hBox->addSpacing(4);
auto fdAudioDur = new QLabel("0");
hBox->addWidget(fdAudioDur);
hBox->addWidget(new QLabel(tr("s")));
hBox->addStretch();
hBox->addWidget(new QLabel(tr("Vol")));
auto fdVol = new QSlider(Qt::Horizontal);
fdVol->setMaximum(100);
fdVol->setMinimumWidth(120);
hBox->addWidget(fdVol);
auto fdAudioVol = new QLabel("0");
fdAudioVol->setAlignment(Qt::AlignRight);
fdAudioVol->setFixedWidth(38);
hBox->addWidget(fdAudioVol);
vBox->addWidget(wgtAudioInfo);
connect(fdVol, &QSlider::valueChanged, this, [this, fdAudioVol](int value) {
fdAudioVol->setText(QString::number(value)+"%");
auto item = mAudiosList->currentItem();
if(item==nullptr) return;
auto info = static_cast<AudioInfo>(item->data(Qt::UserRole).value<AudioInfo>());
info.vol = value;
item->setData(Qt::UserRole, QVariant::fromValue(info));
});
connect(mAudiosList, &QListWidget::currentItemChanged, this, [fdAudioDur, wgtAudioInfo, fdVol, fdAudioVol](QListWidgetItem *current, QListWidgetItem *) {
if(current==nullptr) {
wgtAudioInfo->hide();
return;
}
wgtAudioInfo->show();
auto info = static_cast<AudioInfo>(current->data(Qt::UserRole).value<AudioInfo>());
fdAudioDur->setText(QString::number(info.dur));
fdVol->setValue(info.vol);
fdAudioVol->setText(QString::number(info.vol)+"%");
});
line = new QFrame();
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Sunken);
vBox->addWidget(line);
QJsonValue validDate = mAttr["validDate"];
auto wValidDate = new QCheckBox(tr("Valid Date"));
bool isDateValid = validDate["isValid"].toBool();
wValidDate->setChecked(isDateValid);
vBox->addWidget(wValidDate);
hBox = new QHBoxLayout();
auto fdStart = new QDateEdit();
fdStart->setDisplayFormat("yyyy-MM-dd");
fdStart->setEnabled(isDateValid);
fdStart->setDate(QDate::fromString(validDate["start"].toString(), "yyyy-MM-dd"));
hBox->addWidget(fdStart);
auto bnDateStart = new LoDateSelector();
bnDateStart->setEnabled(isDateValid);
connect(bnDateStart, &LoDateSelector::sDateSelected, fdStart, &QDateEdit::setDate);
hBox->addWidget(bnDateStart);
hBox->addWidget(new QLabel(""));
auto fdEnd = new QDateEdit();
fdEnd->setDisplayFormat("yyyy-MM-dd");
fdEnd->setEnabled(isDateValid);
fdEnd->setDate(QDate::fromString(validDate["end"].toString(), "yyyy-MM-dd"));
connect(fdStart, &QDateEdit::dateChanged, this, [this, wValidDate, fdStart, fdEnd](const QDate &date) {
auto end = fdEnd->date();
if(! date.isValid() || date > end) {
QMessageBox::warning(gMainWin, tr("Warning"), tr("Start Time can't be later than End Time"));
fdStart->setDate(end);
}
mAttr["validDate"] = QJsonObject{
{"isValid", wValidDate->isChecked()},
{"start", fdStart->date().toString("yyyy-MM-dd")},
{"end", end.toString("yyyy-MM-dd")}
};
});
connect(fdEnd, &QDateEdit::dateChanged, this, [this, wValidDate, fdStart, fdEnd](const QDate &date) {
QDate start = fdStart->date();
if(! date.isValid() || start > date) {
QMessageBox::warning(gMainWin, tr("Warning"), tr("End Time can't be earlier than Start Time"));
fdEnd->setDate(start);
}
mAttr["validDate"] = QJsonObject{
{"isValid", wValidDate->isChecked()},
{"start", start.toString("yyyy-MM-dd")},
{"end", fdEnd->date().toString("yyyy-MM-dd")}
};
});
hBox->addWidget(fdEnd);
auto bnDateEnd = new LoDateSelector();
bnDateEnd->setEnabled(isDateValid);
connect(bnDateEnd, &LoDateSelector::sDateSelected, fdEnd, &QDateEdit::setDate);
hBox->addWidget(bnDateEnd);
hBox->addStretch();
connect(wValidDate, &QCheckBox::toggled, this, [this, fdStart, fdEnd, bnDateStart, bnDateEnd](bool checked) {
fdStart->setEnabled(checked);
fdEnd->setEnabled(checked);
bnDateStart->setEnabled(checked);
bnDateEnd->setEnabled(checked);
mAttr["validDate"] = QJsonObject{
{"isValid", checked},
{"start", fdStart->date().toString("yyyy-MM-dd")},
{"end", fdEnd->date().toString("yyyy-MM-dd")}
};
});
vBox->addLayout(hBox);
line = new QFrame();
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Sunken);
vBox->addWidget(line);
hBox = new QHBoxLayout();
hBox->setSpacing(12);
hBox->addWidget(new QLabel(tr("Plan")));
hBox->addStretch();
auto bnAddPlan = new QPushButton();
bnAddPlan->setObjectName("bnAddPlan");
hBox->addWidget(bnAddPlan);
auto bnClearPlan = new QPushButton();
bnClearPlan->setObjectName("bnClearPlan");
hBox->addWidget(bnClearPlan);
vBox->addLayout(hBox);
auto wPlans = new wPlanList();
wPlans->setStyleSheet(R"rrr(
QListWidget::item {
height: 100;
border-top: 1px solid #6A838F;
margin-right: 9px;
})rrr");
wPlans->onRestorePlan(mAttr["plans"].toArray());
connect(bnAddPlan, &QPushButton::clicked, wPlans, &wPlanList::onAddPlan);
connect(bnClearPlan, &QPushButton::clicked, wPlans, &wPlanList::clear);
connect(wPlans, &QListWidget::itemChanged, this, [this, wPlans] {
mAttr["plans"] = wPlans->plansJson();
});
vBox->addWidget(wPlans, 2);
return mAttrWgt;
}