Erster Teststream absolviert. Positives Ergebnis
This commit is contained in:
@@ -9,18 +9,28 @@
|
||||
#include <QMenuBar>
|
||||
#include <QMenu>
|
||||
#include <QAction>
|
||||
#include <QSettings>
|
||||
#include <QDialog>
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QFrame>
|
||||
#include <QTimer>
|
||||
#include <QDir>
|
||||
#include <QDateTime>
|
||||
|
||||
MainWindow::MainWindow(const QString& videoDevice,
|
||||
const QString& audioDevice,
|
||||
int targetFps,
|
||||
bool smoothModeEnabled,
|
||||
bool retroModeEnabled,
|
||||
float brightness,
|
||||
float sharpenAmount,
|
||||
float lastSharpenAmount,
|
||||
QWidget* parent)
|
||||
: QMainWindow(parent)
|
||||
, m_lastSharpenAmount(lastSharpenAmount)
|
||||
, m_videoDevice(videoDevice)
|
||||
, m_audioDevice(audioDevice)
|
||||
{
|
||||
@@ -28,8 +38,20 @@ MainWindow::MainWindow(const QString& videoDevice,
|
||||
resize(1280, 720);
|
||||
|
||||
m_video = new VideoWidget(this);
|
||||
m_video->setSmoothModeEnabled(smoothModeEnabled);
|
||||
m_video->setRetroModeEnabled(retroModeEnabled);
|
||||
m_video->setSharpenAmount(sharpenAmount);
|
||||
m_video->setBrightness(brightness);
|
||||
setCentralWidget(m_video);
|
||||
|
||||
// Kurzzeitige Einblendung unten links (funktioniert auch im Vollbild,
|
||||
// wo Menü- und Statusleiste ausgeblendet sind).
|
||||
m_overlayLabel = new QLabel(m_video);
|
||||
m_overlayLabel->setStyleSheet(
|
||||
"background-color: rgba(20, 20, 20, 200); color: white;"
|
||||
"padding: 6px 12px; border-radius: 4px; font-size: 13px;");
|
||||
m_overlayLabel->hide();
|
||||
|
||||
setupMenuBar();
|
||||
updateStatusBar();
|
||||
|
||||
@@ -45,6 +67,12 @@ MainWindow::MainWindow(const QString& videoDevice,
|
||||
connect(m_capture, &CaptureThread::captureInfo,
|
||||
this, &MainWindow::onCaptureInfo,
|
||||
Qt::QueuedConnection);
|
||||
connect(m_capture, &CaptureThread::frameDumped,
|
||||
this, &MainWindow::onFrameDumped,
|
||||
Qt::QueuedConnection);
|
||||
connect(m_capture, &CaptureThread::hardwareControlChanged,
|
||||
this, &MainWindow::onHardwareControlChanged,
|
||||
Qt::QueuedConnection);
|
||||
|
||||
m_capture->start();
|
||||
|
||||
@@ -65,6 +93,54 @@ MainWindow::~MainWindow()
|
||||
|
||||
void MainWindow::setupMenuBar()
|
||||
{
|
||||
QMenu* viewMenu = menuBar()->addMenu("&Anzeige");
|
||||
|
||||
m_smoothAction = viewMenu->addAction("Bildglättung\tS");
|
||||
m_smoothAction->setCheckable(true);
|
||||
m_smoothAction->setChecked(m_video->smoothModeEnabled());
|
||||
connect(m_smoothAction, &QAction::triggered, this, &MainWindow::toggleSmoothMode);
|
||||
|
||||
m_retroAction = viewMenu->addAction("Retro-Optik (Scanlines/Maske)\tC");
|
||||
m_retroAction->setCheckable(true);
|
||||
m_retroAction->setChecked(m_video->retroModeEnabled());
|
||||
connect(m_retroAction, &QAction::triggered, this, &MainWindow::toggleRetroMode);
|
||||
|
||||
m_sharpenAction = viewMenu->addAction("Nachschärfen\tX");
|
||||
m_sharpenAction->setCheckable(true);
|
||||
m_sharpenAction->setChecked(m_video->sharpenAmount() > 0.0f);
|
||||
connect(m_sharpenAction, &QAction::triggered, this, &MainWindow::toggleSharpen);
|
||||
|
||||
QAction* sharpenUpAct = viewMenu->addAction("Schärfen +\t+");
|
||||
connect(sharpenUpAct, &QAction::triggered, this, &MainWindow::increaseSharpen);
|
||||
|
||||
QAction* sharpenDownAct = viewMenu->addAction("Schärfen -\t-");
|
||||
connect(sharpenDownAct, &QAction::triggered, this, &MainWindow::decreaseSharpen);
|
||||
|
||||
viewMenu->addSeparator();
|
||||
|
||||
QAction* brighterAct = viewMenu->addAction("Heller\t.");
|
||||
connect(brighterAct, &QAction::triggered, this, &MainWindow::increaseBrightness);
|
||||
|
||||
QAction* darkerAct = viewMenu->addAction("Dunkler\t,");
|
||||
connect(darkerAct, &QAction::triggered, this, &MainWindow::decreaseBrightness);
|
||||
|
||||
viewMenu->addSeparator();
|
||||
|
||||
QAction* rawAct = viewMenu->addAction("Roh-Modus (Diagnose)\tR");
|
||||
rawAct->setCheckable(true);
|
||||
rawAct->setChecked(m_video->rawEnabled());
|
||||
connect(rawAct, &QAction::triggered, this, &MainWindow::toggleRaw);
|
||||
|
||||
QAction* compareAct = viewMenu->addAction("Live-Vergleich (roh/bearbeitet)\tV");
|
||||
compareAct->setCheckable(true);
|
||||
compareAct->setChecked(m_video->compareEnabled());
|
||||
connect(compareAct, &QAction::triggered, this, &MainWindow::toggleCompare);
|
||||
|
||||
viewMenu->addSeparator();
|
||||
|
||||
QAction* screenshotAct = viewMenu->addAction("Screenshot speichern\tP");
|
||||
connect(screenshotAct, &QAction::triggered, this, &MainWindow::saveScreenshot);
|
||||
|
||||
QMenu* helpMenu = menuBar()->addMenu("&Hilfe");
|
||||
|
||||
QAction* aboutAct = helpMenu->addAction("Über HDMI Viewer");
|
||||
@@ -72,11 +148,155 @@ void MainWindow::setupMenuBar()
|
||||
connect(aboutAct, &QAction::triggered, this, &MainWindow::showAbout);
|
||||
}
|
||||
|
||||
void MainWindow::showOverlayMessage(const QString& text)
|
||||
{
|
||||
if (!m_overlayLabel) return;
|
||||
|
||||
m_overlayLabel->setText(text);
|
||||
m_overlayLabel->adjustSize();
|
||||
|
||||
const int margin = 16;
|
||||
m_overlayLabel->move(margin, m_video->height() - m_overlayLabel->height() - margin);
|
||||
m_overlayLabel->show();
|
||||
m_overlayLabel->raise();
|
||||
|
||||
QTimer::singleShot(1500, m_overlayLabel, &QLabel::hide);
|
||||
}
|
||||
|
||||
void MainWindow::toggleSmoothMode()
|
||||
{
|
||||
const bool enabled = !m_video->smoothModeEnabled();
|
||||
m_video->setSmoothModeEnabled(enabled);
|
||||
if (m_smoothAction) m_smoothAction->setChecked(enabled);
|
||||
|
||||
QSettings settings;
|
||||
settings.setValue("display/smooth", enabled);
|
||||
|
||||
statusBar()->showMessage(
|
||||
enabled ? "Bildglättung aktiviert" : "Bildglättung deaktiviert", 2000);
|
||||
showOverlayMessage(enabled ? "Bildglättung: an" : "Bildglättung: aus");
|
||||
}
|
||||
|
||||
void MainWindow::toggleRetroMode()
|
||||
{
|
||||
const bool enabled = !m_video->retroModeEnabled();
|
||||
m_video->setRetroModeEnabled(enabled);
|
||||
if (m_retroAction) m_retroAction->setChecked(enabled);
|
||||
|
||||
QSettings settings;
|
||||
settings.setValue("display/retro", enabled);
|
||||
|
||||
statusBar()->showMessage(
|
||||
enabled ? "Retro-Optik aktiviert" : "Retro-Optik deaktiviert", 2000);
|
||||
showOverlayMessage(enabled ? "Retro-Optik: an" : "Retro-Optik: aus");
|
||||
}
|
||||
|
||||
void MainWindow::toggleSharpen()
|
||||
{
|
||||
// Quick-Toggle: aus (0) ↔ zuletzt benutzter Wert (nicht der feste
|
||||
// Standardwert!) – merkt sich also z. B. 7.0, nicht immer 2.5.
|
||||
if (m_video->sharpenAmount() > 0.0f) {
|
||||
m_lastSharpenAmount = m_video->sharpenAmount();
|
||||
applySharpen(0.0f);
|
||||
} else {
|
||||
applySharpen(m_lastSharpenAmount > 0.0f ? m_lastSharpenAmount : VideoWidget::kSharpenDefault);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::increaseSharpen()
|
||||
{
|
||||
applySharpen(m_video->sharpenAmount() + VideoWidget::kSharpenStep);
|
||||
}
|
||||
|
||||
void MainWindow::decreaseSharpen()
|
||||
{
|
||||
applySharpen(m_video->sharpenAmount() - VideoWidget::kSharpenStep);
|
||||
}
|
||||
|
||||
void MainWindow::applySharpen(float value)
|
||||
{
|
||||
value = qBound(VideoWidget::kSharpenMin, value, VideoWidget::kSharpenMax);
|
||||
m_video->setSharpenAmount(value);
|
||||
if (m_sharpenAction) m_sharpenAction->setChecked(value > 0.0f);
|
||||
|
||||
QSettings settings;
|
||||
settings.setValue("display/sharpen", static_cast<double>(value));
|
||||
if (value > 0.0f) {
|
||||
m_lastSharpenAmount = value;
|
||||
settings.setValue("display/sharpen_last", static_cast<double>(value));
|
||||
}
|
||||
|
||||
statusBar()->showMessage(QString("Nachschärfen: %1").arg(value, 0, 'f', 1), 2000);
|
||||
showOverlayMessage(QString("Nachschärfen: %1").arg(value, 0, 'f', 1));
|
||||
}
|
||||
|
||||
void MainWindow::increaseBrightness()
|
||||
{
|
||||
applyBrightness(m_video->brightness() + VideoWidget::kBrightnessStep);
|
||||
}
|
||||
|
||||
void MainWindow::decreaseBrightness()
|
||||
{
|
||||
applyBrightness(m_video->brightness() - VideoWidget::kBrightnessStep);
|
||||
}
|
||||
|
||||
void MainWindow::toggleRaw()
|
||||
{
|
||||
const bool enabled = !m_video->rawEnabled();
|
||||
m_video->setRawEnabled(enabled);
|
||||
|
||||
const QString msg = enabled ? "Roh-Modus: an (alle Effekte umgangen)" : "Roh-Modus: aus";
|
||||
statusBar()->showMessage(msg, 2000);
|
||||
showOverlayMessage(msg);
|
||||
}
|
||||
|
||||
void MainWindow::toggleCompare()
|
||||
{
|
||||
const bool enabled = !m_video->compareEnabled();
|
||||
m_video->setCompareEnabled(enabled);
|
||||
|
||||
const QString msg = enabled ? "Vergleich: an (links roh, rechts bearbeitet)" : "Vergleich: aus";
|
||||
statusBar()->showMessage(msg, 2000);
|
||||
showOverlayMessage(msg);
|
||||
}
|
||||
|
||||
void MainWindow::saveScreenshot()
|
||||
{
|
||||
const QImage img = m_video->grabProcessedFrame();
|
||||
if (img.isNull()) {
|
||||
statusBar()->showMessage("Screenshot fehlgeschlagen (kein Bild)", 3000);
|
||||
return;
|
||||
}
|
||||
|
||||
const QString path = QDir::homePath() + "/hdmiviewer_screenshot_" +
|
||||
QDateTime::currentDateTime().toString("yyyy-MM-dd_HH-mm-ss") + ".png";
|
||||
|
||||
if (img.save(path, "PNG")) {
|
||||
statusBar()->showMessage("Screenshot gespeichert: " + path, 5000);
|
||||
showOverlayMessage("Screenshot gespeichert");
|
||||
} else {
|
||||
statusBar()->showMessage("Screenshot konnte nicht gespeichert werden: " + path, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::applyBrightness(float value)
|
||||
{
|
||||
value = qBound(VideoWidget::kBrightnessMin, value, VideoWidget::kBrightnessMax);
|
||||
m_video->setBrightness(value);
|
||||
|
||||
QSettings settings;
|
||||
settings.setValue("display/brightness", static_cast<double>(value));
|
||||
|
||||
statusBar()->showMessage(
|
||||
QString("Helligkeit: %1 %").arg(qRound(value * 100.0f)), 2000);
|
||||
showOverlayMessage(QString("Helligkeit: %1 %").arg(qRound(value * 100.0f)));
|
||||
}
|
||||
|
||||
void MainWindow::showAbout()
|
||||
{
|
||||
auto* dlg = new QDialog(this);
|
||||
dlg->setWindowTitle("Über HDMI Viewer");
|
||||
dlg->setFixedSize(420, 340);
|
||||
dlg->setFixedSize(460, 470);
|
||||
dlg->setModal(true);
|
||||
|
||||
auto* root = new QVBoxLayout(dlg);
|
||||
@@ -125,26 +345,37 @@ void MainWindow::showAbout()
|
||||
grid->addWidget(v, row, 1);
|
||||
};
|
||||
|
||||
addRow(0, "Version", "1.3.0");
|
||||
addRow(0, "Version", "1.4.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");
|
||||
addRow(7, "Rendering", "OpenGL 3.3 · Bildglättung / Retro-Optik");
|
||||
|
||||
// ── Tastenkürzel-Hinweis ──────────────────────────────────────────────────
|
||||
auto* sep2 = new QFrame(body);
|
||||
sep2->setFrameShape(QFrame::HLine);
|
||||
sep2->setStyleSheet("color: #313244;");
|
||||
grid->addWidget(sep2, 7, 0, 1, 2);
|
||||
grid->addWidget(sep2, 8, 0, 1, 2);
|
||||
|
||||
auto* keysLabel = new QLabel(
|
||||
"<small><b>F</b> Vollbild ein/aus "
|
||||
"<b>Esc</b> Zurück zu Einstellungen</small>", body);
|
||||
"<small><b>F</b> Vollbild "
|
||||
"<b>S</b> Glättung "
|
||||
"<b>C</b> Retro "
|
||||
"<b>X</b>/<b>+</b>/<b>-</b> Schärfen "
|
||||
"<b>,</b>/<b>.</b> Helligkeit<br>"
|
||||
"<b>1</b>/<b>2</b> HW-Kontrast "
|
||||
"<b>3</b>/<b>4</b> HW-Sättigung "
|
||||
"<b>R</b> Roh-Diagnose<br>"
|
||||
"<b>V</b> Vergleich "
|
||||
"<b>P</b> Screenshot "
|
||||
"<b>D</b> Frame dumpen "
|
||||
"<b>Esc</b> Zurück</small>", body);
|
||||
keysLabel->setStyleSheet("color: #6c7086;");
|
||||
keysLabel->setAlignment(Qt::AlignCenter);
|
||||
grid->addWidget(keysLabel, 8, 0, 1, 2);
|
||||
grid->addWidget(keysLabel, 9, 0, 1, 2);
|
||||
|
||||
// ── Schließen-Button ──────────────────────────────────────────────────────
|
||||
auto* btnRow = new QHBoxLayout;
|
||||
@@ -194,6 +425,52 @@ void MainWindow::keyPressEvent(QKeyEvent* event)
|
||||
close();
|
||||
}
|
||||
break;
|
||||
case Qt::Key_S:
|
||||
toggleSmoothMode();
|
||||
break;
|
||||
case Qt::Key_C:
|
||||
toggleRetroMode();
|
||||
break;
|
||||
case Qt::Key_X:
|
||||
toggleSharpen();
|
||||
break;
|
||||
case Qt::Key_Plus:
|
||||
case Qt::Key_Equal: // '+' liegt auf vielen Layouts ohne Shift auf dieser Taste
|
||||
increaseSharpen();
|
||||
break;
|
||||
case Qt::Key_Minus:
|
||||
decreaseSharpen();
|
||||
break;
|
||||
case Qt::Key_Period:
|
||||
increaseBrightness();
|
||||
break;
|
||||
case Qt::Key_Comma:
|
||||
decreaseBrightness();
|
||||
break;
|
||||
case Qt::Key_R:
|
||||
toggleRaw();
|
||||
break;
|
||||
case Qt::Key_D:
|
||||
if (m_capture) m_capture->requestFrameDump();
|
||||
break;
|
||||
case Qt::Key_V:
|
||||
toggleCompare();
|
||||
break;
|
||||
case Qt::Key_P:
|
||||
saveScreenshot();
|
||||
break;
|
||||
case Qt::Key_1:
|
||||
if (m_capture) m_capture->adjustHardwareContrast(-5);
|
||||
break;
|
||||
case Qt::Key_2:
|
||||
if (m_capture) m_capture->adjustHardwareContrast(5);
|
||||
break;
|
||||
case Qt::Key_3:
|
||||
if (m_capture) m_capture->adjustHardwareSaturation(-5);
|
||||
break;
|
||||
case Qt::Key_4:
|
||||
if (m_capture) m_capture->adjustHardwareSaturation(5);
|
||||
break;
|
||||
default:
|
||||
QMainWindow::keyPressEvent(event);
|
||||
}
|
||||
@@ -226,7 +503,7 @@ void MainWindow::updateStatusBar()
|
||||
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")
|
||||
info += QString("Video: %1 Audio: %2 [F] Vollbild [S] Glättung [C] Retro [X/+/-] Schärfen [,/.] Helligkeit [1/2] HW-Kontrast [3/4] HW-Sättigung [R] Roh [V] Vergleich [P] Screenshot [D] Frame dumpen [Esc] Einstellungen")
|
||||
.arg(m_videoDevice,
|
||||
m_audioDevice.isEmpty() ? QStringLiteral("–") : m_audioDevice);
|
||||
|
||||
@@ -244,6 +521,20 @@ void MainWindow::onCaptureInfo(int width, int height, int fps, const QString& fo
|
||||
.arg(width).arg(height).arg(fps).arg(format));
|
||||
}
|
||||
|
||||
void MainWindow::onFrameDumped(const QString& path)
|
||||
{
|
||||
const QString msg = "Frame gespeichert: " + path;
|
||||
statusBar()->showMessage(msg, 5000);
|
||||
showOverlayMessage("Frame gespeichert (siehe Home-Ordner)");
|
||||
}
|
||||
|
||||
void MainWindow::onHardwareControlChanged(const QString& label, int value, int minVal, int maxVal)
|
||||
{
|
||||
const QString msg = QString("%1: %2 (%3..%4)").arg(label).arg(value).arg(minVal).arg(maxVal);
|
||||
statusBar()->showMessage(msg, 2000);
|
||||
showOverlayMessage(msg);
|
||||
}
|
||||
|
||||
void MainWindow::onError(const QString& msg)
|
||||
{
|
||||
statusBar()->showMessage("⚠ " + msg, 8000);
|
||||
|
||||
Reference in New Issue
Block a user