Erster Commit

This commit is contained in:
2026-08-27 09:47:58 +02:00
commit 7d3adbf8b0
87 changed files with 26885 additions and 0 deletions

251
src/MainWindow.cpp Normal file
View File

@@ -0,0 +1,251 @@
#include "MainWindow.h"
#include "VideoWidget.h"
#include "CaptureThread.h"
#include "AudioThread.h"
#include <QKeyEvent>
#include <QCloseEvent>
#include <QStatusBar>
#include <QMenuBar>
#include <QMenu>
#include <QAction>
#include <QDialog>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QFrame>
MainWindow::MainWindow(const QString& videoDevice,
const QString& audioDevice,
int targetFps,
QWidget* parent)
: QMainWindow(parent)
, m_videoDevice(videoDevice)
, m_audioDevice(audioDevice)
{
setWindowTitle("HDMI Viewer");
resize(1280, 720);
m_video = new VideoWidget(this);
setCentralWidget(m_video);
setupMenuBar();
updateStatusBar();
// ── Capture ──────────────────────────────────────────────────────────────
m_capture = new CaptureThread(videoDevice, targetFps, this);
connect(m_capture, &CaptureThread::frameReady,
m_video, &VideoWidget::setFrame,
Qt::QueuedConnection);
connect(m_capture, &CaptureThread::errorOccurred,
this, &MainWindow::onError,
Qt::QueuedConnection);
connect(m_capture, &CaptureThread::captureInfo,
this, &MainWindow::onCaptureInfo,
Qt::QueuedConnection);
m_capture->start();
// ── Audio ─────────────────────────────────────────────────────────────────
if (!audioDevice.isEmpty()) {
m_audio = new AudioThread(audioDevice, this);
connect(m_audio, &AudioThread::errorOccurred,
this, &MainWindow::onError,
Qt::QueuedConnection);
m_audio->start();
}
}
MainWindow::~MainWindow()
{
stopAll();
}
void MainWindow::setupMenuBar()
{
QMenu* helpMenu = menuBar()->addMenu("&Hilfe");
QAction* aboutAct = helpMenu->addAction("Über HDMI Viewer");
aboutAct->setMenuRole(QAction::AboutRole);
connect(aboutAct, &QAction::triggered, this, &MainWindow::showAbout);
}
void MainWindow::showAbout()
{
auto* dlg = new QDialog(this);
dlg->setWindowTitle("Über HDMI Viewer");
dlg->setFixedSize(420, 340);
dlg->setModal(true);
auto* root = new QVBoxLayout(dlg);
root->setSpacing(0);
root->setContentsMargins(0, 0, 0, 0);
// ── Header-Bereich ────────────────────────────────────────────────────────
auto* header = new QWidget(dlg);
header->setFixedHeight(90);
header->setStyleSheet("background-color: #1e1e2e;");
auto* headerLayout = new QVBoxLayout(header);
headerLayout->setContentsMargins(24, 16, 24, 16);
auto* titleLabel = new QLabel("HDMI Viewer", header);
titleLabel->setStyleSheet("font-size: 22px; font-weight: bold; color: #cdd6f4;");
auto* subtitleLabel = new QLabel("Live-Vorschau für HDMI-Grabber", header);
subtitleLabel->setStyleSheet("font-size: 11px; color: #6c7086;");
headerLayout->addWidget(titleLabel);
headerLayout->addWidget(subtitleLabel);
// ── Trennlinie ────────────────────────────────────────────────────────────
auto* sep = new QFrame(dlg);
sep->setFrameShape(QFrame::HLine);
sep->setStyleSheet("color: #313244;");
// ── Info-Bereich ──────────────────────────────────────────────────────────
auto* body = new QWidget(dlg);
auto* grid = new QGridLayout(body);
grid->setContentsMargins(24, 20, 24, 8);
grid->setVerticalSpacing(10);
grid->setHorizontalSpacing(16);
grid->setColumnMinimumWidth(0, 100);
auto addRow = [&](int row, const QString& label, const QString& value) {
auto* l = new QLabel(label, body);
l->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
l->setStyleSheet("font-weight: bold; color: #a6adc8;");
auto* v = new QLabel(value, body);
v->setStyleSheet("color: #cdd6f4;");
v->setTextInteractionFlags(Qt::TextSelectableByMouse);
grid->addWidget(l, row, 0);
grid->addWidget(v, row, 1);
};
addRow(0, "Version", "1.3.0");
addRow(1, "Entwickler", "Dany Thinnes");
addRow(2, "Projekt", "Projekt Hirnfrei");
addRow(3, "Framework", QString("Qt %1").arg(QT_VERSION_STR));
addRow(4, "Sprache", "C++17");
addRow(5, "Video-API", "V4L2 · MJPEG / YUYV / NV12");
addRow(6, "Audio-API", "ALSA");
// ── Tastenkürzel-Hinweis ──────────────────────────────────────────────────
auto* sep2 = new QFrame(body);
sep2->setFrameShape(QFrame::HLine);
sep2->setStyleSheet("color: #313244;");
grid->addWidget(sep2, 7, 0, 1, 2);
auto* keysLabel = new QLabel(
"<small><b>F</b> &nbsp;Vollbild ein/aus &nbsp;&nbsp; "
"<b>Esc</b> &nbsp;Zurück zu Einstellungen</small>", body);
keysLabel->setStyleSheet("color: #6c7086;");
keysLabel->setAlignment(Qt::AlignCenter);
grid->addWidget(keysLabel, 8, 0, 1, 2);
// ── Schließen-Button ──────────────────────────────────────────────────────
auto* btnRow = new QHBoxLayout;
btnRow->setContentsMargins(24, 8, 24, 20);
auto* closeBtn = new QPushButton("Schließen", dlg);
closeBtn->setDefault(true);
closeBtn->setFixedWidth(100);
btnRow->addStretch();
btnRow->addWidget(closeBtn);
connect(closeBtn, &QPushButton::clicked, dlg, &QDialog::accept);
root->addWidget(header);
root->addWidget(sep);
root->addWidget(body, 1);
root->addLayout(btnRow);
dlg->exec();
dlg->deleteLater();
}
void MainWindow::stopAll()
{
if (m_capture) {
m_capture->stopAndWait();
delete m_capture;
m_capture = nullptr;
}
if (m_audio) {
m_audio->stop();
m_audio->wait();
delete m_audio;
m_audio = nullptr;
}
}
void MainWindow::keyPressEvent(QKeyEvent* event)
{
switch (event->key()) {
case Qt::Key_F:
toggleFullscreen();
break;
case Qt::Key_Escape:
if (m_fullscreen) {
toggleFullscreen();
} else {
m_wantsReopen = true;
close();
}
break;
default:
QMainWindow::keyPressEvent(event);
}
}
void MainWindow::closeEvent(QCloseEvent* event)
{
stopAll();
event->accept();
}
void MainWindow::toggleFullscreen()
{
if (m_fullscreen) {
showNormal();
menuBar()->show();
statusBar()->show();
} else {
showFullScreen();
menuBar()->hide();
statusBar()->hide();
}
m_fullscreen = !m_fullscreen;
}
void MainWindow::updateStatusBar()
{
QString info;
if (m_capWidth > 0)
info = QString("%1×%2 @ %3fps %4 ")
.arg(m_capWidth).arg(m_capHeight).arg(m_capFps).arg(m_capFmt);
info += QString("Video: %1 Audio: %2 [F] Vollbild [Esc] Einstellungen")
.arg(m_videoDevice,
m_audioDevice.isEmpty() ? QStringLiteral("") : m_audioDevice);
statusBar()->showMessage(info);
}
void MainWindow::onCaptureInfo(int width, int height, int fps, const QString& format)
{
m_capWidth = width;
m_capHeight = height;
m_capFps = fps;
m_capFmt = format;
updateStatusBar();
setWindowTitle(QString("HDMI Viewer %1×%2 @ %3fps %4")
.arg(width).arg(height).arg(fps).arg(format));
}
void MainWindow::onError(const QString& msg)
{
statusBar()->showMessage("" + msg, 8000);
qWarning() << "[Error]" << msg;
}