launcher project v 0.1

This commit is contained in:
farkadi 2025-01-08 15:18:16 +07:00
commit 86acc9d68f
32 changed files with 2757 additions and 0 deletions

64
README.md Normal file
View File

@ -0,0 +1,64 @@
![Screenshot](image.png)
# WoW Launcher
Современный лаунчер для World of Warcraft 3.3.5a, написанный на Python и QML.
## Возможности
- Современный интерфейс в стиле World of Warcraft
- Загрузка и проверка файлов клиента
- Поддержка Windows и Linux (через Wine/Lutris/Proton/PortProton)
- Автоматическое обновление
- Слайд-шоу с новостями
- Мониторинг состояния серверов
- Система уведомлений
- Настраиваемые параметры
- Загрузка файлов сегментами
- Сворачивание в трей
- Возможность запускать игру через Wine, Lutris, Proton, PortProton
## Требования
- Python 3.8+
- PyQt5
- Requests
## Установка
1. Клонируйте репозиторий:
```
git clone https://github.com/ваш-username/wow-launcher.git
cd wow-launcher
```
2. Установите зависимости:
```
pip install -r requirements.txt
```
3. Запустите лаунчер:
```
python backup3.py
```
## Настройка
1. При первом запуске выберите папку для установки игры
2. Настройте параметры в меню настроек
3. Для Linux пользователей: выберите предпочитаемый эмулятор Windows
## Разработка
Проект использует:
- Python для бэкенда
- QML для интерфейса
- Qt Quick Controls 2 для компонентов
- Material Design для стилизации
## Лицензия
MIT License
## Авторы
JumpyLion87

1
README.txt Normal file
View File

@ -0,0 +1 @@

180
clien.json Normal file
View File

@ -0,0 +1,180 @@
{
"files": {
"Data/ruRU/Interface/Cinematics/Logo_1024.avi": {
"size": 6518380,
"hash": "8ca8b2ef476be8d69a678ac34f75498f3c5cc02b5f618d467b9c78e6edae815e"
},
"Data/ruRU/Interface/Cinematics/Logo_800.avi": {
"size": 4967302,
"hash": "9f3c345a99073f7f06adb1b9bf37c2ffc7e37773d07a4cc72a0cd163db07b765"
},
"Data/ruRU/Interface/Cinematics/WOW_FotLK_1024.avi": {
"size": 66007136,
"hash": "1afe738ad4ad1203f357eb15b1a15926e54ffbd3ad9b7b5ca9fac73dad058292"
},
"Data/ruRU/Interface/Cinematics/WOW_FotLK_800.avi": {
"size": 57244820,
"hash": "52cb90f10863c909d613f84c99f0d2da5139793c2998f2b2905c49395b489821"
},
"Data/ruRU/Interface/Cinematics/WOW_Intro_1024.avi": {
"size": 48319392,
"hash": "697fad32b005def2bb6908fb3a77f43be134d661fd03ffa29d2c6739ec5cd7b7"
},
"Data/ruRU/Interface/Cinematics/WOW_Intro_800.avi": {
"size": 41883066,
"hash": "0d62fcd314daf93add75510f644e04a4192effcd323db0c791df01609bf3e77b"
},
"Data/ruRU/Interface/Cinematics/WOW_Intro_BC_1024.avi": {
"size": 47575820,
"hash": "0277fba0da2a8d0ea61121996390ab3d718729c7802c31b9d0f9447233b0dda0"
},
"Data/ruRU/Interface/Cinematics/WOW_Intro_BC_800.avi": {
"size": 41261394,
"hash": "ed5511a783734bf214cc084fde253b530b868336f774b61ec090c8891627b880"
},
"Data/ruRU/Interface/Cinematics/WOW_Intro_LK_1024.avi": {
"size": 56177056,
"hash": "be3dc55b635da30ffddc84898a241b1551f1e32b1a6a0c97768afdc4a078a51d"
},
"Data/ruRU/Interface/Cinematics/WOW_Intro_LK_800.avi": {
"size": 48820376,
"hash": "fcd5e4576be7e7d2e97a436f13e37734e72adfa3457d1fbbcc9e6173aea7f7e5"
},
"Data/ruRU/Interface/Cinematics/WOW_Wrathgate_1024.avi": {
"size": 75685834,
"hash": "4b40199be93942548f77fb3cfeaf001738bccee0211940c8ed4ff6f0fb068897"
},
"Data/ruRU/Interface/Cinematics/WOW_Wrathgate_800.avi": {
"size": 65683896,
"hash": "acececbffff5e1b2e23f16259fe662df3a02823d4890d3ac3a4d96b3a401c729"
},
"Data/ruRU/backup-ruRU.MPQ": {
"size": 158243976,
"hash": "b2acbf9da5adb20926b42194fcc026bdfcf0a5074d5e058fa24c6da57593a8f0"
},
"Data/ruRU/base-ruRU.MPQ": {
"size": 22975058,
"hash": "f361e6706aab57cce50e8a7ba8a08f312ba3c328a5219cd6f72db1894d4c1148"
},
"Data/ruRU/eula.html": {
"size": 36274,
"hash": "e784b78eb85d4231dec97870e6cfd585d0b2563b1af350ba34ab75545b478b3f"
},
"Data/ruRU/expansion-locale-ruRU.MPQ": {
"size": 16919739,
"hash": "4a565f808c1c4b413dcc1bbfa9980ca700500f220506704ecbb4734883d6ee8d"
},
"Data/ruRU/expansion-speech-ruRU.MPQ": {
"size": 269445963,
"hash": "69a5057a064fc22efa008acc03faf78fa1abc8b7f2320367e53569b7b2dea3c9"
},
"Data/ruRU/lichking-locale-ruRU.MPQ": {
"size": 12354378,
"hash": "89b1eba015927e321683a3867be1b8a76e593c438527eb42f4934d03484f1b04"
},
"Data/ruRU/lichking-speech-ruRU.MPQ": {
"size": 6772364,
"hash": "99afb35d5f092a19b760815bb0a21b8eb4b74d7e9361498dc13d4a65cc951b59"
},
"Data/ruRU/locale-ruRU.MPQ": {
"size": 190129109,
"hash": "dd587f0a673924d3320c30812d90476fc72a240e30056d0656c8c6c5e32dde1f"
},
"Data/ruRU/patch-ruRU-2.MPQ": {
"size": 270846455,
"hash": "25365c0319d186e7c0a09b8f0b63c1de765f4387bd3d532709fe163170c439e3"
},
"Data/ruRU/patch-ruRU-3.MPQ": {
"size": 109883630,
"hash": "792f87603eb77000a23c56f85c6250796e4f4032d0cad671a70c0fa300a2a0c3"
},
"Data/ruRU/patch-ruRU.MPQ": {
"size": 613796676,
"hash": "af99e71afc6eec6f6087a8a43948210a30c15283e71dc0c49ea0a6543bee69ff"
},
"Data/ruRU/patch-ruRU-8.MPQ": {
"size": 115949,
"hash": "3dcc6776a3a1995b833e216c1517522ed5787ec9389c4ebbe206b6d4f7df1ce2"
},
"Data/ruRU/speech-ruRU.MPQ": {
"size": 445791052,
"hash": "a76376c43b90633648269fe94e2b89454b00bc096f6acc11302492fd2102727e"
},
"Data/ruRU/termination.html": {
"size": 4882,
"hash": "2e2dd7c567c96bb0b3d2aa59ca2822cbc4dedfa110eaddbbbe9294076e987c04"
},
"Data/ruRU/tos.html": {
"size": 96614,
"hash": "a7bdc9a6faa1098ed3a844a1a79ed85d3dd0e2a34dcd10cd73e2da5305dc7f45"
},
"Data/common-2.MPQ": {
"size": 1756781838,
"hash": "dc0f8fe0607754de306eba74e9972ab6d91eb467076f34b26407ace3446f3f57"
},
"Data/common.MPQ": {
"size": 2856083241,
"hash": "63bac342322bbc1f88b3344b1a73e67a84ed527fbbe9c728e2528e8660346399"
},
"Data/expansion.MPQ": {
"size": 1899508519,
"hash": "edf04e46a28fee2727d29a61c2f866e4b3dce41468ebd41f34c115fae25a5abf"
},
"Data/lichking.MPQ": {
"size": 2553955175,
"hash": "7be69de4ae1328026cf1de021fde70666367c255573e283302089658cdd56cfb"
},
"Data/patch-2.MPQ": {
"size": 1401729059,
"hash": "c8b78bb75bcf5773e9ae99e11bdfcabc2a37aed3da947367d8af808d4d3c23c6"
},
"Data/patch-3.MPQ": {
"size": 605089137,
"hash": "56dbbfc8f9ce7182ca88538d75284e738020138a7ca4217d72d8168865510dd3"
},
"Data/patch.MPQ": {
"size": 4004713057,
"hash": "92b4a94a6c7a23c0b9fd88c47823e41792879a7ee1f70c2349a255153ca25d54"
},
"Battle.net.dll": {
"size": 15588224,
"hash": "88116951b04723cbb1073a1a0ff72dfdc40367f6649dffd08c602caa3ef1141f"
},
"DivxDecoder.dll": {
"size": 413696,
"hash": "ed34d37b575c91a56704218eb9f6abbefda8b7de0e2ed44c96191abd0f9915a5"
},
"Microsoft.VC80.CRT.manifest": {
"size": 1870,
"hash": "a50941352cb9d8f7ba6fbf7db5c8af95fb5ab76fc5d60cfd0984e558678908cc"
},
"Scan.dll": {
"size": 51972,
"hash": "22bafdab4e842aa675f089672a366a164c735bd63026eea8704778085200bf3f"
},
"Wow.exe": {
"size": 7704216,
"hash": "28287cd94e6ab2d6e68865f49314b456d2750dcb5d0b8ecca12eeddf72b7bf80"
},
"WowError.exe": {
"size": 350360,
"hash": "964eb9ed647c4614f3a3ecf977955750223ecbc8ff0a3bb4078e4dff779244f8"
},
"dbghelp.dll": {
"size": 1039728,
"hash": "3f9ad6986eb1f3a09eb0b5cad888ba656f9148b3339399076f2541b3bc59213c"
},
"ijl15.dll": {
"size": 372736,
"hash": "334aa12f7dee453d1c6cb1b661a3bb3494d3e4cc9c2ff3f9002064c78404e43a"
},
"msvcr80.dll": {
"size": 632656,
"hash": "9382aaed2db19cd75a70e38964f06c63f19f63c9dfb5a33b0c2d445bb41b6e46"
},
"unicows.dll": {
"size": 245408,
"hash": "22f23cc65698741184ec34f46e6f69717644e0b5aabf5d5bd015101f2d72e56e"
}
}
}

313
main.qml Normal file
View File

@ -0,0 +1,313 @@
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.Controls.Material 2.15
import QtQuick.Layouts 1.15
import "./qml/Theme" as Theme
import "./qml/components" as Components
ApplicationWindow {
id: mainWindow
visible: true
width: 1010
height: 650
minimumWidth: 1010
minimumHeight: 650
maximumWidth: 1010
maximumHeight: 650
title: "World of Warcraft 3.3.5a Launcher"
// Добавляем фоновое изображение
background: Rectangle {
color: Theme.Theme.backgroundColor
Image {
anchors.fill: parent
source: "qml/images/background.jpg" // Путь к изображению
fillMode: Image.PreserveAspectCrop
opacity: 0.3
// Добавляем затемнение
Rectangle {
anchors.fill: parent
color: "#000000"
opacity: 0.6
}
}
}
Material.theme: Material.Dark
Material.accent: Theme.Theme.accentColor
Material.background: Theme.Theme.backgroundColor
ColumnLayout {
anchors.fill: parent
spacing: 0
// Основной контент
RowLayout {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.margins: Theme.Theme.margin
spacing: Theme.Theme.spacing
// Левая панель
Components.Section {
Layout.preferredWidth: 300
Layout.minimumWidth: 300
Layout.fillHeight: true
// Заголовок
Components.Header {
Layout.fillWidth: true
Layout.minimumHeight: 70
}
// Информационная панель
Components.InfoPanel {
Layout.fillWidth: true
Layout.minimumHeight: 150
serverInfo: "x2 WotLK"
requirements: "• OS: Windows 7/8/10/11\n• CPU: 2.4 GHz\n• RAM: 2 GB\n• HDD: 15 GB"
}
// Растягивающийся элемент
Item {
Layout.fillHeight: true
}
// Статус текст
Label {
Layout.fillWidth: true
Layout.minimumHeight: 50
text: launcher ? launcher.statusText : "Пожалуйста, выберите папку с игрой"
color: Theme.Theme.primaryText
font.pixelSize: Theme.Theme.normalSize
wrapMode: Text.WordWrap
}
Rectangle {
Layout.fillWidth: true
height: 1
color: Qt.darker(Theme.Theme.borderColor, 1.2)
Layout.margins: Theme.Theme.spacing
}
// Кнопки
Components.WoWButton {
Layout.fillWidth: true
Layout.minimumHeight: Theme.Theme.buttonHeight
text: "Выбрать папку с игрой"
visible: launcher ? !launcher.gamePath : true
onClicked: launcher && launcher.selectGamePath()
tooltip: "Выберите папку, где будет установлена игра"
}
Components.WoWButton {
Layout.fillWidth: true
Layout.minimumHeight: Theme.Theme.buttonHeight
text: launcher && launcher.isDownloading ? "Остановить загрузку" : "Скачать клиент"
onClicked: launcher && launcher.startDownload()
tooltip: launcher && launcher.isDownloading ?
"Остановить текущую загрузку" :
"Загрузить клиент World of Warcraft"
}
// Добавляем отступ после кнопок
Item {
Layout.fillWidth: true
Layout.minimumHeight: Theme.Theme.spacing * 2 // Отступ снизу
}
}
// Правая панель
Components.Section {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.minimumWidth: 400
// Слайд-шоу
Components.SlideShow {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.minimumHeight: 300
imageUrls: [
"qml/images/slide1.jpg",
"qml/images/slide2.jpg",
"qml/images/slide3.jpg",
"qml/images/slide4.jpg"
]
}
Rectangle {
Layout.fillWidth: true
height: 1
color: Qt.darker(Theme.Theme.borderColor, 1.2)
Layout.margins: Theme.Theme.spacing
}
// Нижняя часть с прогрессом и кнопкой
RowLayout {
Layout.fillWidth: true
Layout.margins: Theme.Theme.spacing
spacing: Theme.Theme.spacing
// Прогресс-бар и имя файла
ColumnLayout {
Layout.fillWidth: true
spacing: Theme.Theme.spacing / 2
Label {
Layout.fillWidth: true
text: launcher ? launcher.currentFileName : ""
color: Theme.Theme.primaryText
font.pixelSize: Theme.Theme.smallSize
visible: text !== ""
}
Components.ProgressSection {
Layout.fillWidth: true
Layout.minimumHeight: 40
Layout.leftMargin: 1
Layout.rightMargin: 1
Layout.topMargin: 1
Layout.bottomMargin: 1
value: launcher ? launcher.downloadProgress : 0
text: {
if (launcher) {
if (launcher.downloadSpeed && launcher.downloadSizeInfo) {
return launcher.downloadSpeed + " / " + launcher.downloadSizeInfo
}
return launcher.downloadSpeed
}
return ""
}
}
}
// Кнопка запуска
Components.WoWButton {
Layout.alignment: Qt.AlignBottom
Layout.minimumWidth: 120
Layout.minimumHeight: Theme.Theme.buttonHeight
text: "ИГРАТЬ"
enabled: launcher ? launcher.canPlay : false
onClicked: launcher && launcher.launchGame()
tooltip: enabled ?
"Запустить World of Warcraft" :
"Сначала установите игру"
}
}
}
}
// Статус бар
Rectangle {
Layout.fillWidth: true
Layout.minimumHeight: 30
height: 30
color: Qt.rgba(Theme.Theme.frameColor.r, Theme.Theme.frameColor.g, Theme.Theme.frameColor.b, 0.35)
border.color: Qt.rgba(Theme.Theme.borderColor.r, Theme.Theme.borderColor.g, Theme.Theme.borderColor.b, 0.4)
RowLayout {
anchors {
fill: parent
leftMargin: Theme.Theme.margin
rightMargin: Theme.Theme.margin
topMargin: Theme.Theme.margin / 2
bottomMargin: Theme.Theme.margin / 2
}
spacing: Theme.Theme.spacing
// Статус сервера
Label {
Layout.minimumWidth: 100
text: launcher ? launcher.serverStatus : "⚫ Offline"
color: launcher && launcher.isServerOnline ? Theme.Theme.primaryText : Theme.Theme.disabledText
font.bold: true
ToolTip.visible: serverMouseArea.containsMouse
ToolTip.text: launcher && launcher.isServerOnline ?
"Сервер доступен" :
"Сервер недоступен"
MouseArea {
id: serverMouseArea
anchors.fill: parent
hoverEnabled: true
}
}
// Растягивающийся элемент
Item { Layout.fillWidth: true }
// Версия
Label {
Layout.minimumWidth: 80
text: "Версия: " + (launcher ? launcher.version : "3.3.5")
color: Theme.Theme.secondaryText
}
// Кнопка настроек
ToolButton {
Layout.preferredWidth: 30
Layout.preferredHeight: 30
icon.source: "qml/components/qml/images/icons/settings.png"
icon.color: Theme.Theme.secondaryText
icon.width: 16
icon.height: 16
ToolTip.visible: hovered
ToolTip.text: "Настройки"
onClicked: settingsDialog.open()
}
}
}
}
Components.Notification {
id: notification
anchors {
horizontalCenter: parent.horizontalCenter
bottom: parent.bottom
bottomMargin: Theme.Theme.margin * 2
}
z: 999
}
// Функция для показа уведомлений
function showNotification(message, type) {
notification.type = type || "info"
notification.text = message
notification.show()
}
Components.SettingsDialog {
id: settingsDialog
}
Components.AboutDialog {
id: aboutDialog
}
Components.ContextMenu {
id: contextMenu
}
// Добавить MouseArea для всего окна
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.RightButton
onClicked: {
if (mouse.button === Qt.RightButton)
contextMenu.popup()
}
}
// Добавляем обработку закрытия окна
onClosing: function(close) {
if (launcher.settings.closeToTray) {
close.accepted = false
launcher.minimizeToTray()
}
}
}

41
qml/Theme/Theme.qml Normal file
View File

@ -0,0 +1,41 @@
pragma Singleton
import QtQuick 2.15
QtObject {
// Цвета
readonly property color backgroundColor: "#0A0A0A"
readonly property color frameColor: "#1A1A1A"
readonly property color borderColor: "#393939"
readonly property color primaryText: "#FFB100"
readonly property color secondaryText: "#CD8500"
readonly property color disabledText: "#4A4A4A"
readonly property color accentColor: "#4B0082"
// Размеры
readonly property int margin: 10
readonly property int spacing: 15
readonly property int radius: 8
readonly property int buttonHeight: 45
// Шрифты
readonly property int titleSize: 24
readonly property int subtitleSize: 16
readonly property int normalSize: 14
readonly property int smallSize: 12
// Градиенты
readonly property var buttonGradient: Gradient {
GradientStop { position: 0.0; color: "#1B3859" }
GradientStop { position: 1.0; color: "#0A1B2A" }
}
readonly property var buttonHoverGradient: Gradient {
GradientStop { position: 0.0; color: "#2B4869" }
GradientStop { position: 1.0; color: "#1A2B3A" }
}
readonly property var buttonPressedGradient: Gradient {
GradientStop { position: 0.0; color: "#0A1B2A" }
GradientStop { position: 1.0; color: "#1B3859" }
}
}

2
qml/Theme/qmldir Normal file
View File

@ -0,0 +1,2 @@
module Theme
singleton Theme 1.0 Theme.qml

View File

@ -0,0 +1,61 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Controls.Material 2.15
import QtQuick.Layouts 1.15
import "../Theme" as Theme
Dialog {
id: root
title: "О программе"
modal: true
x: (parent.width - width) / 2
y: (parent.height - height) / 2
width: 400
height: 300
Material.background: Theme.Theme.frameColor
ColumnLayout {
anchors.fill: parent
spacing: Theme.Theme.spacing
Image {
source: "qml/images/logo.png"
Layout.preferredWidth: 200
Layout.preferredHeight: 100
Layout.alignment: Qt.AlignHCenter
fillMode: Image.PreserveAspectFit
}
Label {
text: "World of Warcraft 3.3.5a Launcher"
font.pixelSize: Theme.Theme.titleSize
font.bold: true
color: Theme.Theme.primaryText
Layout.alignment: Qt.AlignHCenter
}
Label {
text: "Версия: " + (launcher ? launcher.version : "3.3.5")
color: Theme.Theme.secondaryText
Layout.alignment: Qt.AlignHCenter
}
Item { Layout.fillHeight: true }
Label {
text: "© 2024 Your Server Name"
color: Theme.Theme.secondaryText
Layout.alignment: Qt.AlignHCenter
}
}
footer: DialogButtonBox {
Button {
text: "Закрыть"
DialogButtonBox.buttonRole: DialogButtonBox.RejectRole
onClicked: root.reject()
}
}
}

View File

@ -0,0 +1,37 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import "../Theme" as Theme
Menu {
id: contextMenu
MenuItem {
text: "Открыть папку с игрой"
enabled: launcher && launcher.gamePath
onTriggered: launcher.openGameFolder()
}
MenuItem {
text: "Проверить файлы"
enabled: launcher && launcher.gamePath && !launcher.isDownloading
onTriggered: launcher.verifyFiles()
}
MenuItem {
text: "Восстановить клиент"
enabled: launcher && launcher.gamePath && !launcher.isDownloading
onTriggered: launcher.repairClient()
}
MenuSeparator { }
MenuItem {
text: "Настройки"
onTriggered: settingsDialog.open()
}
MenuItem {
text: "О программе"
onTriggered: aboutDialog.open()
}
}

122
qml/components/Header.qml Normal file
View File

@ -0,0 +1,122 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Controls.Material 2.15
import QtQuick.Layouts 1.15
import "../Theme" as Theme
Rectangle {
id: root
Layout.fillWidth: true
Layout.minimumHeight: 70
color: "transparent"
// Анимированный фон логотипа
Rectangle {
id: logoBackground
anchors.left: parent.left
width: logo.width
height: parent.height
color: "transparent"
Rectangle {
anchors.fill: parent
gradient: Gradient {
GradientStop { position: 0.0; color: "#00152536" }
GradientStop { position: 0.5; color: "#20152536" }
GradientStop { position: 1.0; color: "#00152536" }
}
SequentialAnimation on opacity {
loops: Animation.Infinite
NumberAnimation { to: 0.3; duration: 2000; easing.type: Easing.InOutQuad }
NumberAnimation { to: 1.0; duration: 2000; easing.type: Easing.InOutQuad }
}
}
}
Image {
id: logo
source: "qml/images/logo.png"
width: parent.height * 1.5
height: parent.height
fillMode: Image.PreserveAspectFit
// Эффект свечения для логотипа
Rectangle {
anchors.fill: parent
color: "#3A5570"
opacity: 0
SequentialAnimation on opacity {
loops: Animation.Infinite
NumberAnimation { to: 0.2; duration: 2000; easing.type: Easing.InOutQuad }
NumberAnimation { to: 0; duration: 2000; easing.type: Easing.InOutQuad }
}
}
}
// Статистика сервера
Row {
anchors {
right: parent.right
verticalCenter: parent.verticalCenter
rightMargin: Theme.Theme.spacing
}
spacing: Theme.Theme.spacing * 2
// Онлайн
StatsBox {
label: "ОНЛАЙН"
value: "1234"
// Анимация при изменении значения
Behavior on value {
NumberAnimation {
duration: 500
easing.type: Easing.OutBack
}
}
}
// Рейты
StatsBox {
label: "РЕЙТЫ"
value: "x2"
}
}
// Компонент для отображения статистики
component StatsBox: Column {
property string label: ""
property string value: ""
spacing: 2
Label {
text: label
color: Theme.Theme.secondaryText
font.pixelSize: Theme.Theme.smallSize
font.bold: true
anchors.horizontalCenter: parent.horizontalCenter
}
Label {
text: value
color: "#00AAFF"
font.pixelSize: Theme.Theme.titleSize
font.bold: true
anchors.horizontalCenter: parent.horizontalCenter
// Подсветка при наведении
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: parent.color = "#40AAFF"
onExited: parent.color = "#00AAFF"
}
Behavior on color {
ColorAnimation { duration: 150 }
}
}
}
}

View File

@ -0,0 +1,127 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Controls.Material 2.15
import QtQuick.Layouts 1.15
import "../Theme" as Theme
Pane {
id: root
Layout.fillWidth: true
padding: Theme.Theme.margin
Material.elevation: 6
Material.background: Qt.darker(Theme.Theme.frameColor, 1.1)
property alias serverInfo: serverLabel.text
property alias requirements: reqLabel.text
background: Rectangle {
color: Qt.darker(Theme.Theme.frameColor, 1.1)
radius: Theme.Theme.radius / 2
border.color: Qt.darker(Theme.Theme.borderColor, 1.2)
border.width: 1
Rectangle {
anchors.fill: parent
radius: parent.radius
gradient: Gradient {
GradientStop { position: 0.0; color: "#20FFFFFF" }
GradientStop { position: 1.0; color: "#00FFFFFF" }
}
}
}
ColumnLayout {
width: parent.width
spacing: Theme.Theme.spacing * 1.5
// Секция сервера
RowLayout {
Layout.fillWidth: true
spacing: Theme.Theme.spacing
Image {
source: "qml/images/icons/server.png"
sourceSize: Qt.size(24, 24)
Layout.alignment: Qt.AlignTop
opacity: 0.8
}
ColumnLayout {
Layout.fillWidth: true
spacing: Theme.Theme.spacing / 2
Label {
text: "ИНФОРМАЦИЯ О СЕРВЕРЕ"
color: Theme.Theme.secondaryText
font.pixelSize: Theme.Theme.smallSize
font.bold: true
}
Label {
id: serverLabel
Layout.fillWidth: true
color: "#77A7D1"
font.pixelSize: Theme.Theme.normalSize
font.bold: true
Behavior on text {
SequentialAnimation {
NumberAnimation { target: serverLabel; property: "opacity"; to: 0; duration: 100 }
PropertyAction { target: serverLabel; property: "text" }
NumberAnimation { target: serverLabel; property: "opacity"; to: 1; duration: 100 }
}
}
}
}
}
Rectangle {
Layout.fillWidth: true
height: 1
color: Qt.darker(Theme.Theme.borderColor, 1.2)
opacity: 0.5
}
// Секция системных требований
RowLayout {
Layout.fillWidth: true
spacing: Theme.Theme.spacing
Image {
source: "qml/images/icons/requirements.png"
sourceSize: Qt.size(24, 24)
Layout.alignment: Qt.AlignTop
opacity: 0.8
}
ColumnLayout {
Layout.fillWidth: true
spacing: Theme.Theme.spacing / 2
Label {
text: "СИСТЕМНЫЕ ТРЕБОВАНИЯ"
color: Theme.Theme.secondaryText
font.pixelSize: Theme.Theme.smallSize
font.bold: true
}
Label {
id: reqLabel
Layout.fillWidth: true
color: Theme.Theme.secondaryText
font.pixelSize: Theme.Theme.smallSize
wrapMode: Text.WordWrap
Behavior on text {
SequentialAnimation {
NumberAnimation { target: reqLabel; property: "opacity"; to: 0; duration: 100 }
PropertyAction { target: reqLabel; property: "text" }
NumberAnimation { target: reqLabel; property: "opacity"; to: 1; duration: 100 }
}
}
}
}
}
}
}

View File

@ -0,0 +1,67 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import "../Theme" as Theme
Rectangle {
id: root
width: message.width + Theme.Theme.spacing * 4
height: message.height + Theme.Theme.spacing * 2
radius: Theme.Theme.radius
opacity: 0
property string text: ""
property string type: "info" // info, error, success
color: {
switch(type) {
case "error": return "#4D2C2C"
case "success": return "#2C4D2C"
default: return Theme.Theme.frameColor
}
}
border.color: {
switch(type) {
case "error": return "#FF4444"
case "success": return "#44FF44"
default: return Theme.Theme.borderColor
}
}
border.width: 1
Label {
id: message
anchors.centerIn: parent
text: root.text
color: Theme.Theme.primaryText
font.pixelSize: Theme.Theme.smallSize
}
// Анимации
NumberAnimation {
id: showAnim
target: root
property: "opacity"
to: 1
duration: 200
}
NumberAnimation {
id: hideAnim
target: root
property: "opacity"
to: 0
duration: 200
}
Timer {
id: hideTimer
interval: 3000
onTriggered: hideAnim.start()
}
function show() {
showAnim.start()
hideTimer.restart()
}
}

View File

@ -0,0 +1,151 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Controls.Material 2.15
import QtQuick.Layouts 1.15
import "../Theme" as Theme
Item {
id: root
Layout.fillWidth: true
Layout.minimumHeight: 40
visible: value > 0 || text !== ""
property alias value: progressBar.value
property alias text: speedLabel.text
ColumnLayout {
anchors.fill: parent
spacing: Theme.Theme.spacing
ProgressBar {
id: progressBar
Layout.fillWidth: true
Layout.preferredHeight: 14
from: 0
to: 1.0
background: Rectangle {
implicitWidth: 200
implicitHeight: 14
color: "#0A0A0A"
radius: 2
border.width: 1
border.color: "#2A4055"
clip: true
Rectangle {
anchors.fill: parent
radius: parent.radius
color: "#152536"
visible: progressBar.value > 0 && progressBar.value < 1
clip: true
Rectangle {
id: animatedBackground
width: parent.width
height: parent.height
color: "#1E3346"
radius: parent.radius
SequentialAnimation on opacity {
loops: Animation.Infinite
running: progressBar.value > 0 && progressBar.value < 1
NumberAnimation { to: 0.3; duration: 1000 }
NumberAnimation { to: 0.1; duration: 1000 }
}
}
}
}
contentItem: Item {
implicitWidth: 200
implicitHeight: 14
clip: true
Rectangle {
width: progressBar.visualPosition * parent.width
height: parent.height
radius: 2
clip: true
gradient: Gradient {
GradientStop { position: 0.0; color: "#1E3346" }
GradientStop { position: 0.5; color: "#2B4869" }
GradientStop { position: 1.0; color: "#1E3346" }
}
Rectangle {
width: parent.width
height: 1
color: "#3A5570"
opacity: 0.7
}
Rectangle {
width: parent.width
height: parent.height
radius: parent.radius
gradient: Gradient {
GradientStop { position: 0.0; color: "#20FFFFFF" }
GradientStop { position: 0.5; color: "#40FFFFFF" }
GradientStop { position: 1.0; color: "#20FFFFFF" }
}
SequentialAnimation on opacity {
loops: Animation.Infinite
running: progressBar.value > 0 && progressBar.value < 1
NumberAnimation { to: 0.1; duration: 1000 }
NumberAnimation { to: 0.3; duration: 1000 }
}
}
Behavior on width {
NumberAnimation {
duration: 100
easing.type: Easing.OutQuad
}
}
}
}
}
RowLayout {
Layout.fillWidth: true
spacing: Theme.Theme.spacing
Label {
id: speedLabel
color: "#77A7D1"
font.pixelSize: Theme.Theme.smallSize
font.bold: true
visible: text !== ""
Behavior on text {
SequentialAnimation {
NumberAnimation { target: speedLabel; property: "opacity"; to: 0.7; duration: 100 }
PropertyAction { target: speedLabel; property: "text" }
NumberAnimation { target: speedLabel; property: "opacity"; to: 1.0; duration: 100 }
}
}
}
Label {
Layout.fillWidth: true
text: Math.round(progressBar.value * 100) + "%"
color: "#77A7D1"
font.pixelSize: Theme.Theme.smallSize
font.bold: true
horizontalAlignment: Text.AlignRight
visible: progressBar.value > 0
Behavior on text {
SequentialAnimation {
NumberAnimation { target: parent; property: "opacity"; to: 0.7; duration: 100 }
PropertyAction { target: parent; property: "text" }
NumberAnimation { target: parent; property: "opacity"; to: 1.0; duration: 100 }
}
}
}
}
}
}

View File

@ -0,0 +1,73 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Controls.Material 2.15
import QtQuick.Layouts 1.15
import "../Theme" as Theme
Pane {
id: root
padding: Theme.Theme.margin
Material.elevation: 6
Material.background: Theme.Theme.frameColor
// Анимация появления
opacity: 0
Component.onCompleted: appearAnimation.start()
NumberAnimation {
id: appearAnimation
target: root
property: "opacity"
from: 0
to: 1
duration: 500
easing.type: Easing.OutCubic
}
background: Rectangle {
color: Qt.rgba(Theme.Theme.frameColor.r, Theme.Theme.frameColor.g, Theme.Theme.frameColor.b, 0.35)
radius: Theme.Theme.radius
// Граница с тенью
Rectangle {
anchors.fill: parent
radius: parent.radius
color: "transparent"
border.color: Qt.rgba(Theme.Theme.borderColor.r, Theme.Theme.borderColor.g, Theme.Theme.borderColor.b, 0.4)
border.width: 1
// Нижняя тень
Rectangle {
anchors.bottom: parent.bottom
width: parent.width
height: 2
color: "#000000"
opacity: 0.1
}
// Верхний блик
Rectangle {
width: parent.width
height: 1
color: "#FFFFFF"
opacity: 0.1
}
}
// Внутренний градиент
Rectangle {
anchors.fill: parent
radius: parent.radius
gradient: Gradient {
GradientStop { position: 0.0; color: Qt.rgba(1, 1, 1, 0.1) }
GradientStop { position: 1.0; color: Qt.rgba(1, 1, 1, 0) }
}
}
}
// Контент
contentItem: ColumnLayout {
spacing: Theme.Theme.spacing
}
}

View File

@ -0,0 +1,206 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Controls.Material 2.15
import QtQuick.Layouts 1.15
import "../Theme" as Theme
Dialog {
id: root
title: "Настройки"
modal: true
x: (parent.width - width) / 2
y: (parent.height - height) / 2
width: 400
height: 500
Material.background: Theme.Theme.frameColor
ScrollView {
id: scrollView
anchors.fill: parent
clip: true
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ScrollBar.vertical.policy: ScrollBar.AsNeeded
ColumnLayout {
width: scrollView.availableWidth
spacing: Theme.Theme.spacing
// Игровые настройки
GroupBox {
Layout.fillWidth: true
title: "Игровые настройки"
ColumnLayout {
anchors.fill: parent
// Путь к игре
Label {
text: "Путь к игре:"
color: Theme.Theme.secondaryText
}
RowLayout {
Layout.fillWidth: true
spacing: Theme.Theme.spacing
TextField {
id: gamePathField
Layout.fillWidth: true
text: launcher ? launcher.gamePath : ""
readOnly: true
color: Theme.Theme.primaryText
background: Rectangle {
color: Qt.darker(Theme.Theme.frameColor, 1.2)
border.color: Theme.Theme.borderColor
radius: 3
}
}
Button {
text: "Обзор"
onClicked: launcher && launcher.selectGamePath()
}
}
CheckBox {
text: "Автозапуск при старте Windows"
checked: launcher && launcher.settings ? launcher.settings.autostart : false
onCheckedChanged: if (launcher && launcher.settings) launcher.settings.autostart = checked
}
CheckBox {
text: "Закрывать лаунчер при запуске игры"
checked: launcher && launcher.settings ? launcher.settings.closeOnLaunch : false
onCheckedChanged: if (launcher && launcher.settings) launcher.settings.closeOnLaunch = checked
}
// Выбор эмулятора для Linux
RowLayout {
Layout.fillWidth: true
visible: Qt.platform.os !== "windows" // Показываем только на Linux/Mac
Label {
text: "Эмулятор Windows:"
color: Theme.Theme.secondaryText
}
ComboBox {
Layout.fillWidth: true
model: ["Wine", "Lutris", "Proton", "PortProton", "CrossOver"]
currentIndex: {
if (launcher && launcher.settings) {
switch(launcher.settings.linuxEmulator) {
case "wine": return 0;
case "lutris": return 1;
case "proton": return 2;
case "portproton": return 3;
case "crossover": return 4;
default: return 0;
}
}
return 0;
}
onCurrentIndexChanged: {
if (launcher && launcher.settings) {
switch(currentIndex) {
case 0: launcher.settings.linuxEmulator = "wine"; break;
case 1: launcher.settings.linuxEmulator = "lutris"; break;
case 2: launcher.settings.linuxEmulator = "proton"; break;
case 3: launcher.settings.linuxEmulator = "portproton"; break;
case 4: launcher.settings.linuxEmulator = "crossover"; break;
}
}
}
}
// Кнопка проверки эмулятора
Button {
text: "Проверить"
onClicked: if (launcher) launcher.checkEmulator()
}
}
}
}
// Настройки загрузки
GroupBox {
Layout.fillWidth: true
title: "Настройки загрузки"
ColumnLayout {
anchors.fill: parent
Label {
text: "Ограничение скорости загрузки:"
}
ComboBox {
Layout.fillWidth: true
model: ["Без ограничений", "1 Мбит/с", "2 Мбит/с", "5 Мбит/с", "10 Мбит/с"]
currentIndex: launcher && launcher.settings ? launcher.settings.speedLimit : 0
onCurrentIndexChanged: if (launcher && launcher.settings) launcher.settings.speedLimit = currentIndex
}
CheckBox {
text: "Автоматически загружать обновления"
checked: launcher && launcher.settings ? launcher.settings.autoUpdate : true
onCheckedChanged: if (launcher && launcher.settings) launcher.settings.autoUpdate = checked
}
}
}
// Настройки интерфейса
GroupBox {
Layout.fillWidth: true
title: "Настройки интерфейса"
ColumnLayout {
anchors.fill: parent
Label {
text: "Интервал слайд-шоу (сек):"
}
SpinBox {
Layout.fillWidth: true
from: 3
to: 15
value: launcher && launcher.settings ? launcher.settings.slideInterval : 5
onValueChanged: if (launcher && launcher.settings) launcher.settings.slideInterval = value
}
CheckBox {
text: "Показывать уведомления"
checked: launcher && launcher.settings ? launcher.settings.showNotifications : true
onCheckedChanged: if (launcher && launcher.settings) launcher.settings.showNotifications = checked
}
}
}
Item {
Layout.fillWidth: true
Layout.minimumHeight: Theme.Theme.spacing * 2
}
}
}
footer: DialogButtonBox {
Button {
text: "Применить"
DialogButtonBox.buttonRole: DialogButtonBox.ApplyRole
onClicked: {
if (launcher) launcher.saveSettings()
root.accept()
}
}
Button {
text: "Отмена"
DialogButtonBox.buttonRole: DialogButtonBox.RejectRole
onClicked: root.reject()
}
}
}

View File

@ -0,0 +1,153 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import "../Theme" as Theme
Item {
id: root
Layout.fillWidth: true
Layout.fillHeight: true
Layout.minimumHeight: 300
property var imageUrls: []
property int interval: 5000
// Добавляем второе изображение для плавного перехода
Image {
id: fadeOutImage
anchors.fill: parent
fillMode: Image.PreserveAspectFit
opacity: 0
}
Image {
id: mainImage
anchors.fill: parent
source: imageUrls.length > 0 ? imageUrls[currentIndex] : ""
fillMode: Image.PreserveAspectFit
property int currentIndex: 0
Timer {
interval: root.interval
running: imageUrls.length > 1
repeat: true
onTriggered: nextSlide()
}
Behavior on opacity {
NumberAnimation { duration: 800; easing.type: Easing.InOutQuad }
}
}
// Кнопки навигации
Rectangle {
id: prevButton
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
width: 40; height: 40
color: "transparent"
opacity: prevMouse.containsMouse ? 1 : 0.3
visible: imageUrls.length > 1
Text {
anchors.centerIn: parent
text: ""
color: "#FFFFFF"
font.pixelSize: 24
}
MouseArea {
id: prevMouse
anchors.fill: parent
hoverEnabled: true
onClicked: prevSlide()
}
}
Rectangle {
id: nextButton
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
width: 40; height: 40
color: "transparent"
opacity: nextMouse.containsMouse ? 1 : 0.3
visible: imageUrls.length > 1
Text {
anchors.centerIn: parent
text: ""
color: "#FFFFFF"
font.pixelSize: 24
}
MouseArea {
id: nextMouse
anchors.fill: parent
hoverEnabled: true
onClicked: nextSlide()
}
}
// Индикаторы в стиле WoW
Row {
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottomMargin: 10
spacing: 8
Repeater {
model: imageUrls.length
Rectangle {
width: 30
height: 4
radius: 2
color: mainImage.currentIndex === index ? "#00AAFF" : "#80FFFFFF"
opacity: mainImage.currentIndex === index ? 1.0 : 0.5
Rectangle {
visible: mainImage.currentIndex === index
anchors.fill: parent
color: "#40FFFFFF"
radius: parent.radius
SequentialAnimation on opacity {
running: mainImage.currentIndex === index
loops: Animation.Infinite
NumberAnimation { to: 0.2; duration: 1000 }
NumberAnimation { to: 1.0; duration: 1000 }
}
}
MouseArea {
anchors.fill: parent
onClicked: showSlide(index)
}
}
}
}
function nextSlide() {
fadeOutImage.source = mainImage.source
fadeOutImage.opacity = 1
mainImage.currentIndex = (mainImage.currentIndex + 1) % imageUrls.length
fadeOutImage.opacity = 0
}
function prevSlide() {
fadeOutImage.source = mainImage.source
fadeOutImage.opacity = 1
mainImage.currentIndex = mainImage.currentIndex === 0 ? imageUrls.length - 1 : mainImage.currentIndex - 1
fadeOutImage.opacity = 0
}
function showSlide(index) {
if (index !== mainImage.currentIndex) {
fadeOutImage.source = mainImage.source
fadeOutImage.opacity = 1
mainImage.currentIndex = index
fadeOutImage.opacity = 0
}
}
}

View File

@ -0,0 +1,131 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Controls.Material 2.15
import QtQuick.Layouts 1.15
import "../Theme" as Theme
Button {
id: control
height: Theme.Theme.buttonHeight
background: Rectangle {
color: {
if (!control.enabled) return "#0A0A0A"
if (control.pressed) return "#1A2A3A"
if (control.hovered) return "#1E3346"
return "#152536"
}
radius: 3
// Верхняя грань (блик)
Rectangle {
width: parent.width
height: 1
color: {
if (!control.enabled) return "#252525"
if (control.pressed) return "#2A4055"
if (control.hovered) return "#3A5570"
return "#2A4055"
}
opacity: control.pressed ? 0.5 : 1
}
// Нижняя грань (тень)
Rectangle {
width: parent.width
height: 1
anchors.bottom: parent.bottom
color: "#000000"
opacity: 0.5
}
// Боковые грани
Rectangle {
width: 1
height: parent.height
color: {
if (!control.enabled) return "#252525"
if (control.pressed) return "#2A4055"
if (control.hovered) return "#3A5570"
return "#2A4055"
}
}
Rectangle {
width: 1
height: parent.height
anchors.right: parent.right
color: {
if (!control.enabled) return "#252525"
if (control.pressed) return "#2A4055"
if (control.hovered) return "#3A5570"
return "#2A4055"
}
}
// Эффект свечения при наведении
Rectangle {
anchors.fill: parent
color: "#3A5570"
opacity: control.hovered ? 0.1 : 0
radius: parent.radius
SequentialAnimation on opacity {
running: control.hovered
loops: Animation.Infinite
NumberAnimation { to: 0.2; duration: 1000 }
NumberAnimation { to: 0.1; duration: 1000 }
}
}
}
contentItem: Text {
text: control.text
font {
family: "Arial"
pixelSize: Theme.Theme.normalSize
bold: true
capitalization: Font.AllUppercase
}
color: {
if (!control.enabled) return "#4A4A4A"
if (control.pressed) return "#88BBDD"
if (control.hovered) return "#BDE0FF"
return "#77A7D1"
}
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
Behavior on color {
ColorAnimation { duration: 150 }
}
}
// Улучшенная анимация нажатия
states: [
State {
name: "pressed"
when: control.pressed
PropertyChanges {
target: control
scale: 0.97
}
}
]
transitions: Transition {
NumberAnimation {
properties: "scale"
duration: 50
easing.type: Easing.OutQuad
}
}
ToolTip {
text: parent.tooltip || ""
visible: parent.hovered && text
delay: 500
}
property string tooltip: ""
}

View File

@ -0,0 +1,19 @@
import QtQuick 2.15
import QtQuick.Layouts 1.15
import "../Theme" as Theme
Rectangle {
color: Theme.Theme.frameColor
radius: Theme.Theme.radius
border.color: Theme.Theme.borderColor
border.width: 2
default property alias content: container.children
ColumnLayout {
id: container
anchors.fill: parent
anchors.margins: Theme.Theme.margin * 2
spacing: Theme.Theme.spacing
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 KiB

7
qml/components/qmldir Normal file
View File

@ -0,0 +1,7 @@
module components
WoWPanel 1.0 WoWPanel.qml
InfoPanel 1.0 InfoPanel.qml
WoWButton 1.0 WoWButton.qml
Section 1.0 Section.qml
Header 1.0 Header.qml
ProgressSection 1.0 ProgressSection.qml

BIN
qml/images/background.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 KiB

BIN
qml/images/icons/tray.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

2
requirements.txt Normal file
View File

@ -0,0 +1,2 @@
PyQt5>=5.15.0
requests>=2.25.1

BIN
screenshots/image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 662 KiB

1000
wow-launcher.py Executable file

File diff suppressed because it is too large Load Diff