Reputation: 16711
The interface of simple video player, which consists of Video
and Image
items, blended by Blend
from QtGraphicalEffects
. Image
has associated MouseArea
and translucent graphical buttons with "hardcoded" text in English. Default images provided by QQuickImageProvider
from following locations:
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)
}
}
}
}
There is also corresponding images in +ru_RU/
subfolder into the resources.
C++ QObject
singletone from global context may have signal void languageChanged(QLocale locale);
, which emitted, when all the QTranslator
s are replaced in QCoreApplication::removeTranslator/installTranslator
for each of loaded resource.
// 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);
}
I want to change locations of images on signal which emitted along with locale changing. Is it possible to change selected files at runtime (i.e. w/o recreating of the parent Item
, which contains VideoPlayer
)?
Connected question: what is affected by file selectors first: QImageReader
's filepaths used internally into QQuickImageProvider
or source:
of Quick's Image
? The latter are derived from the first. Or maybe both?
Upvotes: 1
Views: 1664
Reputation: 3030
The approach is to assign your locale to a QML property, and then use that and the video playbackState enum property in a binding expression to the source
property of the Image
item.
When the button is clicked, the QML locale property is changed, and the binding expression on the source
of the Image
item is automatically re-evaluated, causing the QQuickImageProvider to serve a new image.
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();
}
Upvotes: 1