mirror of
https://github.com/gtalent/sc9k.git
synced 2025-01-22 22:13:36 -06:00
Add settings dialog, camera controls
This commit is contained in:
parent
a8327b2b67
commit
29cdc499e6
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,10 +1,13 @@
|
|||||||
|
.cache
|
||||||
.clangd
|
.clangd
|
||||||
.conanbuild
|
.conanbuild
|
||||||
.current_build
|
.current_build
|
||||||
|
__pycache__
|
||||||
CMakeLists.txt.user
|
CMakeLists.txt.user
|
||||||
Session.vim
|
Session.vim
|
||||||
build
|
build
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
|
cmake-build-debug
|
||||||
dist
|
dist
|
||||||
graph_info.json
|
graph_info.json
|
||||||
tags
|
tags
|
||||||
|
8
.idea/.gitignore
generated
vendored
8
.idea/.gitignore
generated
vendored
@ -1,8 +0,0 @@
|
|||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
# Datasource local storage ignored files
|
|
||||||
/dataSources/
|
|
||||||
/dataSources.local.xml
|
|
||||||
# Editor-based HTTP Client requests
|
|
||||||
/httpRequests/
|
|
16
.idea/misc.xml
generated
16
.idea/misc.xml
generated
@ -1,17 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="CompDBSettings">
|
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
||||||
<option name="linkedExternalProjectsSettings">
|
<component name="CompDBWorkspace">
|
||||||
<CompDBProjectSettings>
|
<contentRoot DIR="$PROJECT_DIR$" />
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="modules">
|
|
||||||
<set>
|
|
||||||
<option value="$PROJECT_DIR$" />
|
|
||||||
</set>
|
|
||||||
</option>
|
|
||||||
</CompDBProjectSettings>
|
|
||||||
</option>
|
|
||||||
</component>
|
</component>
|
||||||
<component name="CompDBWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
|
||||||
</project>
|
</project>
|
2
.idea/vcs.xml
generated
2
.idea/vcs.xml
generated
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="VcsDirectoryMappings">
|
<component name="VcsDirectoryMappings">
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
<mapping directory="" vcs="Git" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
@ -2,7 +2,7 @@
|
|||||||
source:
|
source:
|
||||||
- .
|
- .
|
||||||
copyright_notice: |-
|
copyright_notice: |-
|
||||||
Copyright 2021 gary@drinkingtea.net
|
Copyright 2021 - 2023 gary@drinkingtea.net
|
||||||
|
|
||||||
This Source Code Form is subject to the terms of the Mozilla Public
|
This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
2
Makefile
2
Makefile
@ -14,4 +14,4 @@ run: install
|
|||||||
${ENV_RUN} ${PROJECT_EXECUTABLE}
|
${ENV_RUN} ${PROJECT_EXECUTABLE}
|
||||||
.PHONY: debug
|
.PHONY: debug
|
||||||
debug: install
|
debug: install
|
||||||
${ENV_RUN} gdb --args ${PROJECT_EXECUTABLE}
|
${DEBUGGER} lldb -- ${PROJECT_EXECUTABLE}
|
||||||
|
@ -33,7 +33,7 @@ class RqstHandler(BaseHTTPRequestHandler):
|
|||||||
|
|
||||||
|
|
||||||
def run(name):
|
def run(name):
|
||||||
httpd = HTTPServer(('127.0.0.1', 9302), RqstHandler)
|
httpd = HTTPServer(('0.0.0.0', 9302), RqstHandler)
|
||||||
httpd.serve_forever()
|
httpd.serve_forever()
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,10 +8,13 @@ find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Network Widgets REQUIRED)
|
|||||||
|
|
||||||
add_executable(
|
add_executable(
|
||||||
SlideController MACOSX_BUNDLE WIN32
|
SlideController MACOSX_BUNDLE WIN32
|
||||||
|
cameraclient.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
mainwindow.cpp
|
mainwindow.cpp
|
||||||
obsclient.cpp
|
obsclient.cpp
|
||||||
openlpclient.cpp
|
openlpclient.cpp
|
||||||
|
settingsdata.cpp
|
||||||
|
settingsdialog.cpp
|
||||||
slideview.cpp
|
slideview.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
54
src/cameraclient.cpp
Normal file
54
src/cameraclient.cpp
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 - 2023 gary@drinkingtea.net
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QSettings>
|
||||||
|
|
||||||
|
#include "settingsdata.hpp"
|
||||||
|
#include "cameraclient.hpp"
|
||||||
|
|
||||||
|
CameraClient::CameraClient(QObject *parent): QObject(parent) {
|
||||||
|
setBaseUrl();
|
||||||
|
m_pollTimer.start(1000);
|
||||||
|
connect(&m_pollTimer, &QTimer::timeout, this, &CameraClient::poll);
|
||||||
|
connect(m_pollingNam, &QNetworkAccessManager::finished, this, &CameraClient::handlePollResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraClient::setPreset(int preset) {
|
||||||
|
if (preset > -1) {
|
||||||
|
get(QString("/cgi-bin/ptzctrl.cgi?ptzcmd&poscall&%1").arg(preset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraClient::setBaseUrl() {
|
||||||
|
const auto [host, port] = getCameraConnectionData();
|
||||||
|
m_baseUrl = QString("http://%1:%2").arg(host, QString::number(port));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraClient::get(QString const&urlExt) {
|
||||||
|
QUrl url(QString(m_baseUrl) + urlExt);
|
||||||
|
QNetworkRequest rqst(url);
|
||||||
|
auto reply = m_nam->get(rqst);
|
||||||
|
connect(reply, &QIODevice::readyRead, reply, &QObject::deleteLater);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraClient::poll() {
|
||||||
|
QUrl url(QString(m_baseUrl) + "/cgi-bin/param.cgi?get_device_conf");
|
||||||
|
QNetworkRequest rqst(url);
|
||||||
|
m_pollingNam->get(rqst);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraClient::handlePollResponse(QNetworkReply *reply) {
|
||||||
|
reply->deleteLater();
|
||||||
|
if (reply->error()) {
|
||||||
|
qDebug() << "CameraClient error response:" << reply->errorString();
|
||||||
|
emit pollFailed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
emit pollUpdate();
|
||||||
|
}
|
46
src/cameraclient.hpp
Normal file
46
src/cameraclient.hpp
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 - 2023 gary@drinkingtea.net
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include "consts.hpp"
|
||||||
|
|
||||||
|
class CameraClient: public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
private:
|
||||||
|
QString m_baseUrl;
|
||||||
|
QNetworkAccessManager *const m_nam = new QNetworkAccessManager(this);
|
||||||
|
QNetworkAccessManager *const m_pollingNam = new QNetworkAccessManager(this);
|
||||||
|
QTimer m_pollTimer;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit CameraClient(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
void setPreset(int preset);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setBaseUrl();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void get(QString const&url);
|
||||||
|
|
||||||
|
void poll();
|
||||||
|
|
||||||
|
void handlePollResponse(QNetworkReply *reply);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void pollUpdate();
|
||||||
|
|
||||||
|
void pollFailed();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
@ -8,4 +8,3 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
constexpr auto SlideHost = "192.168.254.61";
|
|
||||||
|
@ -7,10 +7,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
#include <QSettings>
|
||||||
|
|
||||||
#include "mainwindow.hpp"
|
#include "mainwindow.hpp"
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
QSettings::setDefaultFormat(QSettings::Format::IniFormat);
|
||||||
QApplication a(argc, argv);
|
QApplication a(argc, argv);
|
||||||
QApplication::setApplicationName(QObject::tr("Slide Controller 9000"));
|
QApplication::setApplicationName(QObject::tr("Slide Controller 9000"));
|
||||||
MainWindow w;
|
MainWindow w;
|
||||||
|
@ -6,17 +6,21 @@
|
|||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
|
#include <QMenuBar>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QStatusBar>
|
#include <QStatusBar>
|
||||||
|
|
||||||
|
#include "settingsdialog.hpp"
|
||||||
#include "slideview.hpp"
|
#include "slideview.hpp"
|
||||||
#include "mainwindow.hpp"
|
#include "mainwindow.hpp"
|
||||||
|
|
||||||
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent) {
|
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent) {
|
||||||
move(0, 0);
|
move(0, 0);
|
||||||
setFixedSize(590, 555);
|
setFixedSize(610, 555);
|
||||||
setWindowTitle(tr("Slide Controller 9000"));
|
setWindowTitle(tr("Slide Controller 9000"));
|
||||||
|
setupMenu();
|
||||||
const auto mainWidget = new QWidget(this);
|
const auto mainWidget = new QWidget(this);
|
||||||
const auto rootLyt = new QVBoxLayout;
|
const auto rootLyt = new QVBoxLayout;
|
||||||
const auto controlsLayout = new QGridLayout;
|
const auto controlsLayout = new QGridLayout;
|
||||||
@ -26,53 +30,159 @@ MainWindow::MainWindow(QWidget *parent): QMainWindow(parent) {
|
|||||||
rootLyt->addWidget(m_slideView);
|
rootLyt->addWidget(m_slideView);
|
||||||
rootLyt->addLayout(controlsLayout);
|
rootLyt->addLayout(controlsLayout);
|
||||||
// setup slide controls
|
// setup slide controls
|
||||||
const auto showHideLyt = new QHBoxLayout;
|
|
||||||
rootLyt->addLayout(showHideLyt);
|
|
||||||
const auto btnPrevSong = new QPushButton(tr("Previous Song (Left)"), this);
|
const auto btnPrevSong = new QPushButton(tr("Previous Song (Left)"), this);
|
||||||
const auto btnPrevSlide = new QPushButton(tr("Previous Slide (Up)"), this);
|
const auto btnPrevSlide = new QPushButton(tr("Previous Slide (Up)"), this);
|
||||||
const auto btnNextSlide = new QPushButton(tr("Next Slide (Down)"), this);
|
const auto btnNextSlide = new QPushButton(tr("Next Slide (Down)"), this);
|
||||||
const auto btnNextSong = new QPushButton(tr("Next Song (Right)"), this);
|
const auto btnNextSong = new QPushButton(tr("Next Song (Right)"), this);
|
||||||
const auto btnHideSlides = new QPushButton(tr("Hide (1)"), this);
|
|
||||||
const auto btnOpenLpShowSlides = new QPushButton(tr("Show in OpenLP Only (2)"), this);
|
|
||||||
const auto btnShowSlides = new QPushButton(tr("Show (3)"), mainWidget);
|
|
||||||
controlsLayout->addWidget(btnPrevSlide, 0, 1);
|
controlsLayout->addWidget(btnPrevSlide, 0, 1);
|
||||||
controlsLayout->addWidget(btnNextSlide, 0, 2);
|
controlsLayout->addWidget(btnNextSlide, 0, 2);
|
||||||
controlsLayout->addWidget(btnPrevSong, 0, 0);
|
controlsLayout->addWidget(btnPrevSong, 0, 0);
|
||||||
controlsLayout->addWidget(btnNextSong, 0, 3);
|
controlsLayout->addWidget(btnNextSong, 0, 3);
|
||||||
showHideLyt->addWidget(btnHideSlides);
|
controlsLayout->setSpacing(2);
|
||||||
showHideLyt->addWidget(btnOpenLpShowSlides);
|
|
||||||
showHideLyt->addWidget(btnShowSlides);
|
|
||||||
btnNextSong->setShortcut(Qt::Key_Right);
|
btnNextSong->setShortcut(Qt::Key_Right);
|
||||||
btnPrevSong->setShortcut(Qt::Key_Left);
|
btnPrevSong->setShortcut(Qt::Key_Left);
|
||||||
btnNextSlide->setShortcut(Qt::Key_Down);
|
btnNextSlide->setShortcut(Qt::Key_Down);
|
||||||
btnPrevSlide->setShortcut(Qt::Key_Up);
|
btnPrevSlide->setShortcut(Qt::Key_Up);
|
||||||
btnHideSlides->setShortcut(Qt::Key_1);
|
btnNextSong->setFixedWidth(135);
|
||||||
btnOpenLpShowSlides->setShortcut(Qt::Key_2);
|
btnPrevSong->setFixedWidth(135);
|
||||||
btnHideSlides->setToolTip(tr("Also hides slides in OBS"));
|
btnNextSlide->setFixedWidth(135);
|
||||||
btnShowSlides->setShortcut(Qt::Key_3);
|
btnPrevSlide->setFixedWidth(135);
|
||||||
|
setupViewControls(rootLyt);
|
||||||
connect(btnNextSlide, &QPushButton::clicked, &m_openlpClient, &OpenLPClient::nextSlide);
|
connect(btnNextSlide, &QPushButton::clicked, &m_openlpClient, &OpenLPClient::nextSlide);
|
||||||
connect(btnPrevSlide, &QPushButton::clicked, &m_openlpClient, &OpenLPClient::prevSlide);
|
connect(btnPrevSlide, &QPushButton::clicked, &m_openlpClient, &OpenLPClient::prevSlide);
|
||||||
connect(btnNextSong, &QPushButton::clicked, &m_openlpClient, &OpenLPClient::nextSong);
|
connect(btnNextSong, &QPushButton::clicked, &m_openlpClient, &OpenLPClient::nextSong);
|
||||||
connect(btnPrevSong, &QPushButton::clicked, &m_openlpClient, &OpenLPClient::prevSong);
|
connect(btnPrevSong, &QPushButton::clicked, &m_openlpClient, &OpenLPClient::prevSong);
|
||||||
connect(btnHideSlides, &QPushButton::clicked, &m_openlpClient, &OpenLPClient::blankScreen);
|
|
||||||
connect(btnHideSlides, &QPushButton::clicked, &m_obsClient, &OBSClient::hideSlides);
|
|
||||||
connect(btnOpenLpShowSlides, &QPushButton::clicked, &m_openlpClient, &OpenLPClient::showSlides);
|
|
||||||
connect(btnShowSlides, &QPushButton::clicked, &m_obsClient, &OBSClient::showSlides);
|
|
||||||
connect(btnShowSlides, &QPushButton::clicked, &m_openlpClient, &OpenLPClient::showSlides);
|
|
||||||
connect(&m_openlpClient, &OpenLPClient::pollUpdate, m_slideView, &SlideView::pollUpdate);
|
connect(&m_openlpClient, &OpenLPClient::pollUpdate, m_slideView, &SlideView::pollUpdate);
|
||||||
connect(&m_openlpClient, &OpenLPClient::songListUpdate, m_slideView, &SlideView::songListUpdate);
|
connect(&m_openlpClient, &OpenLPClient::songListUpdate, m_slideView, &SlideView::songListUpdate);
|
||||||
connect(&m_openlpClient, &OpenLPClient::slideListUpdate, m_slideView, &SlideView::slideListUpdate);
|
connect(&m_openlpClient, &OpenLPClient::slideListUpdate, m_slideView, &SlideView::slideListUpdate);
|
||||||
connect(&m_openlpClient, &OpenLPClient::pollFailed, m_slideView, &SlideView::reset);
|
connect(&m_openlpClient, &OpenLPClient::pollFailed, m_slideView, &SlideView::reset);
|
||||||
connect(m_slideView, &SlideView::songChanged, &m_openlpClient, &OpenLPClient::changeSong);
|
connect(m_slideView, &SlideView::songChanged, &m_openlpClient, &OpenLPClient::changeSong);
|
||||||
connect(m_slideView, &SlideView::slideChanged, &m_openlpClient, &OpenLPClient::changeSlide);
|
connect(m_slideView, &SlideView::slideChanged, &m_openlpClient, &OpenLPClient::changeSlide);
|
||||||
// setup scene selector
|
|
||||||
connect(btnOpenLpShowSlides, &QPushButton::clicked, &m_obsClient, &OBSClient::hideSlides);
|
|
||||||
// setup status bar
|
// setup status bar
|
||||||
setStatusBar(new QStatusBar(this));
|
setStatusBar(new QStatusBar(this));
|
||||||
|
connect(&m_cameraClient, &CameraClient::pollUpdate, this, &MainWindow::cameraConnectionInit);
|
||||||
connect(&m_openlpClient, &OpenLPClient::songChanged, this, &MainWindow::refreshStatusBar);
|
connect(&m_openlpClient, &OpenLPClient::songChanged, this, &MainWindow::refreshStatusBar);
|
||||||
connect(&m_openlpClient, &OpenLPClient::pollUpdate, this, &MainWindow::openLpConnectionInit);
|
connect(&m_openlpClient, &OpenLPClient::pollUpdate, this, &MainWindow::openLpConnectionInit);
|
||||||
connect(&m_obsClient, &OBSClient::pollUpdate, this, &MainWindow::obsConnectionInit);
|
connect(&m_obsClient, &OBSClient::pollUpdate, this, &MainWindow::obsConnectionInit);
|
||||||
refreshStatusBar();
|
refreshStatusBar();
|
||||||
|
connect(statusBar(), &QStatusBar::messageChanged, this, [this](QStringView const&msg) {
|
||||||
|
if (msg.empty()) {
|
||||||
|
refreshStatusBar();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::setupMenu() {
|
||||||
|
// file menu
|
||||||
|
auto const fileMenu = menuBar()->addMenu(tr("&File"));
|
||||||
|
auto const settingsAct = new QAction(tr("&Settings"), this);
|
||||||
|
auto const quitAct = new QAction(tr("E&xit"), this);
|
||||||
|
settingsAct->setShortcuts(QKeySequence::Preferences);
|
||||||
|
connect(settingsAct, &QAction::triggered, this, &MainWindow::openSettings);
|
||||||
|
quitAct->setShortcuts(QKeySequence::Quit);
|
||||||
|
quitAct->setStatusTip(tr("Exit application"));
|
||||||
|
connect(quitAct, &QAction::triggered, &QApplication::quit);
|
||||||
|
fileMenu->addAction(settingsAct);
|
||||||
|
fileMenu->addAction(quitAct);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::setupDefaultViewControls(QHBoxLayout *viewCtlLyt) {
|
||||||
|
auto const mainWidget = viewCtlLyt->parentWidget();
|
||||||
|
auto const btnHideSlides = new QPushButton(tr("1. Hide"), mainWidget);
|
||||||
|
auto const btnOpenLpShowSlides = new QPushButton(tr("2. Show in OpenLP Only"), mainWidget);
|
||||||
|
auto const btnShowSlides = new QPushButton(tr("3. Show"), mainWidget);
|
||||||
|
viewCtlLyt->addWidget(btnHideSlides);
|
||||||
|
viewCtlLyt->addWidget(btnOpenLpShowSlides);
|
||||||
|
viewCtlLyt->addWidget(btnShowSlides);
|
||||||
|
btnHideSlides->setShortcut(Qt::Key_1);
|
||||||
|
btnOpenLpShowSlides->setShortcut(Qt::Key_2);
|
||||||
|
btnHideSlides->setToolTip(tr("Also hides slides in OBS"));
|
||||||
|
btnShowSlides->setShortcut(Qt::Key_3);
|
||||||
|
connect(btnHideSlides, &QPushButton::clicked, &m_openlpClient, &OpenLPClient::blankScreen);
|
||||||
|
connect(btnHideSlides, &QPushButton::clicked, &m_obsClient, &OBSClient::hideSlides);
|
||||||
|
connect(btnOpenLpShowSlides, &QPushButton::clicked, &m_openlpClient, &OpenLPClient::showSlides);
|
||||||
|
connect(btnOpenLpShowSlides, &QPushButton::clicked, &m_obsClient, &OBSClient::hideSlides);
|
||||||
|
connect(btnShowSlides, &QPushButton::clicked, &m_obsClient, &OBSClient::showSlides);
|
||||||
|
connect(btnShowSlides, &QPushButton::clicked, &m_openlpClient, &OpenLPClient::showSlides);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::setupCustomViewControls(QVector<View> const&views, QGridLayout *viewCtlLyt) {
|
||||||
|
constexpr auto columns = 3;
|
||||||
|
auto const parent = viewCtlLyt->parentWidget();
|
||||||
|
for (auto i = 0; auto const&view : views) {
|
||||||
|
auto const x = i % columns;
|
||||||
|
auto const y = i / columns;
|
||||||
|
auto const name = QString("%1. %2").arg(i + 1).arg(view.name);
|
||||||
|
auto const btn = new QPushButton(name, parent);
|
||||||
|
btn->setShortcut(Qt::Key_1 + i);
|
||||||
|
viewCtlLyt->addWidget(btn, y, x);
|
||||||
|
auto const slides = view.slides;
|
||||||
|
auto const obsSlides = view.obsSlides;
|
||||||
|
auto const cameraPreset = view.cameraPreset;
|
||||||
|
connect(btn, &QPushButton::clicked, this, [this, slides, obsSlides, cameraPreset] {
|
||||||
|
m_cameraClient.setPreset(cameraPreset);
|
||||||
|
m_openlpClient.setSlidesVisible(slides);
|
||||||
|
m_obsClient.setSlidesVisible(obsSlides);
|
||||||
|
});
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::setupViewControls(QVBoxLayout *rootLyt) {
|
||||||
|
auto views = getViews();
|
||||||
|
if (!m_viewControlsParent) {
|
||||||
|
m_viewControlsParent = new QWidget(rootLyt->parentWidget());
|
||||||
|
m_viewControlsParentLyt = new QHBoxLayout(m_viewControlsParent);
|
||||||
|
m_viewControlsParentLyt->setContentsMargins(0, 0, 0, 0);
|
||||||
|
rootLyt->addWidget(m_viewControlsParent);
|
||||||
|
}
|
||||||
|
delete m_viewControls;
|
||||||
|
m_viewControls = new QWidget(m_viewControlsParent);
|
||||||
|
m_viewControlsParentLyt->addWidget(m_viewControls);
|
||||||
|
auto const viewCtlLyt = new QGridLayout(m_viewControls);
|
||||||
|
viewCtlLyt->setSpacing(5);
|
||||||
|
if (views.empty()) {
|
||||||
|
views.emplace_back(View{
|
||||||
|
.name = tr("Hide"),
|
||||||
|
.slides = false,
|
||||||
|
.obsSlides = false,
|
||||||
|
});
|
||||||
|
views.emplace_back(View{
|
||||||
|
.name = tr("Hide in OBS"),
|
||||||
|
.slides = false,
|
||||||
|
.obsSlides = false,
|
||||||
|
});
|
||||||
|
views.emplace_back(View{
|
||||||
|
.name = tr("Show"),
|
||||||
|
.slides = false,
|
||||||
|
.obsSlides = false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setupCustomViewControls(views, viewCtlLyt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::openSettings() {
|
||||||
|
SettingsDialog d(this);
|
||||||
|
auto const result = d.exec();
|
||||||
|
if (result == QDialog::Accepted) {
|
||||||
|
m_cameraClient.setBaseUrl();
|
||||||
|
m_obsClient.setBaseUrl();
|
||||||
|
m_openlpClient.setBaseUrl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::cameraConnectionInit() {
|
||||||
|
disconnect(&m_cameraClient, &CameraClient::pollUpdate, this, &MainWindow::cameraConnectionInit);
|
||||||
|
connect(&m_cameraClient, &CameraClient::pollFailed, this, &MainWindow::cameraConnectionLost);
|
||||||
|
m_cameraConnected = true;
|
||||||
|
refreshStatusBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::cameraConnectionLost() {
|
||||||
|
disconnect(&m_cameraClient, &CameraClient::pollFailed, this, &MainWindow::cameraConnectionLost);
|
||||||
|
connect(&m_cameraClient, &CameraClient::pollUpdate, this, &MainWindow::cameraConnectionInit);
|
||||||
|
m_cameraConnected = false;
|
||||||
|
refreshStatusBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::openLpConnectionInit() {
|
void MainWindow::openLpConnectionInit() {
|
||||||
@ -104,9 +214,10 @@ void MainWindow::obsConnectionLost() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::refreshStatusBar() {
|
void MainWindow::refreshStatusBar() {
|
||||||
const auto openLpStatus = m_openLpConnected ? tr("OpenLP: Connected") : tr("OpenLP: Not Connected");
|
auto const cameraStatus = m_cameraConnected ? tr("Camera: Connected") : tr("Camera: Not Connected");
|
||||||
const auto obsStatus = m_obsConnected ? tr("OBS: Connected") : tr("OBS: Not Connected");
|
auto const openLpStatus = m_openLpConnected ? tr("OpenLP: Connected") : tr("OpenLP: Not Connected");
|
||||||
const auto nextSong = m_openlpClient.getNextSong();
|
auto const obsStatus = m_obsConnected ? tr("OBS: Connected") : tr("OBS: Not Connected");
|
||||||
const auto nextSongTxt = m_openLpConnected ? " | Next Song: " + nextSong : "";
|
auto const nextSong = m_openlpClient.getNextSong();
|
||||||
statusBar()->showMessage(openLpStatus + " | " + obsStatus + nextSongTxt);
|
auto const nextSongTxt = m_openLpConnected ? " | Next Song: " + nextSong : "";
|
||||||
|
statusBar()->showMessage(cameraStatus + " | " + openLpStatus + " | " + obsStatus + nextSongTxt);
|
||||||
}
|
}
|
||||||
|
@ -12,24 +12,45 @@
|
|||||||
|
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
|
|
||||||
|
#include "cameraclient.hpp"
|
||||||
#include "obsclient.hpp"
|
#include "obsclient.hpp"
|
||||||
#include "openlpclient.hpp"
|
#include "openlpclient.hpp"
|
||||||
|
#include "settingsdata.hpp"
|
||||||
|
|
||||||
class MainWindow: public QMainWindow {
|
class MainWindow: public QMainWindow {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
CameraClient m_cameraClient;
|
||||||
OBSClient m_obsClient;
|
OBSClient m_obsClient;
|
||||||
OpenLPClient m_openlpClient;
|
OpenLPClient m_openlpClient;
|
||||||
class SlideView *m_slideView = nullptr;
|
class SlideView *m_slideView = nullptr;
|
||||||
|
bool m_cameraConnected = false;
|
||||||
bool m_openLpConnected = false;
|
bool m_openLpConnected = false;
|
||||||
bool m_obsConnected = false;
|
bool m_obsConnected = false;
|
||||||
|
class QHBoxLayout *m_viewControlsParentLyt = nullptr;
|
||||||
|
class QWidget *m_viewControlsParent = nullptr;
|
||||||
|
class QWidget *m_viewControls = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit MainWindow(QWidget *parent = nullptr);
|
explicit MainWindow(QWidget *parent = nullptr);
|
||||||
~MainWindow() override = default;
|
~MainWindow() override = default;
|
||||||
|
|
||||||
private slots:
|
private:
|
||||||
|
void setupMenu();
|
||||||
|
|
||||||
|
void setupDefaultViewControls(class QHBoxLayout *rootLyt);
|
||||||
|
|
||||||
|
void setupCustomViewControls(QVector<View> const&views, class QGridLayout *rootLyt);
|
||||||
|
|
||||||
|
void setupViewControls(class QVBoxLayout *rootLyt);
|
||||||
|
|
||||||
|
void openSettings();
|
||||||
|
|
||||||
|
void cameraConnectionInit();
|
||||||
|
|
||||||
|
void cameraConnectionLost();
|
||||||
|
|
||||||
void openLpConnectionInit();
|
void openLpConnectionInit();
|
||||||
|
|
||||||
void openLpConnectionLost();
|
void openLpConnectionLost();
|
||||||
|
@ -8,17 +8,20 @@
|
|||||||
|
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
|
#include <QSettings>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
|
#include "settingsdata.hpp"
|
||||||
#include "obsclient.hpp"
|
#include "obsclient.hpp"
|
||||||
|
|
||||||
OBSClient::OBSClient(QObject *parent): QObject(parent) {
|
OBSClient::OBSClient(QObject *parent): QObject(parent) {
|
||||||
|
setBaseUrl();
|
||||||
m_pollTimer.start(1000);
|
m_pollTimer.start(1000);
|
||||||
connect(&m_pollTimer, &QTimer::timeout, this, &OBSClient::poll);
|
connect(&m_pollTimer, &QTimer::timeout, this, &OBSClient::poll);
|
||||||
connect(m_pollingNam, &QNetworkAccessManager::finished, this, &OBSClient::handlePollResponse);
|
connect(m_pollingNam, &QNetworkAccessManager::finished, this, &OBSClient::handlePollResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OBSClient::setScene(QString scene) {
|
void OBSClient::setScene(QString const&scene) {
|
||||||
get(QString("/Scene?name=%1").arg(scene));
|
get(QString("/Scene?name=%1").arg(scene));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,7 +33,7 @@ void OBSClient::hideSlides() {
|
|||||||
setScene("SpeakerScene");
|
setScene("SpeakerScene");
|
||||||
}
|
}
|
||||||
|
|
||||||
void OBSClient::setSlidesVisible(int state) {
|
void OBSClient::setSlidesVisible(bool state) {
|
||||||
if (state) {
|
if (state) {
|
||||||
setScene("MusicScene");
|
setScene("MusicScene");
|
||||||
} else {
|
} else {
|
||||||
@ -38,15 +41,20 @@ void OBSClient::setSlidesVisible(int state) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OBSClient::get(QString urlExt) {
|
void OBSClient::setBaseUrl() {
|
||||||
QUrl url(QString(BaseUrl) + urlExt);
|
const auto [host, port] = getOBSConnectionData();
|
||||||
|
m_baseUrl = QString("http://%1:%2").arg(host, QString::number(port));
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSClient::get(QString const&urlExt) {
|
||||||
|
QUrl url(QString(m_baseUrl) + urlExt);
|
||||||
QNetworkRequest rqst(url);
|
QNetworkRequest rqst(url);
|
||||||
auto reply = m_nam->get(rqst);
|
auto reply = m_nam->get(rqst);
|
||||||
connect(reply, &QIODevice::readyRead, reply, &QObject::deleteLater);
|
connect(reply, &QIODevice::readyRead, reply, &QObject::deleteLater);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OBSClient::poll() {
|
void OBSClient::poll() {
|
||||||
QUrl url(QString(BaseUrl) + "/ping");
|
QUrl url(QString(m_baseUrl) + "/ping");
|
||||||
QNetworkRequest rqst(url);
|
QNetworkRequest rqst(url);
|
||||||
m_pollingNam->get(rqst);
|
m_pollingNam->get(rqst);
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
class OBSClient: public QObject {
|
class OBSClient: public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
private:
|
private:
|
||||||
const QString BaseUrl = QString("http://") + SlideHost + ":9302";
|
QString m_baseUrl;
|
||||||
QNetworkAccessManager *m_nam = new QNetworkAccessManager(this);
|
QNetworkAccessManager *m_nam = new QNetworkAccessManager(this);
|
||||||
QNetworkAccessManager *m_pollingNam = new QNetworkAccessManager(this);
|
QNetworkAccessManager *m_pollingNam = new QNetworkAccessManager(this);
|
||||||
QTimer m_pollTimer;
|
QTimer m_pollTimer;
|
||||||
@ -26,16 +26,18 @@ class OBSClient: public QObject {
|
|||||||
explicit OBSClient(QObject *parent = nullptr);
|
explicit OBSClient(QObject *parent = nullptr);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setScene(QString scene);
|
void setScene(QString const&scene);
|
||||||
|
|
||||||
void showSlides();
|
void showSlides();
|
||||||
|
|
||||||
void hideSlides();
|
void hideSlides();
|
||||||
|
|
||||||
void setSlidesVisible(int state);
|
void setSlidesVisible(bool state);
|
||||||
|
|
||||||
|
void setBaseUrl();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void get(QString url);
|
void get(QString const&url);
|
||||||
|
|
||||||
void poll();
|
void poll();
|
||||||
|
|
||||||
|
@ -11,10 +11,13 @@
|
|||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QJsonValueRef>
|
#include <QJsonValueRef>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
|
#include <QSettings>
|
||||||
|
|
||||||
|
#include "settingsdata.hpp"
|
||||||
#include "openlpclient.hpp"
|
#include "openlpclient.hpp"
|
||||||
|
|
||||||
OpenLPClient::OpenLPClient(QObject *parent): QObject(parent) {
|
OpenLPClient::OpenLPClient(QObject *parent): QObject(parent) {
|
||||||
|
setBaseUrl();
|
||||||
poll();
|
poll();
|
||||||
m_pollTimer.start(250);
|
m_pollTimer.start(250);
|
||||||
connect(&m_pollTimer, &QTimer::timeout, this, &OpenLPClient::poll);
|
connect(&m_pollTimer, &QTimer::timeout, this, &OpenLPClient::poll);
|
||||||
@ -57,6 +60,14 @@ void OpenLPClient::showSlides() {
|
|||||||
get("/api/display/show");
|
get("/api/display/show");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OpenLPClient::setSlidesVisible(bool value) {
|
||||||
|
if (value) {
|
||||||
|
showSlides();
|
||||||
|
} else {
|
||||||
|
blankScreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void OpenLPClient::changeSong(int it) {
|
void OpenLPClient::changeSong(int it) {
|
||||||
auto n = QString::number(it);
|
auto n = QString::number(it);
|
||||||
auto url = "/api/service/set?data=%7B%22request%22%3A+%7B%22id%22%3A+" + n + "%7D%7D&_=1627181837297";
|
auto url = "/api/service/set?data=%7B%22request%22%3A+%7B%22id%22%3A+" + n + "%7D%7D&_=1627181837297";
|
||||||
@ -69,26 +80,31 @@ void OpenLPClient::changeSlide(int slide) {
|
|||||||
get(url);
|
get(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenLPClient::get(QString urlExt) {
|
void OpenLPClient::setBaseUrl() {
|
||||||
QUrl url(QString(BaseUrl) + urlExt);
|
const auto [host, port] = getOpenLPConnectionData();
|
||||||
|
m_baseUrl = QString("http://%1:%2").arg(host, QString::number(port));
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenLPClient::get(QString const&urlExt) {
|
||||||
|
QUrl url(m_baseUrl + urlExt);
|
||||||
QNetworkRequest rqst(url);
|
QNetworkRequest rqst(url);
|
||||||
m_nam->get(rqst);
|
m_nam->get(rqst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenLPClient::requestSongList() {
|
void OpenLPClient::requestSongList() {
|
||||||
QUrl url(QString(BaseUrl) + "/api/service/list?_=1626628079579");
|
QUrl url(m_baseUrl + "/api/service/list?_=1626628079579");
|
||||||
QNetworkRequest rqst(url);
|
QNetworkRequest rqst(url);
|
||||||
m_songListNam->get(rqst);
|
m_songListNam->get(rqst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenLPClient::requestSlideList() {
|
void OpenLPClient::requestSlideList() {
|
||||||
QUrl url(QString(BaseUrl) + "/api/controller/live/text?_=1626628079579");
|
QUrl url(m_baseUrl + "/api/controller/live/text?_=1626628079579");
|
||||||
QNetworkRequest rqst(url);
|
QNetworkRequest rqst(url);
|
||||||
m_slideListNam->get(rqst);
|
m_slideListNam->get(rqst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenLPClient::poll() {
|
void OpenLPClient::poll() {
|
||||||
QUrl url(QString(BaseUrl) + "/api/poll?_=1626628079579");
|
QUrl url(m_baseUrl + "/api/poll?_=1626628079579");
|
||||||
QNetworkRequest rqst(url);
|
QNetworkRequest rqst(url);
|
||||||
m_pollingNam->get(rqst);
|
m_pollingNam->get(rqst);
|
||||||
}
|
}
|
||||||
|
@ -19,11 +19,7 @@ class OpenLPClient: public QObject {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Song {
|
QString m_baseUrl;
|
||||||
QString name;
|
|
||||||
QString id;
|
|
||||||
};
|
|
||||||
const QString BaseUrl = QString("http://") + SlideHost + ":4316";
|
|
||||||
QNetworkAccessManager *m_nam = new QNetworkAccessManager(this);
|
QNetworkAccessManager *m_nam = new QNetworkAccessManager(this);
|
||||||
QNetworkAccessManager *m_pollingNam = new QNetworkAccessManager(this);
|
QNetworkAccessManager *m_pollingNam = new QNetworkAccessManager(this);
|
||||||
QNetworkAccessManager *m_songListNam = new QNetworkAccessManager(this);
|
QNetworkAccessManager *m_songListNam = new QNetworkAccessManager(this);
|
||||||
@ -53,12 +49,16 @@ class OpenLPClient: public QObject {
|
|||||||
|
|
||||||
void showSlides();
|
void showSlides();
|
||||||
|
|
||||||
|
void setSlidesVisible(bool value);
|
||||||
|
|
||||||
void changeSong(int it);
|
void changeSong(int it);
|
||||||
|
|
||||||
void changeSlide(int slide);
|
void changeSlide(int slide);
|
||||||
|
|
||||||
|
void setBaseUrl();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void get(QString url);
|
void get(QString const&url);
|
||||||
|
|
||||||
void requestSongList();
|
void requestSongList();
|
||||||
|
|
||||||
|
142
src/settingsdata.cpp
Normal file
142
src/settingsdata.cpp
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 - 2023 gary@drinkingtea.net
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QSettings>
|
||||||
|
|
||||||
|
#include "settingsdata.hpp"
|
||||||
|
|
||||||
|
void setCameraConnectionData(QSettings &settings, ConnectionData const&cd) {
|
||||||
|
settings.beginGroup("CameraClient");
|
||||||
|
settings.setValue("Host", cd.host);
|
||||||
|
settings.setValue("Port", cd.port);
|
||||||
|
settings.endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setOpenLPConnectionData(QSettings &settings, ConnectionData const&cd) {
|
||||||
|
settings.beginGroup("OpenLPClient");
|
||||||
|
settings.setValue("Host", cd.host);
|
||||||
|
settings.setValue("Port", cd.port);
|
||||||
|
settings.endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setOBSConnectionData(QSettings &settings, ConnectionData const&cd) {
|
||||||
|
settings.beginGroup("OBSClient");
|
||||||
|
settings.setValue("Host", cd.host);
|
||||||
|
settings.setValue("Port", cd.port);
|
||||||
|
settings.endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionData getCameraConnectionData(QSettings &settings) {
|
||||||
|
ConnectionData out;
|
||||||
|
settings.beginGroup("CameraClient");
|
||||||
|
out.host = settings.value("Host", "192.168.100.88").toString();
|
||||||
|
out.port = static_cast<uint16_t>(settings.value("Port", 80).toInt());
|
||||||
|
settings.endGroup();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionData getOpenLPConnectionData(QSettings &settings) {
|
||||||
|
ConnectionData out;
|
||||||
|
settings.beginGroup("OpenLPClient");
|
||||||
|
out.host = settings.value("Host", "127.0.0.1").toString();
|
||||||
|
out.port = static_cast<uint16_t>(settings.value("Port", 4316).toInt());
|
||||||
|
settings.endGroup();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionData getOBSConnectionData(QSettings &settings) {
|
||||||
|
ConnectionData out;
|
||||||
|
settings.beginGroup("OBSClient");
|
||||||
|
out.host = settings.value("Host", "127.0.0.1").toString();
|
||||||
|
out.port = static_cast<uint16_t>(settings.value("Port", 9302).toInt());
|
||||||
|
settings.endGroup();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCameraConnectionData(ConnectionData const&cd) {
|
||||||
|
QSettings settings;
|
||||||
|
settings.beginGroup("CameraClient");
|
||||||
|
settings.setValue("Host", cd.host);
|
||||||
|
settings.setValue("Port", cd.port);
|
||||||
|
settings.endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setOpenLPConnectionData(ConnectionData const&cd) {
|
||||||
|
QSettings settings;
|
||||||
|
settings.beginGroup("OpenLPClient");
|
||||||
|
settings.setValue("Host", cd.host);
|
||||||
|
settings.setValue("Port", cd.port);
|
||||||
|
settings.endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setOBSConnectionData(ConnectionData const&cd) {
|
||||||
|
QSettings settings;
|
||||||
|
settings.beginGroup("OBSClient");
|
||||||
|
settings.setValue("Host", cd.host);
|
||||||
|
settings.setValue("Port", cd.port);
|
||||||
|
settings.endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionData getCameraConnectionData() {
|
||||||
|
QSettings s;
|
||||||
|
return getCameraConnectionData(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionData getOpenLPConnectionData() {
|
||||||
|
QSettings s;
|
||||||
|
return getOpenLPConnectionData(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionData getOBSConnectionData() {
|
||||||
|
QSettings s;
|
||||||
|
return getOBSConnectionData(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void setViews(QSettings &settings, QVector<View> const&views) {
|
||||||
|
settings.beginGroup("Views");
|
||||||
|
settings.beginWriteArray("Views");
|
||||||
|
for (auto i = 0; auto const&view : views) {
|
||||||
|
settings.setArrayIndex(i);
|
||||||
|
settings.setValue("Name", view.name);
|
||||||
|
settings.setValue("Slides", view.slides);
|
||||||
|
settings.setValue("ObsSlides", view.obsSlides);
|
||||||
|
settings.setValue("Preset", view.cameraPreset);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
settings.endArray();
|
||||||
|
settings.endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setViews(QVector<View> const&views) {
|
||||||
|
QSettings s;
|
||||||
|
return setViews(s, views);
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<View> getViews(QSettings &settings) {
|
||||||
|
QVector<View> out;
|
||||||
|
settings.beginGroup("Views");
|
||||||
|
const auto size = settings.beginReadArray("Views");
|
||||||
|
for (auto i = 0; i < size; ++i) {
|
||||||
|
settings.setArrayIndex(i);
|
||||||
|
out.emplace_back(View{
|
||||||
|
.name = settings.value("Name").toString(),
|
||||||
|
.slides = settings.value("Slides").toBool(),
|
||||||
|
.obsSlides = settings.value("ObsSlides").toBool(),
|
||||||
|
.cameraPreset = settings.value("Preset").toInt(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
settings.endArray();
|
||||||
|
settings.endGroup();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<View> getViews() {
|
||||||
|
QSettings s;
|
||||||
|
return getViews(s);
|
||||||
|
}
|
59
src/settingsdata.hpp
Normal file
59
src/settingsdata.hpp
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 - 2023 gary@drinkingtea.net
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
struct ConnectionData {
|
||||||
|
QString host;
|
||||||
|
uint16_t port = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void setCameraConnectionData(class QSettings &settings, ConnectionData const&cd);
|
||||||
|
|
||||||
|
void setOpenLPConnectionData(class QSettings &settings, ConnectionData const&cd);
|
||||||
|
|
||||||
|
void setOBSConnectionData(class QSettings &settings, ConnectionData const&cd);
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
ConnectionData getCameraConnectionData(class QSettings &settings);
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
ConnectionData getOpenLPConnectionData(class QSettings &settings);
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
ConnectionData getOBSConnectionData(class QSettings &settings);
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
ConnectionData getCameraConnectionData();
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
ConnectionData getOpenLPConnectionData();
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
ConnectionData getOBSConnectionData();
|
||||||
|
|
||||||
|
|
||||||
|
struct View {
|
||||||
|
QString name;
|
||||||
|
bool slides = false;
|
||||||
|
bool obsSlides = false;
|
||||||
|
int cameraPreset = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
void setViews(class QSettings &settings, QVector<View> const&views);
|
||||||
|
|
||||||
|
void setViews(QVector<View> const&views);
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
QVector<View> getViews(class QSettings &settings);
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
QVector<View> getViews();
|
224
src/settingsdialog.cpp
Normal file
224
src/settingsdialog.cpp
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 - 2023 gary@drinkingtea.net
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QCheckBox>
|
||||||
|
#include <QFormLayout>
|
||||||
|
#include <QHeaderView>
|
||||||
|
#include <QIntValidator>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QSpacerItem>
|
||||||
|
#include <QTabWidget>
|
||||||
|
#include <QTableWidget>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
|
#include "settingsdialog.hpp"
|
||||||
|
|
||||||
|
constexpr auto MaxPresets = 9;
|
||||||
|
constexpr auto MaxViews = 9;
|
||||||
|
|
||||||
|
enum ViewColumn {
|
||||||
|
Name = 0,
|
||||||
|
Slides,
|
||||||
|
ObsSlides,
|
||||||
|
CameraPreset,
|
||||||
|
Count
|
||||||
|
};
|
||||||
|
|
||||||
|
SettingsDialog::SettingsDialog(QWidget *parent): QDialog(parent) {
|
||||||
|
const auto lyt = new QVBoxLayout(this);
|
||||||
|
const auto tabs = new QTabWidget(this);
|
||||||
|
lyt->addWidget(tabs);
|
||||||
|
tabs->addTab(setupViewConfig(tabs), tr("&Views"));
|
||||||
|
tabs->addTab(setupNetworkInputs(tabs), tr("&Network"));
|
||||||
|
lyt->addWidget(setupButtons(this));
|
||||||
|
setFixedSize(440, 440);
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget *SettingsDialog::setupNetworkInputs(QWidget *parent) {
|
||||||
|
const auto root = new QWidget(parent);
|
||||||
|
const auto lyt = new QFormLayout(root);
|
||||||
|
const auto portValidator = new QIntValidator(1, 65536, this);
|
||||||
|
QSettings settings;
|
||||||
|
// camera settings
|
||||||
|
{
|
||||||
|
const auto c = getCameraConnectionData(settings);
|
||||||
|
m_cameraIpLe = new QLineEdit(root);
|
||||||
|
m_cameraPortLe = new QLineEdit(root);
|
||||||
|
m_cameraIpLe->setText(c.host);
|
||||||
|
m_cameraPortLe->setText(QString::number(c.port));
|
||||||
|
m_cameraPortLe->setValidator(portValidator);
|
||||||
|
lyt->addRow(tr("C&amera IP Address:"), m_cameraIpLe);
|
||||||
|
lyt->addRow(tr("Ca&mera Port:"), m_cameraPortLe);
|
||||||
|
}
|
||||||
|
// OpenLP settings
|
||||||
|
{
|
||||||
|
const auto c = getOpenLPConnectionData(settings);
|
||||||
|
m_openLpIpLe = new QLineEdit(root);
|
||||||
|
m_openLpPortLe = new QLineEdit(root);
|
||||||
|
m_openLpIpLe->setText(c.host);
|
||||||
|
m_openLpPortLe->setText(QString::number(c.port));
|
||||||
|
m_openLpPortLe->setValidator(portValidator);
|
||||||
|
lyt->addRow(tr("Op&enLP IP Address:"), m_openLpIpLe);
|
||||||
|
lyt->addRow(tr("Open&LP Port:"), m_openLpPortLe);
|
||||||
|
}
|
||||||
|
// OBS settings
|
||||||
|
{
|
||||||
|
const auto c = getOBSConnectionData(settings);
|
||||||
|
m_obsIpLe = new QLineEdit(root);
|
||||||
|
m_obsPortLe = new QLineEdit(root);
|
||||||
|
m_obsIpLe->setText(c.host);
|
||||||
|
m_obsPortLe->setText(QString::number(c.port));
|
||||||
|
m_obsPortLe->setValidator(portValidator);
|
||||||
|
lyt->addRow(tr("O&BS IP Address:"), m_obsIpLe);
|
||||||
|
lyt->addRow(tr("OB&S Port:"), m_obsPortLe);
|
||||||
|
}
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget *SettingsDialog::setupViewConfig(QWidget *parent) {
|
||||||
|
auto const root = new QWidget(parent);
|
||||||
|
auto const lyt = new QVBoxLayout(root);
|
||||||
|
// table
|
||||||
|
m_viewTable = new QTableWidget(parent);
|
||||||
|
{
|
||||||
|
lyt->addWidget(m_viewTable);
|
||||||
|
QStringList columns;
|
||||||
|
columns.resize(ViewColumn::Count);
|
||||||
|
columns[ViewColumn::Name] = tr("Name");
|
||||||
|
columns[ViewColumn::Slides] = tr("Slides");
|
||||||
|
columns[ViewColumn::ObsSlides] = tr("OBS Slides");
|
||||||
|
columns[ViewColumn::CameraPreset] = tr("Camera Preset");
|
||||||
|
m_viewTable->setColumnCount(static_cast<int>(columns.size()));
|
||||||
|
m_viewTable->setHorizontalHeaderLabels(columns);
|
||||||
|
m_viewTable->setSelectionMode(QAbstractItemView::SelectionMode::SingleSelection);
|
||||||
|
m_viewTable->setSelectionBehavior(QAbstractItemView::SelectionBehavior::SelectRows);
|
||||||
|
auto const hdr = m_viewTable->horizontalHeader();
|
||||||
|
m_viewTable->setColumnWidth(1, 70);
|
||||||
|
m_viewTable->setColumnWidth(2, 70);
|
||||||
|
m_viewTable->setColumnWidth(3, 70);
|
||||||
|
hdr->setStretchLastSection(true);
|
||||||
|
}
|
||||||
|
// add/removes buttons
|
||||||
|
{
|
||||||
|
auto const btnsRoot = new QWidget(root);
|
||||||
|
auto const btnsLyt = new QHBoxLayout(btnsRoot);
|
||||||
|
auto const addBtn = new QPushButton("+", btnsRoot);
|
||||||
|
auto const rmBtn = new QPushButton("-", btnsRoot);
|
||||||
|
addBtn->setFixedWidth(20);
|
||||||
|
rmBtn->setFixedWidth(20);
|
||||||
|
rmBtn->setDisabled(true);
|
||||||
|
lyt->addWidget(btnsRoot);
|
||||||
|
btnsLyt->addWidget(addBtn);
|
||||||
|
btnsLyt->addWidget(rmBtn);
|
||||||
|
btnsLyt->addSpacerItem(new QSpacerItem(1000, 0, QSizePolicy::Expanding, QSizePolicy::Ignored));
|
||||||
|
connect(addBtn, &QPushButton::clicked, this, [this, addBtn] {
|
||||||
|
auto const row = m_viewTable->rowCount();
|
||||||
|
m_viewTable->setRowCount(row + 1);
|
||||||
|
setupViewRow(row);
|
||||||
|
addBtn->setEnabled(m_viewTable->rowCount() < MaxViews);
|
||||||
|
});
|
||||||
|
connect(rmBtn, &QPushButton::clicked, this, [this, addBtn] {
|
||||||
|
auto const row = m_viewTable->currentRow();
|
||||||
|
m_viewTable->removeRow(row);
|
||||||
|
addBtn->setEnabled(m_viewTable->rowCount() < MaxViews);
|
||||||
|
});
|
||||||
|
connect(m_viewTable, &QTableWidget::currentCellChanged, rmBtn, [this, addBtn, rmBtn] (int row) {
|
||||||
|
rmBtn->setEnabled(row > -1 && row < m_viewTable->rowCount());
|
||||||
|
addBtn->setEnabled(m_viewTable->rowCount() < MaxViews);
|
||||||
|
});
|
||||||
|
const auto views = getViews();
|
||||||
|
m_viewTable->setRowCount(static_cast<int>(views.size()));
|
||||||
|
for (auto row = 0; auto const&view : views) {
|
||||||
|
setupViewRow(row, view);
|
||||||
|
++row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget *SettingsDialog::setupButtons(QWidget *parent) {
|
||||||
|
const auto root = new QWidget(parent);
|
||||||
|
const auto lyt = new QHBoxLayout(root);
|
||||||
|
m_errLbl = new QLabel(root);
|
||||||
|
const auto okBtn = new QPushButton(tr("&OK"), root);
|
||||||
|
const auto cancelBtn = new QPushButton(tr("&Cancel"), root);
|
||||||
|
lyt->addWidget(m_errLbl);
|
||||||
|
lyt->addSpacerItem(new QSpacerItem(1000, 0, QSizePolicy::Expanding, QSizePolicy::Ignored));
|
||||||
|
lyt->addWidget(okBtn);
|
||||||
|
lyt->addWidget(cancelBtn);
|
||||||
|
connect(okBtn, &QPushButton::clicked, this, &SettingsDialog::handleOK);
|
||||||
|
connect(cancelBtn, &QPushButton::clicked, this, &SettingsDialog::reject);
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsDialog::handleOK() {
|
||||||
|
QSettings settings;
|
||||||
|
QVector<View> views;
|
||||||
|
auto const viewsErr = collectViews(views);
|
||||||
|
if (viewsErr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setViews(settings, views);
|
||||||
|
setCameraConnectionData(settings, {
|
||||||
|
.host = m_cameraIpLe->text(),
|
||||||
|
.port = m_cameraPortLe->text().toUShort(),
|
||||||
|
});
|
||||||
|
setOpenLPConnectionData(settings, {
|
||||||
|
.host = m_openLpIpLe->text(),
|
||||||
|
.port = m_openLpPortLe->text().toUShort(),
|
||||||
|
});
|
||||||
|
setOBSConnectionData(settings, {
|
||||||
|
.host = m_obsIpLe->text(),
|
||||||
|
.port = m_obsPortLe->text().toUShort(),
|
||||||
|
});
|
||||||
|
accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsDialog::setupViewRow(int row, View const&view) {
|
||||||
|
// name
|
||||||
|
const auto nameItem = new QTableWidgetItem(view.name);
|
||||||
|
m_viewTable->setItem(row, ViewColumn::Name, nameItem);
|
||||||
|
// slides
|
||||||
|
const auto slidesCb = new QCheckBox(m_viewTable);
|
||||||
|
slidesCb->setChecked(view.slides);
|
||||||
|
m_viewTable->setCellWidget(row, ViewColumn::Slides, slidesCb);
|
||||||
|
// obs slides
|
||||||
|
const auto obsSlidesCb = new QCheckBox(m_viewTable);
|
||||||
|
obsSlidesCb->setChecked(view.obsSlides);
|
||||||
|
m_viewTable->setCellWidget(row, ViewColumn::ObsSlides, obsSlidesCb);
|
||||||
|
// camera preset
|
||||||
|
const auto presetItem = new QTableWidgetItem(QString::number(view.cameraPreset));
|
||||||
|
m_viewTable->setItem(row, ViewColumn::CameraPreset, presetItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
int SettingsDialog::collectViews(QVector<View> &views) const {
|
||||||
|
for (auto row = 0; row < m_viewTable->rowCount(); ++row) {
|
||||||
|
auto const viewNo = row + 1;
|
||||||
|
bool ok = false;
|
||||||
|
auto const name = m_viewTable->item(row, ViewColumn::Name)->text();
|
||||||
|
if (name.trimmed() == "") {
|
||||||
|
m_errLbl->setText(tr("View %1 has no name.").arg(viewNo));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
const auto cameraPreset = m_viewTable->item(row, ViewColumn::CameraPreset)->text().toInt(&ok);
|
||||||
|
if (!ok || cameraPreset < 1 || cameraPreset > MaxPresets) {
|
||||||
|
m_errLbl->setText(tr("View %1 has invalid preset (1-%2)").arg(viewNo).arg(MaxPresets));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
views.emplace_back(View{
|
||||||
|
.name = name,
|
||||||
|
.slides = dynamic_cast<QCheckBox*>(m_viewTable->cellWidget(row, ViewColumn::Slides))->isChecked(),
|
||||||
|
.obsSlides = dynamic_cast<QCheckBox*>(m_viewTable->cellWidget(row, ViewColumn::ObsSlides))->isChecked(),
|
||||||
|
.cameraPreset = cameraPreset,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
40
src/settingsdialog.hpp
Normal file
40
src/settingsdialog.hpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 - 2023 gary@drinkingtea.net
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
#include "settingsdata.hpp"
|
||||||
|
|
||||||
|
class SettingsDialog: public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
private:
|
||||||
|
class QLabel *m_errLbl = nullptr;
|
||||||
|
class QLineEdit *m_cameraIpLe = nullptr;
|
||||||
|
class QLineEdit *m_cameraPortLe = nullptr;
|
||||||
|
class QLineEdit *m_openLpIpLe = nullptr;
|
||||||
|
class QLineEdit *m_openLpPortLe = nullptr;
|
||||||
|
class QLineEdit *m_obsIpLe = nullptr;
|
||||||
|
class QLineEdit *m_obsPortLe = nullptr;
|
||||||
|
class QTableWidget *m_viewTable = nullptr;
|
||||||
|
public:
|
||||||
|
explicit SettingsDialog(QWidget *parent);
|
||||||
|
private:
|
||||||
|
QWidget *setupNetworkInputs(QWidget *parent);
|
||||||
|
QWidget *setupViewConfig(QWidget *parent);
|
||||||
|
QWidget *setupButtons(QWidget *parent);
|
||||||
|
void handleOK();
|
||||||
|
void setupViewRow(int row, View const&view = {});
|
||||||
|
/**
|
||||||
|
* Gets views from table.
|
||||||
|
* @return error code
|
||||||
|
*/
|
||||||
|
[[nodiscard("Must check error code")]]
|
||||||
|
int collectViews(QVector<View> &views) const;
|
||||||
|
};
|
@ -6,7 +6,6 @@
|
|||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <QComboBox>
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QHeaderView>
|
#include <QHeaderView>
|
||||||
@ -43,9 +42,9 @@ QString SlideView::getNextSong() const {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
void SlideView::pollUpdate(QString songName, int slide) {
|
void SlideView::pollUpdate(QString const&songName, int slide) {
|
||||||
auto songItems = m_songSelector->findItems(songName, Qt::MatchFixedString);
|
auto songItems = m_songSelector->findItems(songName, Qt::MatchFixedString);
|
||||||
if (songItems.size() < 1) {
|
if (songItems.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto songItem = songItems.first();
|
auto songItem = songItems.first();
|
||||||
@ -66,11 +65,11 @@ void SlideView::changeSong(int song) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SlideView::slideListUpdate(QStringList tagList, QStringList slideList) {
|
void SlideView::slideListUpdate(QStringList const&tagList, QStringList const&slideList) {
|
||||||
m_currentSlide = 0;
|
m_currentSlide = 0;
|
||||||
m_slideTable->setRowCount(slideList.size());
|
m_slideTable->setRowCount(static_cast<int>(slideList.size()));
|
||||||
for (int i = 0; i < slideList.size(); ++i) {
|
for (int i = 0; i < slideList.size(); ++i) {
|
||||||
auto txt = slideList[i];
|
const auto& txt = slideList[i];
|
||||||
auto item = new QTableWidgetItem(txt);
|
auto item = new QTableWidgetItem(txt);
|
||||||
item->setFlags(item->flags() & ~Qt::ItemIsEditable);
|
item->setFlags(item->flags() & ~Qt::ItemIsEditable);
|
||||||
m_slideTable->setItem(i, 0, item);
|
m_slideTable->setItem(i, 0, item);
|
||||||
@ -86,7 +85,7 @@ void SlideView::reset() {
|
|||||||
m_currentSlide = -1;
|
m_currentSlide = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SlideView::songListUpdate(QStringList songList) {
|
void SlideView::songListUpdate(QStringList const&songList) {
|
||||||
// Is this replacing an existing song list or is it the initial song list?
|
// Is this replacing an existing song list or is it the initial song list?
|
||||||
// We want to reset the song to 0 upon replacement,
|
// We want to reset the song to 0 upon replacement,
|
||||||
// but leave it alone upon initialization.
|
// but leave it alone upon initialization.
|
||||||
|
@ -24,11 +24,11 @@ class SlideView: public QWidget {
|
|||||||
QString getNextSong() const;
|
QString getNextSong() const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void pollUpdate(QString songId, int slideNum);
|
void pollUpdate(const QString& songId, int slideNum);
|
||||||
|
|
||||||
void songListUpdate(QStringList songList);
|
void songListUpdate(QStringList const&songList);
|
||||||
|
|
||||||
void slideListUpdate(QStringList tagList, QStringList songList);
|
void slideListUpdate(QStringList const&tagList, QStringList const&songList);
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user