Commit 34d477ab authored by Leander Schulten's avatar Leander Schulten
Browse files

Add virtual led consumer to provide a led visualization in the application. Closes #32

parent 5a2cfdd3
Pipeline #173147 passed with stage
in 1 minute and 48 seconds
......@@ -43,6 +43,7 @@ SOURCES += \
dmx/programm.cpp \
dmx/programmprototype.cpp \
modules/dmxconsumer.cpp \
modules/ledconsumer.cpp \
scanner.cpp \
test/testloopprogramm.cpp \
settings.cpp \
......@@ -104,6 +105,7 @@ HEADERS += \
dmx/namedobject.h \
dmx/dmxchannelfilter.h \
modules/controlpoint.hpp \
modules/ledconsumer.h \
modules/scanner.hpp \
scanner.h \
updater.h \
......
......@@ -48,6 +48,7 @@
#include "updater.h"
#include <QSslSocket>
#include "gui/controlitemdata.h"
#include "modules/ledconsumer.h"
#ifdef DrMinGW
#include "exchndl.h"
......@@ -289,6 +290,7 @@ int main(int argc, char *argv[])
engine.rootContext()->setContextProperty("Settings",&settings);
engine.rootContext()->setContextProperty("spotify",&spotify);
engine.rootContext()->setContextProperty("updater",&updater);
engine.rootContext()->setContextProperty("ledConsumer",&Modules::LedConsumer::allLedConsumer);
QQmlEngine::setObjectOwnership(&Driver::dmxValueModel,QQmlEngine::CppOwnership);
engine.rootContext()->setContextProperty("dmxOutputValues",&Driver::dmxValueModel);
engine.load(QUrl(QLatin1String("qrc:/qml/main.qml")));
......
......@@ -246,11 +246,32 @@ public:
void dataChanged(int index_){
emit QAbstractItemModel::dataChanged(index(index_), index(index_));
}
void dataChanged(int index_, int length){
emit QAbstractItemModel::dataChanged(index(index_), index(index_ + length - 1));
}
typename std::vector<Type>::const_reference operator[](int index)const{
return model[index];
}
void resize(typename std::vector<Type>::size_type size){
auto diff = static_cast<int>(size) - ssize();
if(diff == 0){
return;
}
if(diff > 0) {
beginPushBack(diff);
} else {
beginRemoveRows(QModelIndex(),ssize()+diff, ssize()-1);
}
model.resize(size);
if(diff > 0) {
endPushBack();
} else {
endRemoveRows();
}
}
void clear(){
if(size()==0){
return;
......
#include "ledconsumer.h"
namespace Modules{
LedConsumer::LedConsumer():Consumer(ValueType::RGB),name("No Name"){
allLedConsumer.push_back(this);
name.setName("Name");
name.setDescription("A name to recognize this led strip in the led virtualisation view.");
properties.push_back(&name);
using namespace std::chrono;
startTimer(1s);
}
void LedConsumer::timerEvent(QTimerEvent *event){
if(lastName != name.getString()){
emit nameChanged();
lastName = name.getString();
}
}
void LedConsumer::doStep(time_diff_t diff){
waitCounter += diff;
if(waitCounter >= 15){
waitCounter = 0;
// only update every 15 ms, the screen only has 60 fps
dataChanged(firstChangedIndex, changedLength);
changedLength = 0;
firstChangedIndex = 0;
}
}
void LedConsumer::setInputData(void*data, unsigned int index, unsigned int length){
const auto maxIndex = std::min(index+length,static_cast<unsigned int>(size()));
const auto maxLength = maxIndex - index;
auto * rgbs = static_cast<rgb_t*>(data);
if(!std::equal(rgbs, rgbs + maxLength, getVector().data() + index)){
std::copy(rgbs, rgbs + maxLength, getVector().data() + index);
if(changedLength==0){
firstChangedIndex = static_cast<int>(index);
changedLength = static_cast<int>(maxLength);
}else if(changedLength != static_cast<int>(maxLength) || firstChangedIndex != static_cast<int>(index)){
// another range is changed, so simply mark the whole range as changed
const auto maxIndex = std::max(firstChangedIndex+changedLength, static_cast<int>(index)+static_cast<int>(maxLength));
firstChangedIndex = std::min(firstChangedIndex, static_cast<int>(index));
changedLength = maxIndex - firstChangedIndex;
}
}
}
QHash<int,QByteArray> LedConsumer::roleNames()const{
QHash<int,QByteArray> r = ModelVector::roleNames();
r[RedDataRole] = "r";
r[GreenDataRole] = "g";
r[BlueDataRole] = "b";
r[ColorDataRole] = "ledColor";
return r;
}
QVariant LedConsumer::data(const QModelIndex &index, int role) const{
Q_UNUSED(role);
if(index.row()>=0&&index.row()<int(size())){
switch (role) {
case RedDataRole:
return getVector()[index.row()].r;
case GreenDataRole:
return getVector()[index.row()].g;
case BlueDataRole:
return getVector()[index.row()].b;
case ColorDataRole:
const auto & rgb = getVector()[index.row()];
return QColor(rgb.r, rgb.g, rgb.b);
}
}
return QVariant();
}
} // namespace Modules
#ifndef LEDCONSUMER_H
#define LEDCONSUMER_H
#include "consumer.hpp"
#include "modelvector.h"
#include <QColor>
#include <QObject>
#include <cmath>
namespace Modules {
class LedConsumer;
class LedConsumer : public ModelVector<rgb_t>, public Consumer
{
Q_OBJECT
Q_PROPERTY(bool active MEMBER active NOTIFY activeChanged)
Q_PROPERTY(QString name READ getNameFromUser NOTIFY nameChanged)
StringProperty name;
std::string lastName;
int firstChangedIndex = 0;
int changedLength = 0;
int waitCounter = 0;
bool active = false;
protected:
void timerEvent(QTimerEvent*event);
public:
inline static ModelVector<LedConsumer*> allLedConsumer;
LedConsumer();
~LedConsumer()override{
allLedConsumer.removeAll(this);
}
QString getNameFromUser()const{
return QString::fromStdString(name.getString());
}
void setInputLength(unsigned int size) override{
resize(size);
}
unsigned int getInputLength()const override{
return static_cast<unsigned int>(size());
}
const char* getName()const override{
return "Virtual LED Consumer";
}
void start() override {active = true;emit activeChanged();}
void stop() override {active = false;emit activeChanged();}
void show() override {}
void doStep(time_diff_t diff) override;
void setInputData(void*data, unsigned int index, unsigned int length) override;
enum{
RedDataRole = Qt::UserRole+1,
GreenDataRole,
BlueDataRole,
ColorDataRole,
};
QHash<int,QByteArray> roleNames()const override;
QVariant data(const QModelIndex &index, int role) const override;
signals:
void activeChanged();
void nameChanged();
};
} // namespace Modules
Q_DECLARE_METATYPE(Modules::rgb_t)
#endif // LEDCONSUMER_H
......@@ -9,6 +9,7 @@
#include "dmxconsumer.h"
#include "scanner.hpp"
#include "scanner.h"
#include "ledconsumer.h"
namespace Modules {
......@@ -85,6 +86,10 @@ typedef Modules::Program* (*CreateProgramm)(unsigned int index);
consumer.emplace("DMXConsumer","With the DMXConsumer class you can write to the DMX Channels",-1,[](){
return new DMXConsumer;
});
// add virtual led consumer to test
consumer.emplace("Virtual LED Consumer","With this led consumer you can view the led stripes in the light control application",-1,[](){
return new LedConsumer;
});
}
void ModuleManager::setSpotify(Spotify::Spotify *s){
......
......@@ -41,6 +41,12 @@ namespace Modules {
this->b *= b / 255.f;
return *this;
}
bool operator==(rgb_t other)const{
return r == other.r && g == other.g && b == other.b;
}
bool operator!=(rgb_t other)const{
return !(*this==other);
}
};
static_assert (sizeof (rgb_t)==3, "size of rgb_t is not 3");
......
......@@ -76,5 +76,7 @@
<file>qml/HelpSystem/Help.qml</file>
<file>qml/HelpSystem/HelpButton.qml</file>
<file>qml/HelpSystem/HelpEntry.qml</file>
<file>qml/LedVisualisation/LedWindow.qml</file>
<file>qml/LedVisualisation/LedVisualisationView.qml</file>
</qresource>
</RCC>
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Window 2.12
import QtQuick.Layouts 1.12
ColumnLayout{
property bool inOwnWindow: false
signal moveToOwnWindow;
Label{
visible: !inOwnWindow
Layout.margins: 5
Layout.fillWidth: true
wrapMode: "WordWrap"
text: "Here you can see a visualisation of the LED stripes used in the Program Block view. The refresh rate is 60 Hz, so the real stripe will look different with a refresh rate up to 1000 Hz."
}
ListView{
Layout.margins: 5
Layout.fillHeight: true
Layout.fillWidth: true
model: ledConsumer
implicitHeight: contentHeight
id: listView
clip: true
delegate:ColumnLayout{
width: listView.width
Label{
Layout.fillWidth: true
text: modelData.name + (modelData.active? " (running)" : " (not running)");
}
Flow{
Layout.fillWidth: true
Layout.bottomMargin: 10
width: listView.width
Repeater{
model: modelData
Rectangle{
width: 10
height: 10
radius: 5
color: ledColor
}
}
}
}
}
Button{
visible: !inOwnWindow
Layout.margins: 5
text: "Move into own window"
onClicked: moveToOwnWindow();
}
}
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Window 2.12
import ".."
Window{
property VerticalTabButton button: null
property LedVisualisationView view: null
flags: Qt.WindowStaysOnTopHint | Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
title: "Virtual LED Stripes"
// TODO: reuse component from SwipeView (does not work on first try)
LedVisualisationView{
id: root
anchors.fill: parent
inOwnWindow: true
}
}
......@@ -4,5 +4,5 @@ import QtQuick.Controls 2.4
TabButton {
topPadding: 6
bottomPadding: 6
width: parent.width
width: parent ? parent.width : 100
}
import QtQuick 2.7
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.0
import QtQuick.Layouts 1.12
import custom.licht 1.0
import "ControlPane"
import "components"
import "HelpSystem"
import "LedVisualisation"
ApplicationWindow {
visible: true
......@@ -106,6 +107,9 @@ ApplicationWindow {
text: qsTr("Module\nPrograms")
enabled: UserManagment.currentUser.havePermission(Permission.MODULE_PROGRAMS_TAB);
}
VerticalTabButton {
text: qsTr("LED\nVisualisations")
}
VerticalTabButton {
text: qsTr("Graph")
}
......@@ -221,6 +225,10 @@ ApplicationWindow {
enabled: UserManagment.currentUser.havePermission(Permission.MODULE_PROGRAMS_TAB);
}
LedVisualisationView{
onMoveToOwnWindow: ledWindow.moveToWindow(SwipeView.index);
}
FFTGraphView{}
Oscillogram{
......@@ -230,6 +238,32 @@ ApplicationWindow {
}
}
LedWindow{
id: ledWindow
property int insertAtIndex
function moveToWindow(index){
let wasNeverVisible = x === 0;
const globalPos = swipeView.itemAt(index).mapToGlobal(0,0);
if(wasNeverVisible) x = globalPos.x
view = swipeView.takeItem(index);
if(wasNeverVisible){
width = view.width;
y = globalPos.y + view.visibleChildren[0].height + 15 /*margin + spacing*/;
}
view.inOwnWindow = true;
if(wasNeverVisible) height = Math.min(view.height, view.implicitHeight);
visible = true;
insertAtIndex = index;
button = tabBar.takeItem(index);
}
onClosing: {
view.inOwnWindow = false;
swipeView.insertItem(insertAtIndex,view);
tabBar.insertItem(insertAtIndex, button);
}
}
Dialog{
modal: true
title: "Error"
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment