diff --git a/QmlLanguage_ru.qm b/QmlLanguage_ru.qm new file mode 100644 index 0000000..01fa30a Binary files /dev/null and b/QmlLanguage_ru.qm differ diff --git a/QmlLanguage_ru.ts b/QmlLanguage_ru.ts new file mode 100644 index 0000000..def99fb --- /dev/null +++ b/QmlLanguage_ru.ts @@ -0,0 +1,105 @@ + + + + + Tray + + + Open + Открыть + + + + Exit + Выход + + + + Next + Следующий + + + + Priviouse + Предидущий + + + + main + + Add + Добавить + + + Privious + Предыдущий + + + Next + Следующий + + + Play + Воспроизвести + + + Stop + Стоп + + + + %1 - %2 + не требует перевода + + + + + mm:ss + не требует перевода + + + + + MediaPlayer + Медиа плеер + + + + + pause + Пауза + + + + + Now playing + Сейчас играет + + + + + play + воиспроизвести + + + + stop + стоп + + + + privious + предидущий + + + + next + следующий + + + + add + добавить + + + diff --git a/Qt_Project.pro b/Qt_Project.pro new file mode 100644 index 0000000..74577a0 --- /dev/null +++ b/Qt_Project.pro @@ -0,0 +1,50 @@ +QT += quick \ + widgets +QT += widgets +QT += multimedia + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Refer to the documentation for the +# deprecated API to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +SOURCES += \ + main.cpp \ + playlist.cpp \ + qmltranslator.cpp \ + save.cpp \ + tray.cpp + +lupdate_only { + SOURCES += main.qml +} + +RESOURCES += qml.qrc\ + translations.qrc + +# Additional import path used to resolve QML modules in Qt Creator's code model +QML_IMPORT_PATH = + +# Additional import path used to resolve QML modules just for Qt Quick Designer +QML_DESIGNER_IMPORT_PATH = + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +TRANSLATIONS += QmlLanguage_ru.ts + +HEADERS += \ + playlist.h \ + qmltranslator.h \ + save.h \ + tray.h diff --git a/Qt_Project.pro.user b/Qt_Project.pro.user new file mode 100644 index 0000000..220e766 --- /dev/null +++ b/Qt_Project.pro.user @@ -0,0 +1,340 @@ + + + + + + EnvironmentId + {a655b66d-2cc4-4333-853b-2e6fa89f0327} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + true + false + 0 + true + true + 0 + 8 + true + 1 + true + true + true + false + + + + ProjectExplorer.Project.PluginSettings + + + -fno-delayed-template-parsing + + true + Builtin.Questionable + + true + Builtin.DefaultTidyAndClazy + 6 + + + + true + + + + + ProjectExplorer.Project.Target.0 + + Desktop Qt 5.15.0 MinGW 64-bit + Desktop Qt 5.15.0 MinGW 64-bit + qt.qt5.5150.win64_mingw81_kit + 1 + 0 + 0 + + true + 0 + D:\Project\build-Qt_Project-Desktop_Qt_5_15_0_MinGW_64_bit-Debug + D:/Project/build-Qt_Project-Desktop_Qt_5_15_0_MinGW_64_bit-Debug + + + true + QtProjectManager.QMakeBuildStep + + false + + + + true + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Сборка + Сборка + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Очистка + Очистка + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Отладка + Qt4ProjectManager.Qt4BuildConfiguration + 2 + 2 + 2 + + + true + 2 + D:\Project\build-Qt_Project-Desktop_Qt_5_15_0_MinGW_64_bit-Release + D:/Project/build-Qt_Project-Desktop_Qt_5_15_0_MinGW_64_bit-Release + + + true + QtProjectManager.QMakeBuildStep + + false + + + + true + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Сборка + Сборка + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Очистка + Очистка + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Выпуск + Qt4ProjectManager.Qt4BuildConfiguration + 0 + 0 + 2 + + + true + 0 + D:\Project\build-Qt_Project-Desktop_Qt_5_15_0_MinGW_64_bit-Profile + D:/Project/build-Qt_Project-Desktop_Qt_5_15_0_MinGW_64_bit-Profile + + + true + QtProjectManager.QMakeBuildStep + + false + + + + true + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Сборка + Сборка + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Очистка + Очистка + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Профилирование + Qt4ProjectManager.Qt4BuildConfiguration + 0 + 0 + 0 + + 3 + + + 0 + Развёртывание + Развёртывание + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + dwarf + + cpu-cycles + + + 250 + + -e + cpu-cycles + --call-graph + dwarf,4096 + -F + 250 + + -F + true + 4096 + false + false + 1000 + + true + + false + false + false + false + true + 0.01 + 10 + true + kcachegrind + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 2 + + Qt4ProjectManager.Qt4RunConfiguration:D:/Project/Qt_Project/Qt_Project.pro + D:/Project/Qt_Project/Qt_Project.pro + + false + + false + true + true + false + false + true + + + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 22 + + + Version + 22 + + diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..1ba6282 --- /dev/null +++ b/main.cpp @@ -0,0 +1,34 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +int main(int argc, char *argv[]) +{ + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + + QApplication app(argc, argv); + + QQmlApplicationEngine engine; + Tray * systemTray = new Tray(); + QQmlContext * context = engine.rootContext(); + QmlTranslator qmlTranslator; + Save * saves= new Save(); + + qmlTranslator.setTranslation(saves->langugeLoad());; + + context->setContextProperty("systemTray", systemTray); + + context->setContextProperty("Save",saves); + + context->setContextProperty("qmlTranslator", &qmlTranslator); + + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + + return app.exec(); +} diff --git a/main.qml b/main.qml new file mode 100644 index 0000000..37e4cb1 --- /dev/null +++ b/main.qml @@ -0,0 +1,341 @@ +import QtQuick 2.0 +import QtQuick.Window 2.15 +import QtMultimedia 5.15 +import QtQuick.Controls 2.12 +import QtQuick.Dialogs 1.3 + + + +ApplicationWindow { + id:root + visible: true + width: 640 + height: 480 + + property int selectedIndex: -1 + property string letter: qsTr("Now playing") + + + MediaPlayer { + id: player; + + volume: volumeSlider.volume + playlist: Playlist{ + id:playlist + } + + } + + ListView { + id:listview + width:parent.width/2 + height: parent.height + + anchors.left: parent.left + anchors.bottom: parent.bottom + anchors.right: privious.left + + model: playlist; + spacing: 5 + delegate: Rectangle{ + width: listview.width + border.color: (model.index == playlist.currentIndex) ? "green" : "gray" + height: 25 + + Text { + id: songName + text: songUrl.text.replace(/.*\//,"") + } + + Text { + id: songUrl + text: source + visible: false + } + + + MouseArea{ + id:delegatemousearea + anchors.fill:parent + onClicked: { + root.selectedIndex = model.index + } + onDoubleClicked: { + playlist.moveItem(root.selectedIndex,0) + playlist.previous() + player.play() + } + } + } + } + + Label{ + id:label + + anchors.bottom: progresstime.top + anchors.horizontalCenter: songImage.horizontalCenter + + text:player.metaData.title == undefined || player.metaData.author == undefined + ? listview.currentItem.songName.text + :qsTr("%1 - %2").arg(player.metaData.author).arg(player.metaData.title) + } + + Label { + id: positionLabel + + anchors.right: progresstime.right + anchors.bottom: progresstime.top + + + // Текущее + readonly property int minutes: Math.floor(player.position / 60000) + readonly property int seconds: Math.round((player.position % 60000) / 1000) + + // Длительность в минута и секундах + readonly property int durationMinutes: Math.floor(player.duration / 60000) + readonly property int durationSeconds: Math.round((player.duration % 60000) / 1000) + + + + text: { + var formatTime = function(date) { + return Qt.formatTime(date, qsTr("mm:ss")) + } + var currentTime = formatTime(new Date(0, 0, 0, 0, minutes, seconds)) + var durationTime = formatTime(new Date(0, 0, 0, 0, durationMinutes, durationSeconds)) + return currentTime + "/" + durationTime + } + } + + Image { + id: songImage + + height: parent.height-next.height-add.height-label.height-progresstime.height + width: parent.width-listview.width + + anchors.bottom: label.top + anchors.bottomMargin: 10 + anchors.right: changer.left + anchors.left: listview.right + anchors.leftMargin: 10 + + source: (player.metaData.posterUrl !== undefined)? player.metaData.posterUrl :"qrc:/png-transparent-color-drawing-headphones-illustration-musical-note-game-photography-stage.png" + } + + FileDialog{ + id:add_File + + nameFilters: ["Song files(*.mp3)"] + selectMultiple: true + + onAccepted: { + playlist.addItems(fileUrls); + } + } + + + Button{ + id:add + + anchors.bottom: privious.top + anchors.right: parent.horizontalCenter + anchors.bottomMargin: 5 + + + onClicked: { + add_File.open() + } + } + Button{ + id:privious + + anchors.right: parent.horizontalCenter + anchors.bottom: parent.bottom + + + onClicked: { + playlist.previous(); + } + } + + Button{ + id:next + + anchors.right: songImage.right + anchors.bottom: parent.bottom + anchors.rightMargin: 5 + + onClicked: { + playlist.next(); + } + } + + Button{ + id:play + + anchors.left: privious.right + anchors.leftMargin: 5 + anchors.bottom: parent.bottom + + onClicked: { + if(player.playbackState===MediaPlayer.PlayingState){ + player.pause() + play.text = qsTr("play") + } + else{ + play.text = qsTr("pause") + player.play() + } + } + } + + Button{ + id:stop + + anchors.right: next.left + anchors.rightMargin: 5 + anchors.bottom: parent.bottom + anchors.left: play.right + anchors.leftMargin: 5 + + onClicked: { + player.stop(); + } + } + + Button { + id: changer + + property string language: Save.langugeLoad() + + icon.source: language=="en_US" ? "qrc:/united-states.png" + : "qrc:/russia.png" + icon.color: "transparent" + + anchors.top: parent.top + anchors.right: parent.right + anchors.margins: 5 + + // При изменении текста, инициализируем установку перевода через С++ слой + onClicked: { + if(language=="en_US"){ + language="ru_RU" + qmlTranslator.setTranslation(language) + } + else{ + language="en_US" + qmlTranslator.setTranslation(language) + } + } + } + + Slider { + id: volumeSlider + + anchors.left: add.right + anchors.bottom: add.bottom + anchors.leftMargin: 5 + + property real volume: QtMultimedia.convertVolume(volumeSlider.value, + QtMultimedia.LogarithmicVolumeScale, + QtMultimedia.LinearVolumeScale) + } + + Slider{ + id:progresstime + + anchors.bottom: volumeSlider.top + anchors.left: add.left + anchors.right: songImage.right + + from: 0 + to: player.duration + stepSize: 1000 + + onPressedChanged: { + if (!pressed && player.seekable) + player.seek(progresstime.value) + } + + Connections{ + target: player + onPositionChanged:{ + progresstime.value=player.position + if(progresstime.value==0 && root.visibility == Window.Hidden && (player.metaData.author == undefined || player.title == undefined)) + systemTray.messege(letter,listview.currentItem.songName.text) + else if(progresstime.value==0 && root.visibility == Window.Hidden) + systemTray.messege(letter,player.metaData.title + " " +player.metaData.author) + } + } + + } + + property bool quit: false + + Connections { + target: systemTray + onSignalShow: { + root.show(); + } + + onSignalQuit: { + quit = true + close(); + } + + onSignalNext:{ + playlist.next(); + } + + onSignalPrevious:{ + playlist.previous() + } + + onSignalIconActivated: { + if(root.visibility == Window.Hidden) { + root.show() + } else { + root.hide() + } + } + } + onClosing: { + if(quit == false){ + close.accepted = false + root.hide() + } else { + Save.languageSave(changer.language) + Qt.quit() + } + } + + Connections { + target: qmlTranslator // был зарегистрирован в main.cpp + onLanguageChanged: { // при получении сигнала изменения языка + retranslateUi() // инициализируем перевод интерфейса + } + } + + // Функция перевода интерфейса + function retranslateUi() { + root.title= qsTr("MediaPlayer") + if(player.playbackState===MediaPlayer.PlayingState){ + play.text = qsTr("pause") + } + else{ + play.text = qsTr("play") + } + stop.text = qsTr("stop") + privious.text=qsTr("privious") + next.text=qsTr("next") + add.text=qsTr("add") + systemTray.setTranslation() + root.letter=qsTr("Now playing") + } + + Component.onCompleted: { + retranslateUi(); + } + +} + diff --git a/play-button.png b/play-button.png new file mode 100644 index 0000000..7238aba Binary files /dev/null and b/play-button.png differ diff --git a/png-transparent-color-drawing-headphones-illustration-musical-note-game-photography-stage.png b/png-transparent-color-drawing-headphones-illustration-musical-note-game-photography-stage.png new file mode 100644 index 0000000..c0572eb Binary files /dev/null and b/png-transparent-color-drawing-headphones-illustration-musical-note-game-photography-stage.png differ diff --git a/qml.qrc b/qml.qrc new file mode 100644 index 0000000..3236c86 --- /dev/null +++ b/qml.qrc @@ -0,0 +1,9 @@ + + + main.qml + play-button.png + png-transparent-color-drawing-headphones-illustration-musical-note-game-photography-stage.png + russia.png + united-states.png + + diff --git a/qmltranslator.cpp b/qmltranslator.cpp new file mode 100644 index 0000000..41bc1a7 --- /dev/null +++ b/qmltranslator.cpp @@ -0,0 +1,15 @@ +#include "qmltranslator.h" +#include +#include + +QmlTranslator::QmlTranslator(QObject *parent) : QObject(parent) +{ + +} + +void QmlTranslator::setTranslation(QString translation) +{ + m_translator.load(":/QmlLanguage_" + translation, "."); // Загружаем перевод + qApp->installTranslator(&m_translator); // Устанавливаем его в приложение + emit languageChanged(); // Сигнализируем об изменении текущего перевода +} diff --git a/qmltranslator.h b/qmltranslator.h new file mode 100644 index 0000000..1b10132 --- /dev/null +++ b/qmltranslator.h @@ -0,0 +1,29 @@ +#ifndef QMLTRANSLATOR_H +#define QMLTRANSLATOR_H + +#include +#include +#include +#include + + +class QmlTranslator : public QObject +{ + Q_OBJECT + +public: + explicit QmlTranslator(QObject *parent = 0); + +signals: + // Сигнал об изменении текущего языка для изменения перевода интерфейса + void languageChanged(); + +public: + // Метод установки перевода, который будет доступен в QML + Q_INVOKABLE void setTranslation(QString translation); + +private: + QTranslator m_translator; +}; + +#endif // QMLTRANSLATOR_H diff --git a/russia.png b/russia.png new file mode 100644 index 0000000..2f19fc3 Binary files /dev/null and b/russia.png differ diff --git a/save.cpp b/save.cpp new file mode 100644 index 0000000..6298db7 --- /dev/null +++ b/save.cpp @@ -0,0 +1,19 @@ +#include "save.h" + +Save::Save(QObject *parent) : QObject(parent) +{ + +} + +void Save::languageSave(QString languge) +{ + saveLanguage =new QSettings("settings.conf",QSettings::IniFormat); + saveLanguage->setValue("Language/translations",languge); + saveLanguage->sync(); +} + +QString Save::langugeLoad() +{ + loadLanguage = new QSettings("settings.conf",QSettings::IniFormat); + return loadLanguage->value("Language/translations","en_US").toString(); +} diff --git a/save.h b/save.h new file mode 100644 index 0000000..9704124 --- /dev/null +++ b/save.h @@ -0,0 +1,24 @@ +#ifndef SAVE_H +#define SAVE_H + + +#include +#include + +class Save : public QObject +{ + Q_OBJECT +public: + explicit Save(QObject *parent = 0); + +public slots: + void languageSave(QString languge); + QString langugeLoad(); + +private: + QSettings * saveLanguage; + QSettings * loadLanguage; + +}; + +#endif // SAVE_H diff --git a/translations.qrc b/translations.qrc new file mode 100644 index 0000000..91a6172 --- /dev/null +++ b/translations.qrc @@ -0,0 +1,5 @@ + + + QmlLanguage_ru.qm + + diff --git a/tray.cpp b/tray.cpp new file mode 100644 index 0000000..3969f82 --- /dev/null +++ b/tray.cpp @@ -0,0 +1,69 @@ +#include "tray.h" +#include +#include +#include +#include + + +Tray::Tray(QObject *parent) : QObject(parent) +{ + + // Создаём контекстное меню с двумя пунктами + trayIconMenu = new QMenu(); + + /* Инициализируем иконку трея, устанавливаем иконку, + * а также задаем всплывающую подсказку + * */ + trayIcon = new QSystemTrayIcon(); + setTranslation(); + trayIcon->setIcon(QIcon(":/play-button.png")); + trayIcon->show(); + trayIcon->setToolTip("MediaPlayer"); + + /* Также подключаем сигнал нажатия на иконку к обработчику + * данного нажатия + * */ + connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), + this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason))); +} + +/* Метод, который обрабатывает нажатие на иконку приложения в трее + * */ +void Tray::iconActivated(QSystemTrayIcon::ActivationReason reason) +{ + switch (reason){ + case QSystemTrayIcon::Trigger: + // В случае сигнала нажатия на иконку трея вызываем сигнал в QML слой + emit signalIconActivated(); + break; + default: + break; + } +} + +void Tray::hideIconTray() +{ + trayIcon->hide(); +} +void Tray::messege(QString title,QString messege){ + trayIcon->showMessage(title,messege,QSystemTrayIcon::Information,500); +} +void Tray::setTranslation(){ + trayIconMenu->clear(); + + QAction * viewWindow = new QAction(trUtf8("Open"), this); + QAction * quitAction = new QAction(trUtf8("Exit"), this); + QAction * nextSong = new QAction(trUtf8("Next"),this); + QAction * previousSong = new QAction(trUtf8("Priviouse"),this); + + connect(viewWindow, &QAction::triggered, this, &Tray::signalShow); + connect(quitAction, &QAction::triggered, this, &Tray::signalQuit); + connect(nextSong, &QAction::triggered,this, &Tray::signalNext); + connect(previousSong, &QAction::triggered,this, &Tray::signalPrevious); + + trayIconMenu->addAction(viewWindow); + trayIconMenu->addAction(nextSong); + trayIconMenu->addAction(previousSong); + trayIconMenu->addAction(quitAction); + trayIcon->setContextMenu(trayIconMenu); +} diff --git a/tray.h b/tray.h new file mode 100644 index 0000000..bf2e315 --- /dev/null +++ b/tray.h @@ -0,0 +1,41 @@ +#ifndef TRAY_H +#define TRAY_H + +#include +#include +#include +#include +#include + +class Tray : public QObject +{ + Q_OBJECT +public: + explicit Tray(QObject *parent = 0); + + // Сигналы от системного трея +signals: + void signalIconActivated(); + void signalShow(); + void signalQuit(); + void signalNext(); + void signalPrevious(); + +private slots: + /* Слот, который будет принимать сигнал от события + * нажатия на иконку приложения в трее + */ + void iconActivated(QSystemTrayIcon::ActivationReason reason); + +public slots: + void hideIconTray(); + void messege(QString title,QString messege); + void setTranslation(); + +private: + /* Объявляем объект будущей иконки приложения для трея */ + QSystemTrayIcon * trayIcon; + QMenu * trayIconMenu; +}; + +#endif // TRAY_H diff --git a/united-states.png b/united-states.png new file mode 100644 index 0000000..36b29d4 Binary files /dev/null and b/united-states.png differ