-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Resolved all conflicts by accepting incoming changes
- Loading branch information
Showing
197 changed files
with
29,164 additions
and
25,374 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
from PyQt5.QtCore import QRectF | ||
from PyQt5.QtGui import QMovie | ||
from PyQt5.QtWidgets import QGraphicsPixmapItem | ||
|
||
|
||
class AnimatedGifItem(QGraphicsPixmapItem): | ||
def __init__(self, gif_path=None, parent=None): | ||
super(AnimatedGifItem, self).__init__(parent) | ||
self.movie = QMovie(gif_path) if gif_path else QMovie() | ||
self.movie.frameChanged.connect(self.updatePixmap) | ||
|
||
def setGifPath(self, path): | ||
self.movie.setFileName(path) | ||
self.movie.start() | ||
|
||
def updatePixmap(self, frame_number): | ||
self.setPixmap(self.movie.currentPixmap()) | ||
|
||
def play(self): | ||
if self.movie.state() in [QMovie.NotRunning, QMovie.Paused]: | ||
self.movie.start() | ||
|
||
def pause(self): | ||
if self.movie.state() == QMovie.Running: | ||
self.movie.setPaused(True) | ||
|
||
def resume(self): | ||
self.movie.setPaused(False) | ||
|
||
def stop(self): | ||
self.movie.stop() | ||
|
||
def boundingRect(self): | ||
return QRectF(self.pixmap().rect()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import sys | ||
|
||
from PyQt5.QtCore import Qt | ||
from PyQt5.QtGui import QScreen | ||
from PyQt5.QtWidgets import QApplication, QDesktopWidget, QGraphicsView, QMainWindow | ||
from Stage import Stage | ||
from StageShareManager import StageShareManager | ||
|
||
from pydofus2.com.ankamagames.atouin.Atouin import Atouin | ||
from pydofus2.com.ankamagames.atouin.data.elements.Elements import Elements | ||
from pydofus2.com.ankamagames.atouin.managers.MapDisplayManager import MapDisplayManager | ||
from pydofus2.com.ankamagames.atouin.types.AtouinOptions import AtouinOptions | ||
from pydofus2.com.ankamagames.atouin.types.DataMapContainer import DataMapContainer | ||
from pydofus2.com.ankamagames.atouin.types.SimpleGraphicsContainer import SimpleGraphicsContainer | ||
from pydofus2.com.ankamagames.dofus.kernel.Kernel import Kernel | ||
from pydofus2.com.ankamagames.dofus.types.DofusOptions import DofusOptions | ||
from pydofus2.com.ankamagames.jerakine.logger.Logger import Logger | ||
from pydofus2.com.ankamagames.jerakine.managers.LangManager import LangManager | ||
from pydofus2.com.ankamagames.jerakine.types.CustomSharedObject import CustomSharedObject | ||
from pydofus2.DofusUI.MapRenderer import MapRenderer | ||
from pydofus2.DofusUI.OptionManager import OptionManager | ||
|
||
|
||
class DofusUI(QMainWindow): | ||
_instance = None | ||
_initialized = False | ||
|
||
def __new__(cls): | ||
if not cls._instance: | ||
cls._instance = super(DofusUI, cls).__new__(cls) | ||
return cls._instance | ||
|
||
def __init__(self, parent=None): | ||
if self._initialized: | ||
return | ||
super(DofusUI, self).__init__(parent) | ||
self.setObjectName("sharedStage") | ||
self.setWindowTitle("DofusUI") | ||
self.stage = Stage(self) | ||
self.view = QGraphicsView(self.stage) | ||
self.view.setObjectName("stage main view") | ||
self.setCentralWidget(self.view) | ||
self._windowInitialized = False | ||
self._worldContainer = SimpleGraphicsContainer() | ||
self.stage.addItem(self._worldContainer) | ||
StageShareManager().stage = self.stage | ||
StageShareManager().qMainWindow = self | ||
self.initOptions() | ||
|
||
def initWindow(self, isFullScreen): | ||
if self._windowInitialized: | ||
return | ||
self._windowInitialized = True | ||
clientDimensionSo = CustomSharedObject.getLocal("clientData") | ||
if clientDimensionSo.data.get("width") > 0 and clientDimensionSo.data.get("height") > 0: | ||
self.resize(clientDimensionSo.data["width"], clientDimensionSo.data["height"]) | ||
if not isFullScreen and clientDimensionSo.data.get("displayState") == "maximized": | ||
self.setWindowState(Qt.WindowMaximized) | ||
else: | ||
mainScreen = QScreen.availableGeometry(QApplication.primaryScreen()) | ||
self.resize(mainScreen.width() * 0.8, mainScreen.height() * 0.8) | ||
self.setWindowState(Qt.WindowMaximized) | ||
self.centerOnScreen() | ||
self.show() | ||
|
||
def centerOnScreen(self): | ||
qr = self.frameGeometry() | ||
desktop_geom = QDesktopWidget().availableGeometry() | ||
cp = desktop_geom.center() | ||
qr.moveCenter(cp) | ||
self.move(qr.topLeft()) | ||
|
||
def setDisplayOptions(self, opt: DofusOptions): | ||
self.initWindow(opt.getOption("fullScreen")) | ||
self._doOptions = opt | ||
self._doOptions.propertyChanged.connect(self.onOptionChange) | ||
|
||
def onOptionChange(self, name, value, oldValue): | ||
Logger().info(f"DofusUI property {name} changed from {oldValue} to {value}") | ||
|
||
def getWorldContainer(self): | ||
return self._worldContainer | ||
|
||
def initOptions(self): | ||
from pydofus2.com.ankamagames.atouin.Atouin import Atouin | ||
from pydofus2.com.ankamagames.atouin.Frustum import Frustum | ||
|
||
OptionManager.reset() | ||
ao = AtouinOptions(self.getWorldContainer(), Kernel().worker) | ||
ao.setOption( | ||
"frustum", | ||
Frustum( | ||
LangManager().getIntEntry("config.atouin.frustum.marginLeft"), | ||
LangManager().getIntEntry("config.atouin.frustum.marginTop"), | ||
LangManager().getIntEntry("config.atouin.frustum.marginRight"), | ||
LangManager().getIntEntry("config.atouin.frustum.marginBottom"), | ||
), | ||
) | ||
ao.setOption("mapsPath", LangManager().getEntry("config.atouin.path.maps")) | ||
ao.setOption("elementsIndexPath", LangManager().getEntry("config.atouin.path.elements")) | ||
ao.setOption("elementsPath", LangManager().getEntry("config.gfx.path.cellElement")) | ||
ao.setOption("swfPath", LangManager().getEntry("config.gfx.path.world.swf")) | ||
ao.setOption("tacticalModeTemplatesPath", LangManager().getEntry("config.atouin.path.tacticalModeTemplates")) | ||
Atouin().setDisplayOptions(ao) | ||
self.setDisplayOptions(DofusOptions()) | ||
|
||
def saveClientSize(self): | ||
clientDimensionSo = CustomSharedObject.getLocal("clientData") | ||
clientDimensionSo.data["height"] = self.width() | ||
clientDimensionSo.data["width"] = self.height() | ||
clientDimensionSo.data["displayState"] = ( | ||
"maximized" if self.windowState() == Qt.WindowMaximized else str(self.windowState()) | ||
) | ||
clientDimensionSo.flush() | ||
clientDimensionSo.close() | ||
|
||
def beforeExit(self): | ||
# This is the method you want to execute before the window closes | ||
print("Executing beforeExit: performing cleanup or saving settings.") | ||
self.saveClientSize() | ||
|
||
def closeEvent(self, event): | ||
# This method is automatically called when the window is about to close | ||
self.beforeExit() # Call your method here | ||
event.accept() # Proceed with the window closure | ||
|
||
|
||
if __name__ == "__main__": | ||
Logger.logToConsole = True | ||
app = QApplication(sys.argv) | ||
main_window = DofusUI() | ||
mapRenderer = MapRenderer(Atouin().worldContainer, Elements()) | ||
MapDisplayManager().loadMap(191104002.0) | ||
dataMap = DataMapContainer(MapDisplayManager().dataMap) | ||
mapRenderer.render(dataMap) | ||
sys.exit(app.exec_()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
from queue import Queue | ||
|
||
from PyQt5.QtCore import QMutex, QMutexLocker, QObject, QRunnable, QThreadPool, QWaitCondition, pyqtSignal | ||
|
||
from pydofus2.com.ankamagames.jerakine.resources.protocols.impl.PakProtocol2 import PakProtocol2 | ||
from pydofus2.com.ankamagames.jerakine.types.Uri import Uri | ||
|
||
|
||
class GfxLoaderWorker(QRunnable): | ||
def __init__( | ||
self, task_queue: Queue, mutex: QMutex, init_conditions: dict[str, QWaitCondition], loader: "GfxParallelLoader" | ||
): | ||
super().__init__() | ||
self.task_queue = task_queue | ||
self.mutex = mutex | ||
self.loader = loader | ||
self.init_conditions = init_conditions | ||
|
||
def run(self): | ||
with QMutexLocker(self.mutex): | ||
self.loader.active_workers += 1 | ||
|
||
while True: | ||
uri: Uri = None | ||
with QMutexLocker(self.mutex): | ||
if self.task_queue.empty(): | ||
break | ||
uri = self.task_queue.get() | ||
|
||
if uri: | ||
try: | ||
# Check and wait if initialization is needed and ongoing by another thread | ||
self.wait_for_initialization(uri.path) | ||
|
||
# Perform the initialization if needed | ||
if uri.path not in self.loader.protocol.indexes: | ||
with QMutexLocker(self.mutex): | ||
# Check again to ensure it hasn't been initialized in the meantime | ||
if uri.path not in self.loader.protocol.indexes: | ||
self.init_conditions[uri.path] = QWaitCondition() | ||
self.loader.protocol.initStreamsIndexTable(uri) | ||
condition = self.init_conditions.pop(uri.path, None) | ||
if condition: | ||
condition.wakeAll() | ||
|
||
# Load directly after ensuring the initialization is done | ||
result = self.loader.protocol.loadDirectly(uri) | ||
if result is None: | ||
raise Exception( | ||
f"No GFX found matching the uri {uri.path} / {uri.subPath} in the gfx d2o file index" | ||
) | ||
|
||
self.loader.progress.emit(uri.tag, result) | ||
|
||
except Exception as e: | ||
self.loader.error.emit(uri.tag, str(e)) | ||
with QMutexLocker(self.mutex): | ||
self.loader.active_workers -= 1 | ||
self.loader.onWorkerDied() | ||
|
||
def wait_for_initialization(self, path): | ||
with QMutexLocker(self.mutex): | ||
if path in self.loader.protocol._indexes: | ||
return # Already initialized, no need to wait | ||
if path in self.init_conditions: | ||
condition = self.init_conditions[path] | ||
condition.wait(self.mutex) # Wait until initialization is complete | ||
|
||
|
||
class GfxParallelLoader(QObject): | ||
progress = pyqtSignal(int, bytes) # Emit tag and result | ||
error = pyqtSignal(int, Exception) # Emit error message | ||
finished = pyqtSignal() # Emit when all tasks are done | ||
|
||
def __init__(self, maxThreads=6): | ||
super().__init__() | ||
self.task_queue = Queue() | ||
self.mutex = QMutex() | ||
self.pool = QThreadPool() | ||
self.protocol = PakProtocol2() | ||
self.pool.setMaxThreadCount(maxThreads) | ||
self.active_workers = 0 | ||
self.init_conditions = {} | ||
|
||
def loadItems(self, uris: list[Uri]): | ||
for uri in uris: | ||
self.task_queue.put(uri) | ||
if not self.pool.activeThreadCount(): | ||
for _ in range(self.pool.maxThreadCount()): | ||
worker = GfxLoaderWorker(self.task_queue, self.mutex, self.init_conditions, self) | ||
self.pool.start(worker) | ||
|
||
def onWorkerDied(self): | ||
if self.active_workers == 0: | ||
self.finished.emit() |
Oops, something went wrong.