Skip to content

Commit

Permalink
added long build media playback support
Browse files Browse the repository at this point in the history
  • Loading branch information
arBmind committed Jul 7, 2023
1 parent 9a599cb commit b4b7232
Show file tree
Hide file tree
Showing 10 changed files with 186 additions and 19 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build_cmake.yml
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ jobs:
COMMAND
sudo apt install chrpath
libgl1-mesa-dev libvulkan-dev libxcb-xinput-dev libxcb-xinerama0-dev libxkbcommon-dev libxkbcommon-x11-dev
libxcb-image0 libxcb-keysyms1 libxcb-render-util0 libxcb-xkb1 libxcb-randr0 libxcb-icccm4
libxcb-image0 libxcb-keysyms1 libxcb-render-util0 libxcb-xkb1 libxcb-randr0 libxcb-icccm4 libpulse0
xvfb
RESULT_VARIABLE result
)
Expand Down Expand Up @@ -251,7 +251,7 @@ jobs:
)
endforeach()
foreach(package qtimageformats qtserialport)
foreach(package qtimageformats qtserialport qtmultimedia)
downloadAndExtract(
"${qt_base_url}/qt.qt6.${qt_version_dotless}.addons.${package}.${qt_package_arch_suffix}/${qt_package_version}${package}${qt_package_suffix}.7z"
${package}.7z
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build_qbs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
os: windows-2022
qt_version: "6.5.1"
qt_arch: "win64_msvc2019_64"
qt_modules: qtimageformats qtserialport qtquicktimeline qtquick3d qt5compat qtshadertools
qt_modules: qtimageformats qtserialport qtquicktimeline qtquick3d qt5compat qtshadertools qtmultimedia
qt_tools: tools_openssl_x64,qt.tools.openssl.win_x64
qt_version_x86: "5.15.2"
qt_arch_x86: "win32_msvc2019"
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ if (MSVC AND QT_FEATURE_static_runtime)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()

find_package(Qt6 OPTIONAL_COMPONENTS Quick QuickWidgets Designer DesignerComponentsPrivate
find_package(Qt6 OPTIONAL_COMPONENTS Quick QuickWidgets Designer DesignerComponentsPrivate MultimediaWidgets
Help SerialPort Svg Tools LinguistTools QUIET)

find_package(Threads)
Expand Down
1 change: 1 addition & 0 deletions scripts/deployqt.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ def main():

plugins = ['assetimporters', 'accessible', 'codecs', 'designer', 'iconengines', 'imageformats', 'platformthemes',
'platforminputcontexts', 'platforms', 'printsupport', 'qmltooling', 'sqldrivers', 'styles',
'multimedia',
'xcbglintegrations',
'wayland-decoration-client',
'wayland-graphics-integration-client',
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/projectexplorer/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
add_qtc_plugin(ProjectExplorer
DEPENDS Qt::Qml
DEPENDS Qt::Qml Qt::MultimediaWidgets
PLUGIN_DEPENDS Core TextEditor
PLUGIN_TEST_DEPENDS GenericProjectManager
SOURCES
Expand Down
14 changes: 14 additions & 0 deletions src/plugins/projectexplorer/buildmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -498,13 +498,26 @@ void BuildManager::emitCancelMessage()
addToOutputWindow(Tr::tr("Canceled build/deployment."), BuildStep::OutputFormat::ErrorMessage);
}

static void handleLongBuild() {
if (std::chrono::milliseconds{d->m_elapsed.elapsed()} <= std::chrono::seconds{ProjectExplorerPlugin::projectExplorerSettings().longBuildThreshold}) {
return;
}
auto media = d->m_allStepsSucceeded
? ProjectExplorerPlugin::projectExplorerSettings().longBuildSuccessMediaPath
: ProjectExplorerPlugin::projectExplorerSettings().longBuildFailedMediaPath;
if (media.isEmpty()) return;
ProjectExplorerSettings::playAlertMedia(media);
}

void BuildManager::clearBuildQueue()
{
for (BuildStep *bs : std::as_const(d->m_buildQueue)) {
decrementActiveBuildSteps(bs);
disconnectOutput(bs);
}

handleLongBuild();

d->m_stepNames.clear();
d->m_buildQueue.clear();
d->m_enabledState.clear();
Expand Down Expand Up @@ -751,6 +764,7 @@ void BuildManager::nextStep()
d->m_currentBuildStep->setupOutputFormatter(d->m_outputWindow->outputFormatter());
d->m_currentBuildStep->run();
} else {
handleLongBuild();
d->m_running = false;
d->m_poppedUpTaskWindow = false;
d->m_isDeploying = false;
Expand Down
24 changes: 24 additions & 0 deletions src/plugins/projectexplorer/projectexplorer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,10 @@ const char ABORT_BUILD_ALL_ON_ERROR_SETTINGS_KEY[]
= "ProjectExplorer/Settings/AbortBuildAllOnError";
const char LOW_BUILD_PRIORITY_SETTINGS_KEY[] = "ProjectExplorer/Settings/LowBuildPriority";

const char LONG_BUILD_THRESHOLD_SETTINGS_KEY[] = "ProjectExplorer/Settings/LongBuildThreshold";
const char LONG_BUILD_SUCCESS_MEDIA_SETTINGS_KEY[] = "ProjectExplorer/Settings/LongBuildSuccessMedia";
const char LONG_BUILD_FAILED_MEDIA_SETTINGS_KEY[] = "ProjectExplorer/Settings/LongBuildFailedMedia";

const char CUSTOM_PARSER_COUNT_KEY[] = "ProjectExplorer/Settings/CustomParserCount";
const char CUSTOM_PARSER_PREFIX_KEY[] = "ProjectExplorer/Settings/CustomParser";

Expand Down Expand Up @@ -1682,6 +1686,16 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
= s->value(Constants::LOW_BUILD_PRIORITY_SETTINGS_KEY, defaultSettings.lowBuildPriority)
.toBool();

dd->m_projectExplorerSettings.longBuildThreshold
= s->value(Constants::LONG_BUILD_THRESHOLD_SETTINGS_KEY, defaultSettings.longBuildThreshold)
.toInt();
dd->m_projectExplorerSettings.longBuildSuccessMediaPath
= s->value(Constants::LONG_BUILD_SUCCESS_MEDIA_SETTINGS_KEY, defaultSettings.longBuildSuccessMediaPath)
.toString();
dd->m_projectExplorerSettings.longBuildFailedMediaPath
= s->value(Constants::LONG_BUILD_FAILED_MEDIA_SETTINGS_KEY, defaultSettings.longBuildFailedMediaPath)
.toString();

dd->m_buildPropertiesSettings.readSettings(s);

const int customParserCount = s->value(Constants::CUSTOM_PARSER_COUNT_KEY).toInt();
Expand Down Expand Up @@ -2256,6 +2270,16 @@ void ProjectExplorerPluginPrivate::savePersistentSettings()
int(dd->m_projectExplorerSettings.stopBeforeBuild),
int(defaultSettings.stopBeforeBuild));

s->setValueWithDefault(Constants::LONG_BUILD_THRESHOLD_SETTINGS_KEY,
int(dd->m_projectExplorerSettings.longBuildThreshold),
int(defaultSettings.longBuildThreshold));
s->setValueWithDefault(Constants::LONG_BUILD_SUCCESS_MEDIA_SETTINGS_KEY,
dd->m_projectExplorerSettings.longBuildSuccessMediaPath,
defaultSettings.longBuildSuccessMediaPath);
s->setValueWithDefault(Constants::LONG_BUILD_FAILED_MEDIA_SETTINGS_KEY,
dd->m_projectExplorerSettings.longBuildFailedMediaPath,
defaultSettings.longBuildFailedMediaPath);

dd->m_buildPropertiesSettings.writeSettings(s);

s->setValueWithDefault(Constants::CUSTOM_PARSER_COUNT_KEY, int(dd->m_customParsers.count()), 0);
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/projectexplorer/projectexplorer.qbs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Project {
name: "ProjectExplorer"

QtcPlugin {
Depends { name: "Qt"; submodules: ["widgets", "xml", "network", "qml"] }
Depends { name: "Qt"; submodules: ["widgets", "xml", "network", "qml", "multimedia", "multimediawidgets"] }
Depends { name: "Aggregation" }
Depends { name: "Utils" }

Expand Down
121 changes: 121 additions & 0 deletions src/plugins/projectexplorer/projectexplorersettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,101 @@
#include <utils/hostosinfo.h>
#include <utils/pathchooser.h>

#include <QAudioOutput>
#include <QButtonGroup>
#include <QCheckBox>
#include <QComboBox>
#include <QCoreApplication>
#include <QLabel>
#include <QMediaPlayer>
#include <QPushButton>
#include <QRadioButton>
#include <QRandomGenerator>
#include <QSpinBox>
#include <QVBoxLayout>
#include <QVideoSink>
#include <QVideoWidget>

using namespace Core;
using namespace Utils;

namespace ProjectExplorer {

class VideoDialog final : public QDialog {
public:
VideoDialog(QMediaPlayer* player, QVideoWidget* video) : QDialog() {
setLayout(new QVBoxLayout);
layout()->setContentsMargins(0, 0, 0, 0);
layout()->addWidget(video);
grabMouse();
grabKeyboard();
QObject::connect(
player, &QMediaPlayer::playingChanged, this,
[this](bool isPlaying) {
if (isPlaying) return;
if (isVisible()) close();
else deleteLater();
},
Qt::QueuedConnection);
QObject::connect(this, &QDialog::finished, this, [this, player]() {
if (player->isPlaying()) player->stop();
else deleteLater();
});
}

protected:
void mousePressEvent(QMouseEvent* event) override {
QDialog::mousePressEvent(event);
close();
}
void keyPressEvent(QKeyEvent* event) override {
QDialog::keyPressEvent(event);
close();
}
};

static auto resolveSourcePath(QString sourcePath) -> QUrl {
if (sourcePath.contains(';')) {
auto list = sourcePath.split(';');
auto index = QRandomGenerator::global()->bounded(0, list.size());
return QUrl::fromLocalFile(list[index]);
}
return QUrl::fromLocalFile(sourcePath);
}

void ProjectExplorerSettings::playAlertMedia(QString sourcePath)
{
static QMediaPlayer* player = []() {
qWarning("Creating Player with AudioOutput...");
auto player = new QMediaPlayer();
player->setAudioOutput(new QAudioOutput{player});
return player;
}();
player->stop();
player->setPosition(0);
player->setSource(resolveSourcePath(sourcePath));
if (player->hasVideo()) {
auto video = new QVideoWidget();
player->setVideoOutput(video);
QObject::connect(
player->videoSink(), &QVideoSink::videoFrameChanged, video,
[video]() {
auto dialog = new VideoDialog(player, video);
dialog->open();
},
Qt::SingleShotConnection);
player->play();
}
else if (player->hasAudio()) {
player->play();
}
else {
qWarning("Nothing to play!");
}
}

} // namespace ProjectExplorer

namespace ProjectExplorer::Internal {

enum { UseCurrentDirectory, UseProjectDirectory };
Expand Down Expand Up @@ -71,6 +156,10 @@ class ProjectExplorerSettingsWidget : public IOptionsPageWidget
QComboBox *m_terminalModeComboBox;
QCheckBox *m_jomCheckbox;

QSpinBox *m_longBuildThresholdSpinBox = new QSpinBox;
Utils::PathChooser *m_longBuildSuccessMediaPath = new Utils::PathChooser;
Utils::PathChooser *m_longBuildFailedMediaPath = new Utils::PathChooser;

QButtonGroup *m_directoryButtonGroup;
};

Expand Down Expand Up @@ -122,6 +211,23 @@ ProjectExplorerSettingsWidget::ProjectExplorerSettingsWidget()
"Disable it if you experience problems with your builds.");
jomLabel->setWordWrap(true);

m_longBuildThresholdSpinBox->setSuffix("secs");
m_longBuildThresholdSpinBox->setMinimum(0);
m_longBuildSuccessMediaPath->setToolTip(Tr::tr("Media File played if long build succeeds (empty to disable)"));
m_longBuildSuccessMediaPath->setExpectedKind(PathChooser::File);
m_longBuildSuccessMediaPath->setHistoryCompleter(QLatin1String("General.Media.History"));
auto playSuccessMediaButton = new QPushButton(Tr::tr("Play"));
connect(playSuccessMediaButton, &QPushButton::clicked, [&]() {
ProjectExplorerSettings::playAlertMedia(m_longBuildSuccessMediaPath->filePath().toString());
});
m_longBuildFailedMediaPath->setToolTip(Tr::tr("Media File played if long build fails (empty to disable)"));
m_longBuildFailedMediaPath->setExpectedKind(PathChooser::File);
m_longBuildFailedMediaPath->setHistoryCompleter(QLatin1String("General.Media.History"));
auto playFailedMediaButton = new QPushButton(Tr::tr("Play"));
connect(playFailedMediaButton, &QPushButton::clicked, [&]() {
ProjectExplorerSettings::playAlertMedia(m_longBuildFailedMediaPath->filePath().toString());
});

using namespace Layouting;
Column {
Group {
Expand Down Expand Up @@ -157,6 +263,14 @@ ProjectExplorerSettingsWidget::ProjectExplorerSettingsWidget()
jomLabel,
},
},
Group {
title(Tr::tr("Long Build Announcements")),
Form {
Tr::tr("Threshold:"), m_longBuildThresholdSpinBox, br,
Tr::tr("Success Media File:"), m_longBuildSuccessMediaPath, playSuccessMediaButton, br,
Tr::tr("Failed Media File:"), m_longBuildFailedMediaPath, playFailedMediaButton, br,
},
},
st,
}.attachTo(this);

Expand Down Expand Up @@ -193,6 +307,9 @@ ProjectExplorerSettings ProjectExplorerSettingsWidget::settings() const
m_settings.clearIssuesOnRebuild = m_clearIssuesCheckBox->isChecked();
m_settings.abortBuildAllOnError = m_abortBuildAllOnErrorCheckBox->isChecked();
m_settings.lowBuildPriority = m_lowBuildPriorityCheckBox->isChecked();
m_settings.longBuildThreshold = m_longBuildThresholdSpinBox->value();
m_settings.longBuildSuccessMediaPath = m_longBuildSuccessMediaPath->filePath().toString();
m_settings.longBuildFailedMediaPath = m_longBuildFailedMediaPath->filePath().toString();
return m_settings;
}

Expand All @@ -214,6 +331,10 @@ void ProjectExplorerSettingsWidget::setSettings(const ProjectExplorerSettings &
m_clearIssuesCheckBox->setChecked(m_settings.clearIssuesOnRebuild);
m_abortBuildAllOnErrorCheckBox->setChecked(m_settings.abortBuildAllOnError);
m_lowBuildPriorityCheckBox->setChecked(m_settings.lowBuildPriority);

m_longBuildThresholdSpinBox->setValue(m_settings.longBuildThreshold);
m_longBuildSuccessMediaPath->setPath(m_settings.longBuildSuccessMediaPath);
m_longBuildFailedMediaPath->setPath(m_settings.longBuildFailedMediaPath);
}

FilePath ProjectExplorerSettingsWidget::projectsDirectory() const
Expand Down
33 changes: 20 additions & 13 deletions src/plugins/projectexplorer/projectexplorersettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,20 @@ class ProjectExplorerSettings
friend bool operator==(const ProjectExplorerSettings &p1, const ProjectExplorerSettings &p2)
{
return p1.buildBeforeDeploy == p2.buildBeforeDeploy
&& p1.deployBeforeRun == p2.deployBeforeRun
&& p1.saveBeforeBuild == p2.saveBeforeBuild
&& p1.useJom == p2.useJom
&& p1.prompToStopRunControl == p2.prompToStopRunControl
&& p1.automaticallyCreateRunConfigurations == p2.automaticallyCreateRunConfigurations
&& p1.addLibraryPathsToRunEnv == p2.addLibraryPathsToRunEnv
&& p1.environmentId == p2.environmentId
&& p1.stopBeforeBuild == p2.stopBeforeBuild
&& p1.terminalMode == p2.terminalMode
&& p1.closeSourceFilesWithProject == p2.closeSourceFilesWithProject
&& p1.clearIssuesOnRebuild == p2.clearIssuesOnRebuild
&& p1.abortBuildAllOnError == p2.abortBuildAllOnError
&& p1.lowBuildPriority == p2.lowBuildPriority;
&& p1.deployBeforeRun == p2.deployBeforeRun
&& p1.saveBeforeBuild == p2.saveBeforeBuild && p1.useJom == p2.useJom
&& p1.prompToStopRunControl == p2.prompToStopRunControl
&& p1.automaticallyCreateRunConfigurations == p2.automaticallyCreateRunConfigurations
&& p1.addLibraryPathsToRunEnv == p2.addLibraryPathsToRunEnv
&& p1.environmentId == p2.environmentId && p1.stopBeforeBuild == p2.stopBeforeBuild
&& p1.terminalMode == p2.terminalMode
&& p1.longBuildThreshold == p2.longBuildThreshold
&& p1.longBuildSuccessMediaPath == p2.longBuildSuccessMediaPath
&& p1.longBuildFailedMediaPath == p2.longBuildFailedMediaPath
&& p1.closeSourceFilesWithProject == p2.closeSourceFilesWithProject
&& p1.clearIssuesOnRebuild == p2.clearIssuesOnRebuild
&& p1.abortBuildAllOnError == p2.abortBuildAllOnError
&& p1.lowBuildPriority == p2.lowBuildPriority;
}

BuildBeforeRunMode buildBeforeDeploy = BuildBeforeRunMode::WholeProject;
Expand All @@ -53,6 +54,12 @@ class ProjectExplorerSettings
: StopBeforeBuild::None;
TerminalMode terminalMode = TerminalMode::Off;

int longBuildThreshold = 30;
QString longBuildSuccessMediaPath = {};
QString longBuildFailedMediaPath = {};

static void playAlertMedia(QString sourcePath);

// Add a UUid which is used to identify the development environment.
// This is used to warn the user when he is trying to open a .user file that was created
// somewhere else (which might lead to unexpected results).
Expand Down

0 comments on commit b4b7232

Please sign in to comment.