Commit 1b37eb37 authored by Exc404's avatar Exc404

Merge branch 'main' into Анимации

parents ffeb4b7e 9a49b9d1
...@@ -7,12 +7,13 @@ Use the following commands in UNIX shell: ...@@ -7,12 +7,13 @@ Use the following commands in UNIX shell:
```shell ```shell
# Prepare and activate virtual environment # Prepare and activate virtual environment
python -m venv .venv python -m pip install --user pipx
source .venv/bin/activate python -m pipx ensurepath
pipx install poetry
# Install requirements # Install requirements
pip install -r requirements.txt poetry install
# Run # Run
python src/main.py poetry run main
``` ```
\ No newline at end of file
import sys import sys
from pathlib import Path from pathlib import Path
from PySide6.QtGui import QGuiApplication from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine from PySide6.QtQml import QQmlApplicationEngine
from ingame.models.App import App
from ingame.models.GamesModel import GamesModel
from models.App import App
from models.GamesModel import GamesModel
# TODO: add VirtualKeyboard # TODO: add VirtualKeyboard
if __name__ == "__main__": def main():
app = QGuiApplication(sys.argv) app = QGuiApplication(sys.argv)
app_model = App()
app.aboutToQuit.connect(app_model.close_event)
qml_file = Path(__file__).resolve().parent / "../qml/qml.qml" qml_file = Path(__file__).resolve().parent / "../qml/qml.qml"
engine = QQmlApplicationEngine() engine = QQmlApplicationEngine()
app_model = App()
context = engine.rootContext() context = engine.rootContext()
context.setContextProperty("core_app", app_model) context.setContextProperty("core_app", app_model)
...@@ -23,3 +25,7 @@ if __name__ == "__main__": ...@@ -23,3 +25,7 @@ if __name__ == "__main__":
sys.exit(-1) sys.exit(-1)
sys.exit(app.exec()) sys.exit(app.exec())
if __name__ == "__main__":
main()
import threading import threading
import glob
import os.path
import subprocess
from time import sleep from time import sleep
from pathlib import Path
from typing import AnyStr, Union
from PySide6 import QtCore from PySide6 import QtCore
from os.path import expanduser from os.path import expanduser
import glob
from desktop_parser import DesktopFile from desktop_parser import DesktopFile
import os.path
from pathlib import Path from ingame.models.Gamepad import Gamepad
from ingame.models.GamesModel import Game, GamesModel
from PySide6.QtCore import Property, Signal, Slot, QObject, Qt from PySide6.QtCore import Property, Signal, Slot, QObject, Qt
from models.GamesModel import Game, GamesModel
import subprocess
class GameShortcut: class GameShortcut:
...@@ -23,13 +26,27 @@ class App(QtCore.QObject): ...@@ -23,13 +26,27 @@ class App(QtCore.QObject):
game_started = Signal(bool, name="gameStarted") game_started = Signal(bool, name="gameStarted")
game_ended = Signal(bool, name="gameEnded") game_ended = Signal(bool, name="gameEnded")
gamepad_clicked_LB = Signal(bool, name="gamepadClickedLB")
gamepad_clicked_RB = Signal(bool, name="gamepadClickedRB")
gamepad_clicked_apply = Signal(bool, name="gamepadClickedApply")
gamepad_axis_left = Signal(bool, name="gamepadAxisLeft")
gamepad_axis_right = Signal(bool, name="gamepadAxisRight")
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.games_model = GamesModel() self.games_model: GamesModel = GamesModel()
self.home = expanduser('~') self.home: AnyStr = expanduser('~')
self.config_location = '/.config/PortProton.conf' self.config_location: str = '/.config/PortProton.conf'
self.portproton_location = '' self.portproton_location: str = ''
self.running_game_process = None self.running_game_process: Union[subprocess.Popen, None] = None
self.gamepad: Gamepad = Gamepad()
self.gamepad.lb_clicked = lambda: self.gamepad_clicked_LB.emit(True)
self.gamepad.rb_clicked = lambda: self.gamepad_clicked_RB.emit(True)
self.gamepad.apply_clicked = lambda: self.gamepad_clicked_apply.emit(True)
self.gamepad.l_clicked = lambda: self.gamepad_axis_left.emit(True)
self.gamepad.r_clicked = lambda: self.gamepad_axis_right.emit(True)
self.setup() self.setup()
def setup(self): def setup(self):
...@@ -46,15 +63,15 @@ class App(QtCore.QObject): ...@@ -46,15 +63,15 @@ class App(QtCore.QObject):
data = desktop_file.data data = desktop_file.data
entry = data['Desktop Entry'] entry = data['Desktop Entry']
name = entry['Name'] or 'generic' _name = entry['Name'] or 'generic'
exec = 'Exec' in entry and entry['Exec'] or '' _exec = 'Exec' in entry and entry['Exec'] or ''
icon = entry['Icon'] _icon = entry['Icon']
assert (isinstance(name, str) assert (isinstance(_name, str)
and isinstance(exec, str) and isinstance(_exec, str)
and isinstance(icon, str)) and isinstance(_icon, str))
exec_split = exec.split(' ') exec_split = _exec.split(' ')
# Ignore extra non-related desktop entries # Ignore extra non-related desktop entries
if (len(exec_split) <= 1 or if (len(exec_split) <= 1 or
...@@ -63,12 +80,17 @@ class App(QtCore.QObject): ...@@ -63,12 +80,17 @@ class App(QtCore.QObject):
# TODO parse product name # TODO parse product name
icon = (os.path.isfile(icon) and icon _icon = (os.path.isfile(_icon) and _icon
or os.path.realpath(f"{Path(__file__).resolve().parent}../../../qml/images/game_icon.png")) or os.path.realpath(f"{Path(__file__).resolve().parent}../../../qml/images/PUBG.png"))
# Автозапуск игры:
# PW_GUI_DISABLED_CS=1
# START_FROM_STEAM=1
_exec = f"env START_FROM_STEAM=1 {_exec}"
# Тест карточек self.games_model.add_game(Game(name=_name, icon=_icon, exec=_exec))
icon = os.path.realpath(f"{Path(__file__).resolve().parent}../../../qml/images/PUBG.png")
self.games_model.add_game(Game(name=name, icon=icon, exec=exec)) self.gamepad.run()
except FileNotFoundError: except FileNotFoundError:
print('File not found') print('File not found')
...@@ -76,6 +98,16 @@ class App(QtCore.QObject): ...@@ -76,6 +98,16 @@ class App(QtCore.QObject):
print('An error occurred', e) print('An error occurred', e)
pass pass
### CALLBACKS ###
def close_event(self):
# do stuff
# if can_exit:
self.gamepad.terminate()
# event.accept() # let the window close
# else:
# event.ignore()
### SLOTS ### ### SLOTS ###
@Slot(str) @Slot(str)
......
import subprocess
import threading
from typing import Union
import pygame
from pygame.joystick import Joystick
class Gamepad:
LB_BUTTON = 4
RB_BUTTON = 5
LEFT_RIGHT_AXIS = 0
LEFT_RIGHT_AXIS_SENSITIVITY = 0.7
APPLY_BUTTON = 0
def __init__(self):
self.joystick: Union[Joystick, None] = None
self.terminated: bool = False
self.last_rb_clicked: bool = False
self.last_lb_clicked: bool = False
self.last_apply_clicked: bool = False
self.last_left_clicked: bool = False
self.last_right_clicked: bool = False
self.lb_clicked: () = lambda: None
self.rb_clicked: () = lambda: None
self.l_clicked: () = lambda: None
self.r_clicked: () = lambda: None
self.apply_clicked: () = lambda: None
self.thread: Union[threading.Thread, None] = None
pygame.init()
pygame.joystick.init()
def run(self):
joystick_count = pygame.joystick.get_count()
if joystick_count == 0:
print("No joysticks found.")
else:
self.joystick = pygame.joystick.Joystick(0)
self.joystick.init()
self.thread = threading.Thread(target=lambda t, _exec: t.cycle(), args=(self, exec))
self.thread.start()
def cycle(self):
try:
# TODO: use this instead:
# events = pygame.event.get()
# for event in events:
# if event.type == pygame.JOYBUTTONDOWN:
while not self.terminated:
# pygame.event.pump()
pygame.event.wait()
lb_button = self.joystick.get_button(self.LB_BUTTON)
rb_button = self.joystick.get_button(self.RB_BUTTON)
apply_button = self.joystick.get_button(self.APPLY_BUTTON)
left_right_axis = self.joystick.get_axis(self.LEFT_RIGHT_AXIS)
# LB
if lb_button and not self.last_lb_clicked:
self.last_lb_clicked = not self.last_lb_clicked
self.lb_clicked()
if not lb_button and self.last_lb_clicked:
self.last_lb_clicked = not self.last_lb_clicked
# RB
if rb_button and not self.last_rb_clicked:
self.last_rb_clicked = not self.last_rb_clicked
self.rb_clicked()
if not rb_button and self.last_rb_clicked:
self.last_rb_clicked = not self.last_rb_clicked
# APPLY
if apply_button and not self.last_apply_clicked:
self.last_apply_clicked = not self.last_apply_clicked
self.apply_clicked()
if not apply_button and self.last_apply_clicked:
self.last_apply_clicked = not self.last_apply_clicked
# LEFT
if (left_right_axis <= -self.LEFT_RIGHT_AXIS_SENSITIVITY) and not self.last_left_clicked:
self.last_left_clicked = not self.last_left_clicked
self.l_clicked()
if not (left_right_axis <= -self.LEFT_RIGHT_AXIS_SENSITIVITY) and self.last_left_clicked:
self.last_left_clicked = not self.last_left_clicked
# RIGHT
if (left_right_axis >= self.LEFT_RIGHT_AXIS_SENSITIVITY) and not self.last_right_clicked:
self.last_right_clicked = not self.last_right_clicked
self.r_clicked()
if (not left_right_axis >= self.LEFT_RIGHT_AXIS_SENSITIVITY) and self.last_right_clicked:
self.last_right_clicked = not self.last_right_clicked
# print(f"Button {self.LB_BUTTON}: {lb_button}")
# print(f"Button {self.RB_BUTTON}: {rb_button}")
# for i in range(self.joystick.get_numaxes()):
# axis = self.joystick.get_axis(i)
# print(f"Axis {i}: {axis}")
# for i in range(self.joystick.get_numbuttons()):
# button = self.joystick.get_button(i)
# print(f"Button {i}: {button}")
except pygame.error:
pass
def terminate(self):
self.terminated = True
pygame.quit()
pass
pass
import typing import typing
from dataclasses import dataclass, fields from dataclasses import dataclass, fields
from PySide6.QtCore import QAbstractListModel, QModelIndex, Qt, QByteArray from PySide6.QtCore import QAbstractListModel, QModelIndex, Qt, QByteArray
......
This diff is collapsed. Click to expand it.
[tool.poetry]
name = "ingame"
version = "0.1.0"
description = ""
authors = ["Mikhail Tergoev <57610802+castro-fidel@users.noreply.github.com>"]
readme = "README.md"
[tool.poetry.dependencies]
python = ">=3.11,<3.13"
PySide6 = "^6.7.0"
PySide6-Essentials = "^6.7.0"
PySide6-Addons = "^6.7.0"
shiboken6 = "^6.7.0"
pyqtgraph = "^0.13.6"
requests = "^2.31.0"
desktop-parser = "^0.1.1"
pygame = "^2.5.2"
[tool.poetry.group.dev.dependencies]
mypy = "^1.9.0"
[tool.poetry.scripts]
main = 'ingame.main:main'
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
...@@ -223,6 +223,75 @@ Rectangle { ...@@ -223,6 +223,75 @@ Rectangle {
// LOGIC
property int focusedTabs: 0;
property int focusedItems: 0;
function applyTabsFocus(inc){
if(!visible)
return;
let c = row.children;
tabs.focusedTabs += inc;
if(tabs.focusedTabs >= c.length)
tabs.focusedTabs = 0;
if(tabs.focusedTabs < 0)
tabs.focusedTabs = c.length - 1;
c[tabs.focusedTabs].forceActiveFocus();
c[tabs.focusedTabs].clicked();
/* if (c[i].focus) {
console.log("focus found");
c[i].nextItemInFocusChain().forceActiveFocus()
break
} */
}
function applyItemsFocus(inc){
if(!gamesScroller.visible)
return;
let c = gamesGrid.children;
tabs.focusedItems += inc;
if(tabs.focusedItems >= c.length)
tabs.focusedItems = 0;
if(tabs.focusedItems < 0)
tabs.focusedItems = c.length - 1;
c[tabs.focusedItems].forceActiveFocus();
// gamesScroller.contentY = c[tabs.focusedItems].y; // not working
// c[tabs.focusedItems].clicked();
}
Connections {
target: core_app
function onGamepadClickedLB(done){
if(!visible) return;
tabs.applyTabsFocus(-1)
}
function onGamepadClickedRB(done){
if(!visible) return;
tabs.applyTabsFocus(1)
}
function onGamepadAxisLeft(done){
if(!visible) return;
tabs.applyItemsFocus(-1)
}
function onGamepadAxisRight(done){
if(!visible) return;
tabs.applyItemsFocus(1)
}
function onGamepadClickedApply(done){
if(!visible) return;
let c = gamesGrid.children;
c[tabs.focusedItems].clicked();
}
}
} }
......
...@@ -11,13 +11,32 @@ Window { ...@@ -11,13 +11,32 @@ Window {
Connections { Connections {
target: core_app target: core_app
function onGameStarted(done) { function onGameStarted(done) {
console.log("gameStarted!!"); console.log("core_app: gameStarted");
window.scene = SceneConstants.runningScene; window.scene = SceneConstants.runningScene;
} }
function onGameEnded(done) { function onGameEnded(done) {
console.log("gameEnded!!"); console.log("core_app: gameEnded");
window.scene = SceneConstants.gameInfoScene; window.scene = SceneConstants.gameInfoScene;
} }
function onGamepadClickedLB(done){
// console.log("core_app: onGamepadClickedLB");
}
function onGamepadClickedRB(done){
// console.log("core_app: onGamepadClickedRB");
}
function onGamepadAxisLeft(done){
// console.log("core_app: onGamepadAxisLeft");
}
function onGamepadAxisRight(done){
// console.log("core_app: onGamepadAxisRight");
}
function onGamepadClickedApply(done){
// console.log("core_app: onGamepadClickedApply");
}
}
Component.onDestruction: {
console.log("Desctructing window");
} }
id: window id: window
......
...@@ -79,4 +79,38 @@ Rectangle { ...@@ -79,4 +79,38 @@ Rectangle {
} }
} }
// LOGIC
property int focusedItems: 0;
function applyItemsFocus(inc){
let c = children;
focusedItems += inc;
if(focusedItems >= c.length)
focusedItems = 0;
if(focusedItems < 0)
focusedItems = c.length - 1;
c[focusedItems].forceActiveFocus();
// c[focusedItems].clicked();
}
Connections {
target: core_app
function onGamepadAxisLeft(done){
if(!visible) return;
container.applyItemsFocus(-1)
}
function onGamepadAxisRight(done){
if(!visible) return;
container.applyItemsFocus(1)
}
function onGamepadClickedApply(done){
if(!visible) return;
let c = container.children;
c[container.focusedItems].clicked();
}
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment