λ my beloved
This commit is contained in:
@@ -45,16 +45,6 @@ class GameCover:
|
|||||||
self.pictures = pictures
|
self.pictures = pictures
|
||||||
self.new_cover(path)
|
self.new_cover(path)
|
||||||
|
|
||||||
# Wrap the function in another one as Gio.Task.run_in_thread does not allow for passing args
|
|
||||||
def create_func(self, path: Optional[Path]) -> Callable:
|
|
||||||
self.animation = GdkPixbuf.PixbufAnimation.new_from_file(str(path))
|
|
||||||
self.anim_iter = self.animation.get_iter()
|
|
||||||
|
|
||||||
def wrapper(task: Gio.Task, *_args: Any) -> None:
|
|
||||||
self.update_animation((task, self.animation))
|
|
||||||
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
def new_cover(self, path: Optional[Path] = None) -> None:
|
def new_cover(self, path: Optional[Path] = None) -> None:
|
||||||
self.animation = None
|
self.animation = None
|
||||||
self.texture = None
|
self.texture = None
|
||||||
@@ -64,8 +54,12 @@ class GameCover:
|
|||||||
|
|
||||||
if path:
|
if path:
|
||||||
if path.suffix == ".gif":
|
if path.suffix == ".gif":
|
||||||
|
self.animation = GdkPixbuf.PixbufAnimation.new_from_file(str(path))
|
||||||
|
self.anim_iter = self.animation.get_iter()
|
||||||
self.task = Gio.Task.new()
|
self.task = Gio.Task.new()
|
||||||
self.task.run_in_thread(self.create_func(self.path))
|
self.task.run_in_thread(
|
||||||
|
lambda *_: self.update_animation((self.task, self.animation))
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self.texture = Gdk.Texture.new_from_filename(str(path))
|
self.texture = Gdk.Texture.new_from_filename(str(path))
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
from gi.repository import Adw, GLib, Gtk
|
from gi.repository import Adw, Gio, GLib, Gtk
|
||||||
|
|
||||||
from src import shared
|
from src import shared
|
||||||
from src.errors.error_producer import ErrorProducer
|
from src.errors.error_producer import ErrorProducer
|
||||||
@@ -31,7 +31,6 @@ from src.importer.sources.location import UnresolvableLocationError
|
|||||||
from src.importer.sources.source import Source
|
from src.importer.sources.source import Source
|
||||||
from src.store.managers.async_manager import AsyncManager
|
from src.store.managers.async_manager import AsyncManager
|
||||||
from src.store.pipeline import Pipeline
|
from src.store.pipeline import Pipeline
|
||||||
from src.utils.task import Task
|
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=too-many-instance-attributes
|
# pylint: disable=too-many-instance-attributes
|
||||||
@@ -121,10 +120,13 @@ class Importer(ErrorProducer):
|
|||||||
|
|
||||||
for source in self.sources:
|
for source in self.sources:
|
||||||
logging.debug("Importing games from source %s", source.source_id)
|
logging.debug("Importing games from source %s", source.source_id)
|
||||||
task = Task.new(None, None, self.source_callback, (source,))
|
task = Gio.Task.new(None, None, self.source_callback, (source,))
|
||||||
self.n_source_tasks_created += 1
|
self.n_source_tasks_created += 1
|
||||||
task.set_task_data((source,))
|
task.run_in_thread(
|
||||||
task.run_in_thread(self.source_task_thread_func)
|
lambda _task, _obj, _data, _cancellable, src=source: self.source_task_thread_func(
|
||||||
|
(src,)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
self.progress_changed_callback()
|
self.progress_changed_callback()
|
||||||
|
|
||||||
@@ -145,9 +147,7 @@ class Importer(ErrorProducer):
|
|||||||
)
|
)
|
||||||
self.import_dialog.present()
|
self.import_dialog.present()
|
||||||
|
|
||||||
def source_task_thread_func(
|
def source_task_thread_func(self, data: tuple) -> None:
|
||||||
self, _task: Any, _obj: Any, data: tuple, _cancellable: Any
|
|
||||||
) -> None:
|
|
||||||
"""Source import task code"""
|
"""Source import task code"""
|
||||||
|
|
||||||
source: Source
|
source: Source
|
||||||
|
|||||||
@@ -17,13 +17,12 @@
|
|||||||
#
|
#
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from typing import Callable, Any
|
from typing import Any, Callable
|
||||||
|
|
||||||
from gi.repository import Gio
|
from gi.repository import Gio
|
||||||
|
|
||||||
from src.game import Game
|
from src.game import Game
|
||||||
from src.store.managers.manager import Manager
|
from src.store.managers.manager import Manager
|
||||||
from src.utils.task import Task
|
|
||||||
|
|
||||||
|
|
||||||
class AsyncManager(Manager):
|
class AsyncManager(Manager):
|
||||||
@@ -49,11 +48,10 @@ class AsyncManager(Manager):
|
|||||||
self, game: Game, additional_data: dict, callback: Callable[["Manager"], Any]
|
self, game: Game, additional_data: dict, callback: Callable[["Manager"], Any]
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Create a task to process the game in a separate thread"""
|
"""Create a task to process the game in a separate thread"""
|
||||||
task = Task.new(None, self.cancellable, self._task_callback, (callback,))
|
task = Gio.Task.new(None, self.cancellable, self._task_callback, (callback,))
|
||||||
task.set_task_data((game, additional_data))
|
task.run_in_thread(lambda *_: self._task_thread_func((game, additional_data)))
|
||||||
task.run_in_thread(self._task_thread_func)
|
|
||||||
|
|
||||||
def _task_thread_func(self, _task, _source_object, data, _cancellable):
|
def _task_thread_func(self, data):
|
||||||
"""Task thread entry point"""
|
"""Task thread entry point"""
|
||||||
game, additional_data, *_rest = data
|
game, additional_data, *_rest = data
|
||||||
self.run(game, additional_data)
|
self.run(game, additional_data)
|
||||||
|
|||||||
@@ -1,95 +0,0 @@
|
|||||||
# task.py
|
|
||||||
#
|
|
||||||
# Copyright 2023 Geoffrey Coulaud
|
|
||||||
#
|
|
||||||
# This program is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
from functools import wraps
|
|
||||||
from typing import Any, Callable
|
|
||||||
|
|
||||||
from gi.repository import Gio
|
|
||||||
|
|
||||||
|
|
||||||
def create_task_thread_func_closure(func: Callable, data: Any) -> Callable:
|
|
||||||
"""Wrap a Gio.TaskThreadFunc with the given data in a closure"""
|
|
||||||
|
|
||||||
def closure(
|
|
||||||
task: Gio.Task, source_object: object, _data: Any, cancellable: Gio.Cancellable
|
|
||||||
) -> Any:
|
|
||||||
func(task, source_object, data, cancellable)
|
|
||||||
|
|
||||||
return closure
|
|
||||||
|
|
||||||
|
|
||||||
def decorate_set_task_data(task: Gio.Task) -> Callable:
|
|
||||||
"""Decorate Gio.Task.set_task_data to replace it"""
|
|
||||||
|
|
||||||
def decorator(original_method: Callable) -> Callable:
|
|
||||||
@wraps(original_method)
|
|
||||||
def new_method(task_data: Any) -> None:
|
|
||||||
task.task_data = task_data
|
|
||||||
|
|
||||||
return new_method
|
|
||||||
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
|
|
||||||
def decorate_run_in_thread(task: Gio.Task) -> Callable:
|
|
||||||
"""Decorate Gio.Task.run_in_thread to pass the task data correctly
|
|
||||||
Creates a closure around task_thread_func with the task data available."""
|
|
||||||
|
|
||||||
def decorator(original_method: Callable) -> Callable:
|
|
||||||
@wraps(original_method)
|
|
||||||
def new_method(task_thread_func: Callable) -> None:
|
|
||||||
closure = create_task_thread_func_closure(task_thread_func, task.task_data)
|
|
||||||
original_method(closure)
|
|
||||||
|
|
||||||
return new_method
|
|
||||||
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=too-few-public-methods
|
|
||||||
class Task:
|
|
||||||
"""Wrapper around Gio.Task to patch task data not being passed"""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def new(
|
|
||||||
cls,
|
|
||||||
source_object: object,
|
|
||||||
cancellable: Gio.Cancellable,
|
|
||||||
callback: Callable,
|
|
||||||
callback_data: Any,
|
|
||||||
) -> Gio.Task:
|
|
||||||
"""Create a new, monkey-patched Gio.Task.
|
|
||||||
The `set_task_data` and `run_in_thread` methods are decorated.
|
|
||||||
|
|
||||||
As of 2023-05-19, PyGObject does not work well with Gio.Task, so to pass data
|
|
||||||
the only viable way it to create a closure with the thread function and its data.
|
|
||||||
This class is supposed to make Gio.Task comply with its expected behaviour
|
|
||||||
per the docs:
|
|
||||||
|
|
||||||
http://lazka.github.io/pgi-docs/#Gio-2.0/classes/Task.html#Gio.Task.set_task_data
|
|
||||||
|
|
||||||
This code may break if pygobject overrides change in the future.
|
|
||||||
We need to manually pass `self` to the decorators since it's otherwise bound but
|
|
||||||
not accessible from Python's side.
|
|
||||||
"""
|
|
||||||
|
|
||||||
task = Gio.Task.new(source_object, cancellable, callback, callback_data)
|
|
||||||
task.set_task_data = decorate_set_task_data(task)(task.set_task_data)
|
|
||||||
task.run_in_thread = decorate_run_in_thread(task)(task.run_in_thread)
|
|
||||||
return task
|
|
||||||
Reference in New Issue
Block a user