音乐播放器

演示管理 Windows DWM (桌面窗口管理器) 特征、跳转列表、任务栏按钮叠加和缩略图工具栏。

Screenshot of the Music Player example

音乐播放器范例演示如何使用各种提供特征通过 QtWinExtras 模块。

注意: 范例使用来自 QtMultimedia 模块的 QMediaPlayer 来播放音乐,但本文所聚焦的部分是 QtWinExtras 特征的使用。

DWM 特征

范例使用 Windows DWM (桌面窗口管理器) 特征以将窗口内容视觉集成到窗口框架,并使主窗口、音量弹出窗口半透明和模糊。

范例基于是否启用合成,应用不同外观。当合成被启用时,主窗口变半透明且窗口框架会扩展到客户端区域,以使窗口内容无缝集成到窗口框架,如上所示。当合成被禁用时,将着色颜色用作背景颜色。以下屏幕截图阐明 Music Player 范例外观如何当合成被禁用时。

Screenshot of the Music Player example

各自代码组合扩展或重置系统框架,调节必要 QWidget 属性,并设置适当样式表以达成期望外观。

void MusicPlayer::stylize()
{
    if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8) {
        // Set styling options relevant only to Windows 7.
        if (QtWin::isCompositionEnabled()) {
            QtWin::extendFrameIntoClientArea(this, -1, -1, -1, -1);
            setAttribute(Qt::WA_TranslucentBackground, true);
            setAttribute(Qt::WA_NoSystemBackground, false);
            setStyleSheet(QStringLiteral("MusicPlayer { background: transparent; }"));
        } else {
            QtWin::resetExtendedFrame(this);
            setAttribute(Qt::WA_TranslucentBackground, false);
            setStyleSheet(QStringLiteral("MusicPlayer { background: %1; }").arg(QtWin::realColorizationColor().name()));
        }
        volumeButton->stylize();
    }
}
					

音量弹出窗口没有窗口框架,因此足以模糊弹出窗口当启用合成时。此外,应用样式表能获得遵循着色颜色的边框。就像主窗口,当禁用合成时,着色颜色将用作背景颜色。

void VolumeButton::stylize()
{
    if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8) {
        // Set styling options relevant only to Windows 7.
        if (QtWin::isCompositionEnabled()) {
            QtWin::enableBlurBehindWindow(menu);
            QString css("QMenu { border: 1px solid %1; border-radius: 2px; background: transparent; }");
            menu->setStyleSheet(css.arg(QtWin::realColorizationColor().name()));
        } else {
            QtWin::disableBlurBehindWindow(menu);
            QString css("QMenu { border: 1px solid black; background: %1; }");
            menu->setStyleSheet(css.arg(QtWin::realColorizationColor().name()));
        }
    }
}
					

范例应用程序遵守用户合成设置,对动态合成改变作出应答,且看起来是实色不管合成是否被启用。它完成这通过捕获 QWinEvent::CompositionChange 和 QWinEvent::ColorizationChange 事件并相应调节其外观。

bool MusicPlayer::event(QEvent *event)
{
    if (event->type() == QWinEvent::CompositionChange || event->type() == QWinEvent::ColorizationChange)
        stylize();
    return QWidget::event(event);
}
					

跳转列表

范例创建自定义跳转列表,为用户提供对最近播放音乐文件的快速访问。

void MusicPlayer::createJumpList()
{
    QWinJumpList jumplist;
    jumplist.recent()->setVisible(true);
}
					

要启用应用程序跳转列表以展示期望的最近文件,使用以下帮手函数注册相应文件类型。

static bool associateFileTypes()
{
    QString displayName = QGuiApplication::applicationDisplayName();
    QString filePath = QCoreApplication::applicationFilePath();
    QString fileName = QFileInfo(filePath).fileName();
    const QString key = QStringLiteral("HKEY_CURRENT_USER\\Software\\Classes\\Applications\\") + fileName;
    QSettings settings(key, QSettings::NativeFormat);
    if (settings.status() != QSettings::NoError) {
        qWarning() << "Cannot access registry key" << key;
        return false;
    }
    settings.setValue(QStringLiteral("FriendlyAppName"), displayName);
    settings.beginGroup(QStringLiteral("SupportedTypes"));
    QMimeDatabase mimeDatabase;
    const QStringList supportedMimeTypes = MusicPlayer::supportedMimeTypes();
    for (const QString &fileType : supportedMimeTypes) {
        const QStringList suffixes = mimeDatabase.mimeTypeForName(fileType).suffixes();
        for (QString suffix : suffixes) {
            suffix.prepend('.');
            settings.setValue(suffix, QString());
        }
    }
    settings.endGroup();
    settings.beginGroup(QStringLiteral("shell"));
    settings.beginGroup(QStringLiteral("open"));
    settings.setValue(QStringLiteral("FriendlyAppName"), displayName);
    settings.beginGroup(QStringLiteral("Command"));
    settings.setValue(QStringLiteral("."),
                      u'"' + QDir::toNativeSeparators(filePath) + QStringLiteral("\" \"%1\""));
    return true;
}
					

任务栏叠加和进度

范例使用 Windows 任务栏为 2 件事;设置表示当前音乐回放状态的叠加图标,和在任务栏按钮指示回放进度。

Screenshot of the Music Player taskbar

以下代码片段展示任务栏按钮是如何准备的。

为使任务栏和缩略图工具栏工作,必须设置本机窗口句柄通过传递实例化的 QWindow to QWinTaskbarButton::setWindow() or QWinThumbnailToolBar::setWindow() ,分别。此实例的创建是在过程 QWidget::show() 且可以被检索通过调用 QWidget::windowHandle() 在之后。覆盖 QWidget::showEvent() 为此目的:

void MusicPlayer::showEvent(QShowEvent *event)
{
    QWidget::showEvent(event);
    if (!taskbarButton->window()) {
        auto window = windowHandle();
        taskbarButton->setWindow(window);
        thumbnailToolBar->setWindow(window);
    }
}
					

回放进度直接有线到任务栏进度指示器,通过信号和槽。

void MusicPlayer::createTaskbar()
{
    taskbarButton = new QWinTaskbarButton(this);
    taskbarProgress = taskbarButton->progress();
    connect(positionSlider, &QAbstractSlider::valueChanged, taskbarProgress, &QWinTaskbarProgress::setValue);
    connect(positionSlider, &QAbstractSlider::rangeChanged, taskbarProgress, &QWinTaskbarProgress::setRange);
    connect(&mediaPlayer, &QMediaPlayer::stateChanged, this, &MusicPlayer::updateTaskbar);
}
					

会更新覆盖图标和进度指示器,每当音乐回放状态改变时。

void MusicPlayer::updateTaskbar()
{
    switch (mediaPlayer.state()) {
    case QMediaPlayer::PlayingState:
        taskbarButton->setOverlayIcon(style()->standardIcon(QStyle::SP_MediaPlay));
        taskbarProgress->show();
        taskbarProgress->resume();
        break;
    case QMediaPlayer::PausedState:
        taskbarButton->setOverlayIcon(style()->standardIcon(QStyle::SP_MediaPause));
        taskbarProgress->show();
        taskbarProgress->pause();
        break;
    case QMediaPlayer::StoppedState:
        taskbarButton->setOverlayIcon(style()->standardIcon(QStyle::SP_MediaStop));
        taskbarProgress->hide();
        break;
    }
}
					

缩略图工具栏

Screenshot of the Music Player thumbnail

Windows 缩略图工具栏用于提供基本音乐回放控件。这些控件可用于控制应用程序,不必激活应用程序。

void MusicPlayer::createThumbnailToolBar()
{
    thumbnailToolBar = new QWinThumbnailToolBar(this);
    playToolButton = new QWinThumbnailToolButton(thumbnailToolBar);
    playToolButton->setEnabled(false);
    playToolButton->setToolTip(tr("Play"));
    playToolButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
    connect(playToolButton, &QWinThumbnailToolButton::clicked, this, &MusicPlayer::togglePlayback);
    forwardToolButton = new QWinThumbnailToolButton(thumbnailToolBar);
    forwardToolButton->setEnabled(false);
    forwardToolButton->setToolTip(tr("Fast forward"));
    forwardToolButton->setIcon(style()->standardIcon(QStyle::SP_MediaSeekForward));
    connect(forwardToolButton, &QWinThumbnailToolButton::clicked, this, &MusicPlayer::seekForward);
    backwardToolButton = new QWinThumbnailToolButton(thumbnailToolBar);
    backwardToolButton->setEnabled(false);
    backwardToolButton->setToolTip(tr("Rewind"));
    backwardToolButton->setIcon(style()->standardIcon(QStyle::SP_MediaSeekBackward));
    connect(backwardToolButton, &QWinThumbnailToolButton::clicked, this, &MusicPlayer::seekBackward);
    thumbnailToolBar->addButton(backwardToolButton);
    thumbnailToolBar->addButton(playToolButton);
    thumbnailToolBar->addButton(forwardToolButton);
    connect(&mediaPlayer, &QMediaPlayer::positionChanged, this, &MusicPlayer::updateThumbnailToolBar);
    connect(&mediaPlayer, &QMediaPlayer::durationChanged, this, &MusicPlayer::updateThumbnailToolBar);
    connect(&mediaPlayer, &QMediaPlayer::stateChanged, this, &MusicPlayer::updateThumbnailToolBar);
}
					

会相应更新缩略图工具栏按钮,每当音乐回放状态改变时。

void MusicPlayer::updateThumbnailToolBar()
{
    playToolButton->setEnabled(mediaPlayer.duration() > 0);
    backwardToolButton->setEnabled(mediaPlayer.position() > 0);
    forwardToolButton->setEnabled(mediaPlayer.position() < mediaPlayer.duration());
    if (mediaPlayer.state() == QMediaPlayer::PlayingState) {
        playToolButton->setToolTip(tr("Pause"));
        playToolButton->setIcon(style()->standardIcon(QStyle::SP_MediaPause));
    } else {
        playToolButton->setToolTip(tr("Play"));
        playToolButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
    }
}
					

范例工程 @ code.qt.io