简单视频播放器的界面,由Video
和Image
项组成,由Blend
QtGraphicalEffects
混合而成。 Image
已将MouseArea
和半透明图形按钮与英文“硬编码”文本相关联。 QQuickImageProvider
从以下位置提供的默认图片:
import QtQml 2.2
import QtQuick 2.9
import QtMultimedia 5.9
import QtGraphicalEffects 1.0
Blend {
id: blender
mode: "normal"
property alias playbackState: video.playbackState
onVisibleChanged: {
if (!visible) {
video.playlist.clear()
}
}
source: Video {
id: video
visible: false
width: blender.width
height: blender.height
fillMode: VideoOutput.Stretch
playlist: Playlist {
onErrorChanged: {
if (error() !== Playlist.NoError) {
console.log("Error: %1 (%2)".arg(error()).arg(errorString()))
video.stop()
}
}
function nextInLoop() {
currentIndex = (currentIndex + itemCount + 1) % itemCount
}
function previousInLoop() {
currentIndex = (currentIndex + itemCount - 1) % itemCount
}
}
Connections {
target: videoPlayerSingleton
onSetPlaylist: {
if (video.playlist.clear()) {
if (video.playlist.addItems(playlist)) {
if (index < 0) {
video.playlist.playbackMode = Playlist.Random
} else {
video.playlist.currentIndex = index
video.playlist.playbackMode = Playlist.Sequential
}
video.play()
}
}
}
}
autoPlay: true
loops: MediaPlayer.Infinite
}
foregroundSource: Image {
visible: false
width: blender.width
height: blender.height
sourceSize: Qt.size(width, height)
source: {
switch (playbackState) {
case MediaPlayer.PlayingState : return "image://videoplayer/pause"
case MediaPlayer.PausedState : return "image://videoplayer/play"
case MediaPlayer.StoppedState : return "image://videoplayer/logo"
}
}
smooth: true
}
MouseArea {
anchors.fill: parent
readonly property rect previousButton: Qt.rect((130 - 40) / 1920, (505 - 40) / 1080, 80 / 1920, 80 / 1080)
readonly property rect nextButton: Qt.rect((1800 - 40) / 1920, (505 - 40) / 1080, 80 / 1920, 80 / 1080)
readonly property rect playPauseButton: Qt.rect((950 - 40) / 1920, (980 - 40) / 1080, 80 / 1920, 80 / 1080)
readonly property rect backToCatalogButton: Qt.rect((1510 - 40) / 1920, (980 - 40) / 1080, 80 / 1920, 80 / 1080)
readonly property rect openCollectionButton: Qt.rect((1710 - 40) / 1920, (980 - 40) / 1080, 80 / 1920, 80 / 1080)
onClicked: {
var hit = Qt.point(mouse.x / width, mouse.y / height)
if (videoPlayerSingleton.contains(previousButton, hit)) {
video.playlist.previousInLoop()
} else if (videoPlayerSingleton.contains(nextButton, hit)) {
video.playlist.nextInLoop()
} else if (videoPlayerSingleton.contains(playPauseButton, hit)) {
if (playbackState === MediaPlayer.PlayingState) {
video.pause()
} else {
video.play()
}
} else if (videoPlayerSingleton.contains(backToCatalogButton, hit)) {
videoPlayerSingleton.backToCatalog(video.playlist.currentIndex)
} else if (videoPlayerSingleton.contains(openCollectionButton, hit)) {
videoPlayerSingleton.openCollection(video.playlist.currentIndex)
}
}
}
}
+ru_RU/
子文件夹中还有相应的图像进入资源。
来自全局上下文的C ++ QObject
单圈可能具有信号void languageChanged(QLocale locale);
,当QTranslator
替换每个加载的资源时,所有QCoreApplication::removeTranslator/installTranslator
被发出。
// i18n.hpp:
#pragma once
#include <QtCore>
Q_DECLARE_LOGGING_CATEGORY(i18nCategory)
#ifndef PROJECT_DEFAULT_LOCALE
#define PROJECT_DEFAULT_LOCALE "ru_RU"
#endif
class Internationalization
: public QObject
{
Q_OBJECT
static QMutex mutex;
static Internationalization * self;
explicit Internationalization(QString projectName = QStringLiteral(PROJECT_NAME));
public :
static
Internationalization * instance()
{
Q_ASSERT(qApp);
QMutexLocker lock{&mutex};
if (!self) {
self = ::new Internationalization;
}
return self;
}
void setDependencies(QStringList dependencies);
public Q_SLOTS :
void load(QLocale locale = QStringLiteral(PROJECT_DEFAULT_LOCALE));
Q_SIGNALS :
void aboutToLanguageChanged();
void languageChanged(QLocale locale);
private :
QStringList translations;
QList< QPointer< QTranslator > > translators;
};
#define i18n Internationalization::instance()
// i18n.cpp:
#include "i18n.hpp"
Q_LOGGING_CATEGORY(i18nCategory, "internationalization")
QMutex Internationalization::mutex;
Internationalization * Internationalization::self = Q_NULLPTR;
Internationalization::Internationalization(QString projectName)
: QObject{qApp}
{
translations.prepend(projectName);
translations.prepend(PROJECT_NAME);
}
void Internationalization::setDependencies(QStringList dependencies)
{
translations << dependencies;
}
void Internationalization::load(QLocale locale)
{
// get_target_property(QT_QMAKE_EXECUTABLE Qt5::qmake IMPORTED_LOCATION)
// qmake -query QT_INSTALL_TRANSLATIONS
Q_ASSERT(!(translators.size() > translations.size()));
while (translators.size() < translations.size()) {
translators << ::new QTranslator{qApp};
}
Q_EMIT aboutToLanguageChanged();
QLocale::setDefault(locale);
QMutableListIterator translator{translators};
for (const auto translation : translations) {
Q_ASSERT(translator.hasNext());
const auto t = translator.next();
if (!QCoreApplication::removeTranslator(t)) {
if (!t->isEmpty()) {
qCDebug(i18nCategory).noquote()
<< tr("Unable to remove translation from project %1")
.arg(translation);
}
}
if (t->load(locale, translation, ".", ":/i18n")) {
if (!QCoreApplication::installTranslator(t)) {
qCDebug(i18nCategory).noquote()
<< tr("Unable to install translation for %1 locale from project %2")
.arg(locale.name(), translation);
}
} else {
qCDebug(i18nCategory).noquote()
<< tr("Unable to load translation for %1 locale from project %2")
.arg(locale.name(), translation);
}
}
Q_EMIT languageChanged(locale);
}
我想改变随着语言环境变化而发出的信号的图像位置。是否可以在运行时更改所选文件(即无需重新创建包含Item
的父VideoPlayer
}?
已连接的问题:首先受文件选择器影响的是:QImageReader
在 Quick 的QQuickImageProvider
或source:
内部使用的文件路径{{1} }}?后者来自第一个。或者两者兼而有之?
答案 0 :(得分:0)
方法是将您的语言环境分配给QML属性,然后将绑定表达式中的视频playbackState枚举属性用于source
项的Image
属性。
单击该按钮时,将更改QML语言环境属性,并自动重新评估source
项Image
上的绑定表达式,从而使QQuickImageProvider提供新图像。
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
import QtMultimedia 5.9
ApplicationWindow {
id: applicationWindow
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Video {
id: video
}
Column {
id: contents
property bool toggle: false
function toggleLocale() {
if (toggle) {
// set QML locale
applicationWindow.locale = Qt.locale("en_GB")
} else {
// set QML locale
applicationWindow.locale = Qt.locale("ru_RU")
}
toggle = !toggle
}
Text { text: "locale %1".arg(applicationWindow.locale.name) }
Text { text: "playbackState %1".arg(video.playbackState) }
Button { text: "toggle locale"; onClicked: contents.toggleLocale() }
Image {
source: "image://colors/%1/%2".arg(applicationWindow.locale.name).arg(video.playbackState)
}
}
}
#ifndef IMAGEPROVIDER_H
#define IMAGEPROVIDER_H
#include <QDebug>
#include <QImage>
#include <QMediaPlayer>
#include <QQuickImageProvider>
class ImageProvider : public QQuickImageProvider
{
public:
ImageProvider()
: QQuickImageProvider(QQuickImageProvider::Image)
{
}
QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize)
{
Q_UNUSED(requestedSize)
QString localePath = id.split("/").at(0);
QString fileName;
int playbackState = id.split("/").at(1).toInt();
switch (playbackState) {
case QMediaPlayer::StoppedState:
fileName = "logo.png";
break;
case QMediaPlayer::PlayingState:
fileName = "play.png";
break;
case QMediaPlayer::PausedState:
fileName = "pause.png";
break;
default:
fileName = "logo.png";
break;
}
// the following images are located under resources:
// en_GB/logo.png
// en_GB/play.png
// en_GB/pause.png
// ru_RU/logo.png
// ru_RU/play.png
// ru_RU/pause.png
// which is why imagePath starts with : colon
// but they could also be on the filesystem
QString imagePath = QString(":%1/%2").arg(localePath).arg(fileName);
qDebug() << "return Image file:" << imagePath;
QImage image(imagePath);
*size = image.size();
// QImage supports copy, so can return like this
// copy is returned, original on the stack is destroyed
return image;
}
};
#endif // IMAGEPROVIDER_H
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "imageprovider.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.addImageProvider(QLatin1String("colors"), new ImageProvider());
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}