Implemented basic functionality
This commit is contained in:
49
CMakeLists.txt
Normal file
49
CMakeLists.txt
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
project(lightcontrol VERSION 0.1 LANGUAGES CXX)
|
||||||
|
|
||||||
|
set(CMAKE_AUTOMOC ON)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
find_package(Qt6 6.2 REQUIRED COMPONENTS SerialPort Quick)
|
||||||
|
|
||||||
|
qt_add_executable(applightcontrol
|
||||||
|
main.cpp
|
||||||
|
devicetypesmodel.h devicetypesmodel.cpp
|
||||||
|
dmxcontroller.h dmxcontroller.cpp
|
||||||
|
dmxcontrollerthread.h dmxcontrollerthread.cpp
|
||||||
|
lightproject.h lightproject.cpp
|
||||||
|
devicesmodel.h devicesmodel.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
qt_add_qml_module(applightcontrol
|
||||||
|
URI lightcontrol
|
||||||
|
VERSION 1.0
|
||||||
|
QML_FILES
|
||||||
|
main.qml
|
||||||
|
main-dev.qml
|
||||||
|
HomePage.qml
|
||||||
|
SettingsPage.qml
|
||||||
|
LightControlWindow.qml
|
||||||
|
EditableListView.qml
|
||||||
|
DeviceTypesSettingsPage.qml
|
||||||
|
DevicesSettingsPage.qml
|
||||||
|
)
|
||||||
|
|
||||||
|
set_target_properties(applightcontrol PROPERTIES
|
||||||
|
MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
|
||||||
|
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
|
||||||
|
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
|
||||||
|
MACOSX_BUNDLE TRUE
|
||||||
|
WIN32_EXECUTABLE TRUE
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(applightcontrol
|
||||||
|
PRIVATE
|
||||||
|
Qt6::SerialPort
|
||||||
|
Qt6::Quick
|
||||||
|
)
|
||||||
|
|
||||||
|
install(TARGETS applightcontrol
|
||||||
|
BUNDLE DESTINATION .
|
||||||
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
60
DeviceTypesSettingsPage.qml
Normal file
60
DeviceTypesSettingsPage.qml
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls.Material
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Label {
|
||||||
|
text: qsTr("Device Types Settings")
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
//Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
EditableListView {
|
||||||
|
id: listView
|
||||||
|
|
||||||
|
Layout.preferredWidth: 300
|
||||||
|
Layout.maximumWidth: 300
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
model: deviceTypesModel
|
||||||
|
|
||||||
|
onAddClicked: (index) => console.log('added', index);
|
||||||
|
onRemoveClicked: (index) => deviceTypesModel.removeRow(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
GridLayout {
|
||||||
|
Layout.preferredWidth: 300
|
||||||
|
Layout.maximumWidth: 300
|
||||||
|
|
||||||
|
columns: 2
|
||||||
|
|
||||||
|
Label { text: qsTr("Id:") }
|
||||||
|
SpinBox {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
value: listView.currentItem.myData.id
|
||||||
|
onValueModified: listView.currentItem.myData.id = value
|
||||||
|
}
|
||||||
|
Label { text: qsTr("Name:") }
|
||||||
|
TextField {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: listView.currentItem.myData.name
|
||||||
|
onTextEdited: listView.currentItem.myData.name = text
|
||||||
|
}
|
||||||
|
Label { text: qsTr("Registers:") }
|
||||||
|
Pane {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
EditableListView {
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
67
DevicesSettingsPage.qml
Normal file
67
DevicesSettingsPage.qml
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls.Material
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Label {
|
||||||
|
text: qsTr("Devices Settings")
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
//Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
EditableListView {
|
||||||
|
id: listView
|
||||||
|
|
||||||
|
Layout.preferredWidth: 300
|
||||||
|
Layout.maximumWidth: 300
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
model: devicesModel
|
||||||
|
|
||||||
|
onAddClicked: (index) => console.log('added', index)
|
||||||
|
onRemoveClicked: (index) => devicesModel.removeRow(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
GridLayout {
|
||||||
|
Layout.preferredWidth: 300
|
||||||
|
Layout.maximumWidth: 300
|
||||||
|
|
||||||
|
columns: 2
|
||||||
|
|
||||||
|
Label { text: qsTr("Id:") }
|
||||||
|
SpinBox {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
value: listView.currentItem.myData.id
|
||||||
|
onValueModified: listView.currentItem.myData.id = value
|
||||||
|
}
|
||||||
|
Label { text: qsTr("Name:") }
|
||||||
|
TextField {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: listView.currentItem.myData.name
|
||||||
|
onTextEdited: listView.currentItem.myData.name = text
|
||||||
|
}
|
||||||
|
Label { text: qsTr("DeviceType:") }
|
||||||
|
ComboBox {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
model: deviceTypesModel
|
||||||
|
textRole: "name"
|
||||||
|
valueRole: "id"
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
Label { text: qsTr("Address:") }
|
||||||
|
SpinBox {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
value: listView.currentItem.myData.address
|
||||||
|
onValueModified: listView.currentItem.myData.address = value
|
||||||
|
}
|
||||||
|
Label { text: qsTr("Position:") }
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
72
EditableListView.qml
Normal file
72
EditableListView.qml
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls.Material
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
property alias model: listView.model
|
||||||
|
property alias currentIndex: listView.currentIndex
|
||||||
|
property alias currentItem: listView.currentItem
|
||||||
|
|
||||||
|
signal addClicked(index: int)
|
||||||
|
signal removeClicked(index: int)
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
Button {
|
||||||
|
text: qsTr("Add")
|
||||||
|
|
||||||
|
onClicked: addClicked(listView.currentIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
text: qsTr("Remove")
|
||||||
|
|
||||||
|
onClicked: removeClicked(listView.currentIndex)
|
||||||
|
enabled: listView.currentIndex >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: listView
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
property variant myData: model
|
||||||
|
|
||||||
|
width: listView.width
|
||||||
|
height: 40
|
||||||
|
|
||||||
|
color: ListView.isCurrentItem ?
|
||||||
|
Material.color(Material.Purple) :
|
||||||
|
Material.color(Material.Purple, Material.Shade900)
|
||||||
|
radius: 5
|
||||||
|
|
||||||
|
Label {
|
||||||
|
anchors.fill: parent
|
||||||
|
//anchors.verticalCenter: parent.verticalCenter
|
||||||
|
id: text
|
||||||
|
text: model.name
|
||||||
|
padding: 5
|
||||||
|
fontSizeMode: Text.VerticalFit
|
||||||
|
minimumPixelSize: 10;
|
||||||
|
font.pixelSize: 72
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: listView.currentIndex = index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
focus: true
|
||||||
|
}
|
||||||
|
}
|
71
HomePage.qml
Normal file
71
HomePage.qml
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls.Material
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
Flow {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: devicesModel
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
width: 50
|
||||||
|
height: 200
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: index
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Slider {
|
||||||
|
id: slider
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
orientation: Qt.Vertical
|
||||||
|
from: 0
|
||||||
|
to: 255
|
||||||
|
|
||||||
|
onValueChanged: __controller.setChannel(32 + index, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: Math.round(slider.value)
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.preferredWidth: 100
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
Button {
|
||||||
|
Layout.preferredHeight: 100
|
||||||
|
|
||||||
|
text: qsTr('Settings')
|
||||||
|
|
||||||
|
onClicked: stackview.push(settingsPage)
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: settingsPage
|
||||||
|
|
||||||
|
SettingsPage {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
189
LightControlWindow.qml
Normal file
189
LightControlWindow.qml
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls.Material
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Window
|
||||||
|
import QtQuick.VirtualKeyboard
|
||||||
|
|
||||||
|
import com.büro 1.0
|
||||||
|
|
||||||
|
ApplicationWindow {
|
||||||
|
id: window
|
||||||
|
width: 1360
|
||||||
|
height: 768
|
||||||
|
title: qsTr("Hello World")
|
||||||
|
|
||||||
|
Material.theme: Material.Dark
|
||||||
|
Material.accent: Material.Purple
|
||||||
|
|
||||||
|
DeviceTypesModel {
|
||||||
|
id: deviceTypesModel
|
||||||
|
controller: __controller
|
||||||
|
}
|
||||||
|
|
||||||
|
DevicesModel {
|
||||||
|
id: devicesModel
|
||||||
|
controller: __controller
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Pane {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 75
|
||||||
|
|
||||||
|
Material.elevation: 6
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
text: qsTr("Schein-Commander")
|
||||||
|
fontSizeMode: Text.VerticalFit
|
||||||
|
minimumPixelSize: 10;
|
||||||
|
font.pixelSize: 72
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: timeText
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
text: Qt.formatTime(new Date(), "hh:mm:ss")
|
||||||
|
fontSizeMode: Text.VerticalFit
|
||||||
|
minimumPixelSize: 10;
|
||||||
|
font.pixelSize: 72
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: timer
|
||||||
|
interval: 1000
|
||||||
|
repeat: true
|
||||||
|
running: true
|
||||||
|
|
||||||
|
onTriggered: timeText.text = Qt.formatTime(new Date(), "hh:mm:ss");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
text: qsTr("Back")
|
||||||
|
|
||||||
|
onClicked: stackview.pop();
|
||||||
|
enabled: stackview.depth > 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StackView {
|
||||||
|
id: stackview
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
initialItem: homePage
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: homePage
|
||||||
|
|
||||||
|
HomePage {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pushEnter: Transition {
|
||||||
|
PropertyAnimation {
|
||||||
|
property: "opacity"
|
||||||
|
from: 0
|
||||||
|
to:1
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pushExit: Transition {
|
||||||
|
PropertyAnimation {
|
||||||
|
property: "opacity"
|
||||||
|
from: 1
|
||||||
|
to:0
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
popEnter: Transition {
|
||||||
|
PropertyAnimation {
|
||||||
|
property: "opacity"
|
||||||
|
from: 0
|
||||||
|
to:1
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
popExit: Transition {
|
||||||
|
PropertyAnimation {
|
||||||
|
property: "opacity"
|
||||||
|
from: 1
|
||||||
|
to:0
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: closeButton
|
||||||
|
|
||||||
|
visible: inputPanel.active
|
||||||
|
|
||||||
|
anchors.bottom: inputPanel.top
|
||||||
|
anchors.right: parent.right
|
||||||
|
z: 99
|
||||||
|
|
||||||
|
//onClicked: inputPanel.active = false
|
||||||
|
// onClicked: InputContext.priv.hideInputPanel()
|
||||||
|
|
||||||
|
text: qsTr("Close keyboard?")
|
||||||
|
|
||||||
|
focus: false
|
||||||
|
}
|
||||||
|
|
||||||
|
InputPanel {
|
||||||
|
id: inputPanel
|
||||||
|
z: 99
|
||||||
|
x: 0
|
||||||
|
y: window.height
|
||||||
|
width: window.width
|
||||||
|
|
||||||
|
states: State {
|
||||||
|
name: "visible"
|
||||||
|
when: inputPanel.active
|
||||||
|
PropertyChanges {
|
||||||
|
target: inputPanel
|
||||||
|
y: window.height - inputPanel.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
transitions: [
|
||||||
|
Transition {
|
||||||
|
from: "visible"
|
||||||
|
to: ""
|
||||||
|
reversible: false
|
||||||
|
ParallelAnimation {
|
||||||
|
NumberAnimation {
|
||||||
|
properties: "y"
|
||||||
|
duration: 1000
|
||||||
|
easing.type: Easing.OutBounce
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Transition {
|
||||||
|
from: ""
|
||||||
|
to: "visible"
|
||||||
|
reversible: false
|
||||||
|
ParallelAnimation {
|
||||||
|
NumberAnimation {
|
||||||
|
properties: "y"
|
||||||
|
duration: 1000
|
||||||
|
easing.type: Easing.OutBounce
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
10
README.md
10
README.md
@ -1,2 +1,12 @@
|
|||||||
# scheincommander
|
# scheincommander
|
||||||
A qt quick application to control dmx moving heads / lights / fog machines (running on a raspberry pi)
|
A qt quick application to control dmx moving heads / lights / fog machines (running on a raspberry pi)
|
||||||
|
|
||||||
|
## How to build
|
||||||
|
|
||||||
|
```
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
qmake6 ..
|
||||||
|
make -j4096
|
||||||
|
./applightcontrol
|
||||||
|
```
|
||||||
|
47
SettingsPage.qml
Normal file
47
SettingsPage.qml
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls.Material
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Label {
|
||||||
|
text: qsTr("Settings")
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: button0
|
||||||
|
text: qsTr("Device\nTypes")
|
||||||
|
|
||||||
|
Layout.preferredWidth: 100
|
||||||
|
Layout.preferredHeight: 100
|
||||||
|
|
||||||
|
onClicked: stackview.push(deviceTypesSettingsPage)
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: deviceTypesSettingsPage
|
||||||
|
|
||||||
|
DeviceTypesSettingsPage {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: button1
|
||||||
|
text: qsTr("Devices")
|
||||||
|
|
||||||
|
Layout.preferredWidth: 100
|
||||||
|
Layout.preferredHeight: 100
|
||||||
|
|
||||||
|
onClicked: stackview.push(devicesSettingsPage)
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: devicesSettingsPage
|
||||||
|
|
||||||
|
DevicesSettingsPage {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
280
devicesmodel.cpp
Normal file
280
devicesmodel.cpp
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
#include "devicesmodel.h"
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QQmlEngine>
|
||||||
|
|
||||||
|
constexpr auto IdRole = Qt::UserRole;
|
||||||
|
constexpr auto LightTypeIdRole = Qt::UserRole + 1;
|
||||||
|
constexpr auto AddressRole = Qt::UserRole + 2;
|
||||||
|
constexpr auto PositionRole = Qt::UserRole + 3;
|
||||||
|
|
||||||
|
DevicesModel::DevicesModel(QObject *parent) :
|
||||||
|
QAbstractItemModel{parent}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void DevicesModel::setController(DmxController *controller)
|
||||||
|
{
|
||||||
|
if (m_controller != controller)
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
m_controller = controller;
|
||||||
|
endResetModel();
|
||||||
|
emit controllerChanged(m_controller);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex DevicesModel::index(int row, int column, const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return createIndex(row, column, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex DevicesModel::parent(const QModelIndex &child) const
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
int DevicesModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_controller)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return m_controller->lightProject().lights.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
int DevicesModel::columnCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant DevicesModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_controller)
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index.row() < 0 || index.row() >= m_controller->lightProject().lights.size())
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index.column() != 0)
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &light = m_controller->lightProject().lights.at(index.row());
|
||||||
|
|
||||||
|
switch (role)
|
||||||
|
{
|
||||||
|
case Qt::DisplayRole:
|
||||||
|
case Qt::EditRole: return light.name;
|
||||||
|
case IdRole: return light.id;
|
||||||
|
case LightTypeIdRole: return light.lightTypeId;
|
||||||
|
case AddressRole: return light.address;
|
||||||
|
case PositionRole: return light.position;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QMap<int, QVariant> DevicesModel::itemData(const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_controller)
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index.row() < 0 || index.row() >= m_controller->lightProject().lights.size())
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index.column() != 0)
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &light = m_controller->lightProject().lights.at(index.row());
|
||||||
|
|
||||||
|
return {
|
||||||
|
{ Qt::DisplayRole, light.name },
|
||||||
|
{ IdRole, light.id },
|
||||||
|
{ LightTypeIdRole, light.lightTypeId },
|
||||||
|
{ AddressRole, light.address },
|
||||||
|
{ PositionRole, light.position }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> DevicesModel::roleNames() const
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
{ Qt::DisplayRole, "name" },
|
||||||
|
{ IdRole, "id" },
|
||||||
|
{ LightTypeIdRole, "lightTypeId" },
|
||||||
|
{ AddressRole, "address" },
|
||||||
|
{ PositionRole, "position" }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DevicesModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_controller)
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &lights = m_controller->lightProject().lights;
|
||||||
|
if (index.row() < 0 || index.row() >= lights.size())
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index.column() != 0)
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &light = lights.at(index.row());
|
||||||
|
switch (role)
|
||||||
|
{
|
||||||
|
case Qt::DisplayRole:
|
||||||
|
case Qt::EditRole:
|
||||||
|
if (value.userType() != QMetaType::QString)
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__ << value.userType();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
light.name = value.toString();
|
||||||
|
emit dataChanged(index, index, { Qt::DisplayRole, Qt::EditRole });
|
||||||
|
return true;
|
||||||
|
case IdRole:
|
||||||
|
if (value.userType() != QMetaType::Int)
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__ << value.userType();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
light.id = value.toInt();
|
||||||
|
emit dataChanged(index, index, { IdRole });
|
||||||
|
return true;
|
||||||
|
case LightTypeIdRole:
|
||||||
|
if (value.userType() != QMetaType::Int)
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__ << value.userType();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
light.lightTypeId = value.toInt();
|
||||||
|
emit dataChanged(index, index, { LightTypeIdRole });
|
||||||
|
return true;
|
||||||
|
case AddressRole:
|
||||||
|
if (value.userType() != QMetaType::Int)
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__ << value.userType();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
light.address = value.toInt();
|
||||||
|
emit dataChanged(index, index, { AddressRole });
|
||||||
|
return true;
|
||||||
|
case PositionRole:
|
||||||
|
if (value.userType() != QMetaType::QVector3D)
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__ << value.userType();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
light.position = value.value<QVector3D>();
|
||||||
|
emit dataChanged(index, index, { AddressRole });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DevicesModel::removeRows(int row, int count, const QModelIndex &parent)
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_controller)
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &lights = m_controller->lightProject().lights;
|
||||||
|
|
||||||
|
if (row >= lights.size())
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (row + count >= lights.size())
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
beginRemoveRows({}, row, row+count-1);
|
||||||
|
auto begin = std::begin(lights) + row;
|
||||||
|
auto end = begin + count;
|
||||||
|
lights.erase(begin, end);
|
||||||
|
endRemoveRows();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
void registerDenShit()
|
||||||
|
{
|
||||||
|
qmlRegisterType<DevicesModel>("com.büro", 1, 0, "DevicesModel");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Q_COREAPP_STARTUP_FUNCTION(registerDenShit)
|
35
devicesmodel.h
Normal file
35
devicesmodel.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QAbstractItemModel>
|
||||||
|
|
||||||
|
#include "dmxcontroller.h"
|
||||||
|
|
||||||
|
class DevicesModel : public QAbstractItemModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(DmxController* controller READ controller WRITE setController NOTIFY controllerChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit DevicesModel(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
DmxController *controller() { return m_controller; }
|
||||||
|
const DmxController *controller() const { return m_controller; }
|
||||||
|
void setController(DmxController *controller);
|
||||||
|
|
||||||
|
QModelIndex index(int row, int column, const QModelIndex &parent) const override;
|
||||||
|
QModelIndex parent(const QModelIndex &child) const override;
|
||||||
|
int rowCount(const QModelIndex &parent) const override;
|
||||||
|
int columnCount(const QModelIndex &parent) const override;
|
||||||
|
QVariant data(const QModelIndex &index, int role) const override;
|
||||||
|
QMap<int, QVariant> itemData(const QModelIndex &index) const override;
|
||||||
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
|
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
|
||||||
|
bool removeRows(int row, int count, const QModelIndex &parent) override;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void controllerChanged(DmxController *controller);
|
||||||
|
|
||||||
|
private:
|
||||||
|
DmxController *m_controller{};
|
||||||
|
};
|
241
devicetypesmodel.cpp
Normal file
241
devicetypesmodel.cpp
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
#include "devicetypesmodel.h"
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QQmlEngine>
|
||||||
|
|
||||||
|
constexpr auto IdRole = Qt::UserRole;
|
||||||
|
|
||||||
|
DeviceTypesModel::DeviceTypesModel(QObject *parent) :
|
||||||
|
QAbstractItemModel{parent}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceTypesModel::setController(DmxController *controller)
|
||||||
|
{
|
||||||
|
if (m_controller != controller)
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
m_controller = controller;
|
||||||
|
endResetModel();
|
||||||
|
emit controllerChanged(m_controller);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex DeviceTypesModel::index(int row, int column, const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return createIndex(row, column, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex DeviceTypesModel::parent(const QModelIndex &child) const
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
int DeviceTypesModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_controller)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return m_controller->lightProject().lightTypes.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
int DeviceTypesModel::columnCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant DeviceTypesModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_controller)
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index.row() < 0 || index.row() >= m_controller->lightProject().lightTypes.size())
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index.column() != 0)
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &lightType = m_controller->lightProject().lightTypes.at(index.row());
|
||||||
|
|
||||||
|
switch (role)
|
||||||
|
{
|
||||||
|
case Qt::DisplayRole:
|
||||||
|
case Qt::EditRole: return lightType.name;
|
||||||
|
case IdRole: return lightType.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QMap<int, QVariant> DeviceTypesModel::itemData(const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_controller)
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index.row() < 0 || index.row() >= m_controller->lightProject().lightTypes.size())
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index.column() != 0)
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &lightType = m_controller->lightProject().lightTypes.at(index.row());
|
||||||
|
|
||||||
|
return {
|
||||||
|
{ Qt::DisplayRole, lightType.name },
|
||||||
|
{ IdRole, lightType.id }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> DeviceTypesModel::roleNames() const
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
{ Qt::DisplayRole, "name" },
|
||||||
|
{ IdRole, "id" }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeviceTypesModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_controller)
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &lightTypes = m_controller->lightProject().lightTypes;
|
||||||
|
if (index.row() < 0 || index.row() >= lightTypes.size())
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index.column() != 0)
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &lightType = lightTypes.at(index.row());
|
||||||
|
switch (role)
|
||||||
|
{
|
||||||
|
case Qt::DisplayRole:
|
||||||
|
case Qt::EditRole:
|
||||||
|
if (value.userType() != QMetaType::QString)
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__ << value.userType();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
lightType.name = value.toString();
|
||||||
|
emit dataChanged(index, index, { Qt::DisplayRole, Qt::EditRole });
|
||||||
|
return true;
|
||||||
|
case IdRole:
|
||||||
|
if (value.userType() != QMetaType::Int)
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__ << value.userType();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
lightType.id = value.toInt();
|
||||||
|
emit dataChanged(index, index, { IdRole });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeviceTypesModel::removeRows(int row, int count, const QModelIndex &parent)
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_controller)
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &lightTypes = m_controller->lightProject().lightTypes;
|
||||||
|
|
||||||
|
if (row >= lightTypes.size())
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (row + count > lightTypes.size())
|
||||||
|
{
|
||||||
|
qWarning() << "hilfe" << __LINE__;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
beginRemoveRows({}, row, row+count-1);
|
||||||
|
auto begin = std::begin(lightTypes) + row;
|
||||||
|
auto end = begin + count;
|
||||||
|
lightTypes.erase(begin, end);
|
||||||
|
endRemoveRows();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
void registerDenShit()
|
||||||
|
{
|
||||||
|
qmlRegisterType<DeviceTypesModel>("com.büro", 1, 0, "DeviceTypesModel");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Q_COREAPP_STARTUP_FUNCTION(registerDenShit)
|
35
devicetypesmodel.h
Normal file
35
devicetypesmodel.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QAbstractItemModel>
|
||||||
|
|
||||||
|
#include "dmxcontroller.h"
|
||||||
|
|
||||||
|
class DeviceTypesModel : public QAbstractItemModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(DmxController* controller READ controller WRITE setController NOTIFY controllerChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit DeviceTypesModel(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
DmxController *controller() { return m_controller; }
|
||||||
|
const DmxController *controller() const { return m_controller; }
|
||||||
|
void setController(DmxController *controller);
|
||||||
|
|
||||||
|
QModelIndex index(int row, int column, const QModelIndex &parent) const override;
|
||||||
|
QModelIndex parent(const QModelIndex &child) const override;
|
||||||
|
int rowCount(const QModelIndex &parent) const override;
|
||||||
|
int columnCount(const QModelIndex &parent) const override;
|
||||||
|
QVariant data(const QModelIndex &index, int role) const override;
|
||||||
|
QMap<int, QVariant> itemData(const QModelIndex &index) const override;
|
||||||
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
|
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
|
||||||
|
bool removeRows(int row, int count, const QModelIndex &parent) override;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void controllerChanged(DmxController *controller);
|
||||||
|
|
||||||
|
private:
|
||||||
|
DmxController *m_controller{};
|
||||||
|
};
|
109
dmxcontroller.cpp
Normal file
109
dmxcontroller.cpp
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
#include "dmxcontroller.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
DmxController::DmxController(QObject *parent) :
|
||||||
|
QObject{parent},
|
||||||
|
m_thread{*this},
|
||||||
|
m_lastInfo{QDateTime::currentDateTime()},
|
||||||
|
m_counter{},
|
||||||
|
m_lightProject {
|
||||||
|
.lightTypes {
|
||||||
|
{ .id=0, .name="Stairville MH-X50+" },
|
||||||
|
{ .id=1, .name="RGBW Strahler" },
|
||||||
|
{ .id=2, .name="RGB Strahler" },
|
||||||
|
{ .id=3, .name="Nebelmaschine" }
|
||||||
|
},
|
||||||
|
.lights {
|
||||||
|
{ .id=0, .name="Lampe 1", .lightTypeId=1, .address=32 },
|
||||||
|
{ .id=1, .name="Lampe 2", .lightTypeId=1, .address=0 },
|
||||||
|
{ .id=2, .name="Lampe 3", .lightTypeId=1, .address=7 },
|
||||||
|
{ .id=3, .name="Lampe 4", .lightTypeId=1, .address=14 },
|
||||||
|
{ .id=4, .name="Moving Head 1", .lightTypeId=0, .address=40 },
|
||||||
|
{ .id=5, .name="Moving Head 2", .lightTypeId=0, .address=43 },
|
||||||
|
{ .id=6, .name="Moving Head 3", .lightTypeId=0, .address=46 },
|
||||||
|
{ .id=7, .name="Moving Head 4", .lightTypeId=0, .address=49 },
|
||||||
|
{ .id=8, .name="Test 1", .lightTypeId=2, .address=70 },
|
||||||
|
{ .id=9, .name="Test 2", .lightTypeId=2, .address=72 },
|
||||||
|
{ .id=10, .name="Nebelmaschine", .lightTypeId=3, .address=80 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::fill(std::begin(buf), std::end(buf), 0);
|
||||||
|
|
||||||
|
buf[32] = 255;
|
||||||
|
buf[33] = 255;
|
||||||
|
buf[34] = 0;
|
||||||
|
buf[35] = 0;
|
||||||
|
// buf[36] = 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DmxController::start()
|
||||||
|
{
|
||||||
|
m_serialPort.setPortName("/dev/ttyAMA0");
|
||||||
|
if (!m_serialPort.setBaudRate(250000))
|
||||||
|
{
|
||||||
|
qCritical("setBaudRate() failed %s", qPrintable(m_serialPort.errorString()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!m_serialPort.setDataBits(QSerialPort::Data8))
|
||||||
|
{
|
||||||
|
qCritical("setDataBits() failed %s", qPrintable(m_serialPort.errorString()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!m_serialPort.setParity(QSerialPort::NoParity))
|
||||||
|
{
|
||||||
|
qCritical("setParity() failed %s", qPrintable(m_serialPort.errorString()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!m_serialPort.setStopBits(QSerialPort::TwoStop))
|
||||||
|
{
|
||||||
|
qCritical("setStopBits() failed %s", qPrintable(m_serialPort.errorString()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!m_serialPort.setFlowControl(QSerialPort::NoFlowControl))
|
||||||
|
{
|
||||||
|
qCritical("setFlowControl() failed %s", qPrintable(m_serialPort.errorString()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!m_serialPort.open(QIODevice::WriteOnly))
|
||||||
|
{
|
||||||
|
qCritical("open() failed %s", qPrintable(m_serialPort.errorString()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_thread.start(QThread::TimeCriticalPriority);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DmxController::setChannel(int channel, int value)
|
||||||
|
{
|
||||||
|
buf[channel] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DmxController::sendDmxBuffer()
|
||||||
|
{
|
||||||
|
const auto now = QDateTime::currentDateTime();
|
||||||
|
|
||||||
|
m_serialPort.setBreakEnabled(true);
|
||||||
|
QThread::usleep(88);
|
||||||
|
m_serialPort.setBreakEnabled(false);
|
||||||
|
|
||||||
|
// const auto written = write(m_serialPort.handle(), buf, std::size(buf));
|
||||||
|
const auto written = m_serialPort.write(buf, std::size(buf));
|
||||||
|
m_serialPort.flush();
|
||||||
|
// qDebug("%lli written", written);
|
||||||
|
|
||||||
|
m_counter++;
|
||||||
|
|
||||||
|
if (m_lastInfo.msecsTo(now) >= 1000)
|
||||||
|
{
|
||||||
|
qInfo("%i per second ä", m_counter);
|
||||||
|
m_counter = 0;
|
||||||
|
m_lastInfo = now;
|
||||||
|
}
|
||||||
|
}
|
44
dmxcontroller.h
Normal file
44
dmxcontroller.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QSerialPort>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QReadWriteLock>
|
||||||
|
|
||||||
|
#include "dmxcontrollerthread.h"
|
||||||
|
#include "lightproject.h"
|
||||||
|
|
||||||
|
class DmxController : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit DmxController(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
bool start();
|
||||||
|
|
||||||
|
Q_INVOKABLE void setChannel(int channel, int value);
|
||||||
|
|
||||||
|
LightProject &lightProject() { return m_lightProject; }
|
||||||
|
const LightProject &lightProject() const { return m_lightProject; }
|
||||||
|
|
||||||
|
QReadWriteLock &projectLock() { return m_projectLock; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
friend class DmxControllerThread;
|
||||||
|
|
||||||
|
void sendDmxBuffer(); // runs in its own thread
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSerialPort m_serialPort;
|
||||||
|
|
||||||
|
DmxControllerThread m_thread;
|
||||||
|
|
||||||
|
char buf[513];
|
||||||
|
|
||||||
|
LightProject m_lightProject;
|
||||||
|
QReadWriteLock m_projectLock;
|
||||||
|
|
||||||
|
QDateTime m_lastInfo;
|
||||||
|
int m_counter;
|
||||||
|
};
|
15
dmxcontrollerthread.cpp
Normal file
15
dmxcontrollerthread.cpp
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#include "dmxcontrollerthread.h"
|
||||||
|
|
||||||
|
#include "dmxcontroller.h"
|
||||||
|
|
||||||
|
DmxControllerThread::DmxControllerThread(DmxController &controller) :
|
||||||
|
QThread{&controller},
|
||||||
|
m_controller{controller}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void DmxControllerThread::run()
|
||||||
|
{
|
||||||
|
while (!isInterruptionRequested())
|
||||||
|
m_controller.sendDmxBuffer();
|
||||||
|
}
|
18
dmxcontrollerthread.h
Normal file
18
dmxcontrollerthread.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
|
class DmxController;
|
||||||
|
|
||||||
|
class DmxControllerThread : public QThread
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit DmxControllerThread(DmxController &controller);
|
||||||
|
|
||||||
|
void run() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
DmxController &m_controller;
|
||||||
|
};
|
1
lightproject.cpp
Normal file
1
lightproject.cpp
Normal file
@ -0,0 +1 @@
|
|||||||
|
#include "lightproject.h"
|
27
lightproject.h
Normal file
27
lightproject.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QVector3D>
|
||||||
|
|
||||||
|
struct LightTypeConfig
|
||||||
|
{
|
||||||
|
int id;
|
||||||
|
QString name;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LightConfig
|
||||||
|
{
|
||||||
|
int id;
|
||||||
|
QString name;
|
||||||
|
int lightTypeId;
|
||||||
|
int address;
|
||||||
|
QVector3D position;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LightProject
|
||||||
|
{
|
||||||
|
std::vector<LightTypeConfig> lightTypes;
|
||||||
|
std::vector<LightConfig> lights;
|
||||||
|
};
|
5
main-dev.qml
Normal file
5
main-dev.qml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import QtQuick
|
||||||
|
|
||||||
|
LightControlWindow {
|
||||||
|
visible: true
|
||||||
|
}
|
61
main.cpp
Normal file
61
main.cpp
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#include <QGuiApplication>
|
||||||
|
#include <QCommandLineParser>
|
||||||
|
#include <QQmlApplicationEngine>
|
||||||
|
#include <QQmlContext>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "dmxcontroller.h"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
qSetMessagePattern(QStringLiteral("%{time dd.MM.yyyy HH:mm:ss.zzz} "
|
||||||
|
"["
|
||||||
|
"%{if-debug}D%{endif}"
|
||||||
|
"%{if-info}I%{endif}"
|
||||||
|
"%{if-warning}W%{endif}"
|
||||||
|
"%{if-critical}C%{endif}"
|
||||||
|
"%{if-fatal}F%{endif}"
|
||||||
|
"] "
|
||||||
|
"%{function}(): "
|
||||||
|
"%{message}"));
|
||||||
|
|
||||||
|
qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard"));
|
||||||
|
|
||||||
|
QGuiApplication app{argc, argv};
|
||||||
|
|
||||||
|
QCommandLineParser parser;
|
||||||
|
parser.addHelpOption();
|
||||||
|
parser.addVersionOption();
|
||||||
|
|
||||||
|
QCommandLineOption windowedOption {
|
||||||
|
QStringList{"w", "windowed"},
|
||||||
|
QCoreApplication::translate("main", "Show in windowed (only dev)")
|
||||||
|
};
|
||||||
|
parser.addOption(windowedOption);
|
||||||
|
|
||||||
|
if (!parser.parse(app.arguments()))
|
||||||
|
{
|
||||||
|
qFatal("could not parse arguments!");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto windowed = parser.isSet(windowedOption);
|
||||||
|
|
||||||
|
DmxController controller{&app};
|
||||||
|
if (!controller.start() && !windowed)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
QQmlApplicationEngine engine;
|
||||||
|
|
||||||
|
engine.rootContext()->setContextProperty("__controller", &controller);
|
||||||
|
|
||||||
|
const QUrl url{windowed ? u"qrc:/lightcontrol/main-dev.qml"_qs : u"qrc:/lightcontrol/main.qml"_qs};
|
||||||
|
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
|
||||||
|
&app, [url](QObject *obj, const QUrl &objUrl) {
|
||||||
|
if (!obj && url == objUrl)
|
||||||
|
QCoreApplication::exit(-1);
|
||||||
|
}, Qt::QueuedConnection);
|
||||||
|
engine.load(url);
|
||||||
|
|
||||||
|
return app.exec();
|
||||||
|
}
|
Reference in New Issue
Block a user