From 6edcdb148f910fe27bd80ea87d485420848df205 Mon Sep 17 00:00:00 2001 From: yutent Date: Fri, 18 Aug 2023 19:04:51 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=80=E5=A4=A7=E6=B3=A2=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + main.py | 52 ++- mpd/__init__.py | 9 - mpd/asyncio.py | 593 --------------------------------- ui/ctrl_box.py | 78 +++++ ui/image.py | 48 ++- ui/image_button.py | 25 +- ui/slider.py | 3 +- ui/text.py | 12 +- usr/share/sonist/all_a.png | Bin 3366 -> 0 bytes usr/share/sonist/handler.png | Bin 0 -> 5541 bytes usr/share/sonist/handler_a.png | Bin 0 -> 3820 bytes usr/share/sonist/mute_a.png | Bin 3942 -> 0 bytes usr/share/sonist/next_a.png | Bin 3295 -> 0 bytes usr/share/sonist/pause_a.png | Bin 3682 -> 0 bytes usr/share/sonist/play_a.png | Bin 4083 -> 1773 bytes usr/share/sonist/prev_a.png | Bin 3061 -> 0 bytes usr/share/sonist/rand_a.png | Bin 3229 -> 0 bytes usr/share/sonist/single_a.png | Bin 3084 -> 0 bytes usr/share/sonist/volume_a.png | Bin 3290 -> 0 bytes window.py | 170 ++++++++-- 21 files changed, 325 insertions(+), 666 deletions(-) delete mode 100644 mpd/asyncio.py create mode 100644 ui/ctrl_box.py delete mode 100644 usr/share/sonist/all_a.png create mode 100644 usr/share/sonist/handler.png create mode 100644 usr/share/sonist/handler_a.png delete mode 100644 usr/share/sonist/mute_a.png delete mode 100644 usr/share/sonist/next_a.png delete mode 100644 usr/share/sonist/pause_a.png delete mode 100644 usr/share/sonist/prev_a.png delete mode 100644 usr/share/sonist/rand_a.png delete mode 100644 usr/share/sonist/single_a.png delete mode 100644 usr/share/sonist/volume_a.png diff --git a/.gitignore b/.gitignore index 52cf7cd..ce978af 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ __pycache__ *.txt +album ._* diff --git a/main.py b/main.py index 1859506..7d61670 100755 --- a/main.py +++ b/main.py @@ -1,9 +1,10 @@ #!/usr/bin/env python3 -import gi, sys, os +import gi, sys, os, threading # import dbus # import dbus.service, dbus.mainloop.glib from pprint import pprint as print +import musicbrainzngs as mus gi.require_version('Gtk', '3.0') @@ -17,23 +18,58 @@ from mpd.base import MPDClient app_id = 'fun.wkit.sonist' +def run_async(func): + def wrapper(*args, **kwargs): + thread = threading.Thread(target=func, args=args, kwargs=kwargs) + thread.daemon = True + thread.start() + return thread + return wrapper + class Application(Gtk.Application): def __init__(self): Gtk.Application.__init__(self, application_id = app_id) - self.add_window(SonistWindow()) + self.mpd = MPDClient() + self.mpd.timeout = 10 + self.mpd.connect("localhost", 6600) - self.mpc = MPDClient() - self.mpc.timeout = 10 - self.mpc.connect("localhost", 6600) + self.mpd.ping() + + self.connect('window-removed', self.on_window_removed) + + mus.set_useragent('Sonist Gtk', '0.0.1', 'https://github.com/app-cat/sonist-gtk') + + @run_async + def get_cover(self, song, filepath, callback): + try: + data = mus.search_releases(song["artist"], song["title"], 1) + release_id = data["release-list"][0]["release-group"]["id"] + print(release_id) + buff = mus.get_release_group_image_front(release_id, size = 128) + with open(filepath, 'wb') as file: + output = file.write(buff) + callback(filepath) + except: + pass def do_activate(self): print('hello mpc') self.set_app_menu(None) self.set_menubar(None) - # self.window.show_all() + + self.window = SonistWindow(self) + self.add_window(self.window) + + self.window.show_all() + + + def on_window_removed(self, app, win): + if len(self.get_windows()) == 0: + print('朕要休息了~~~') + @@ -62,8 +98,8 @@ if __name__ == "__main__": app = Application() - app.run(sys.argv) # ApplicationService(app) + app.run(sys.argv) + - Gtk.main() \ No newline at end of file diff --git a/mpd/__init__.py b/mpd/__init__.py index 0897f46..4332922 100644 --- a/mpd/__init__.py +++ b/mpd/__init__.py @@ -27,12 +27,3 @@ from mpd.base import MPDError from mpd.base import PendingCommandError from mpd.base import ProtocolError from mpd.base import VERSION - - -try: - from mpd.twisted import MPDProtocol -except ImportError: - - class MPDProtocol: - def __init__(): - raise "No twisted module found" diff --git a/mpd/asyncio.py b/mpd/asyncio.py deleted file mode 100644 index 0bd1343..0000000 --- a/mpd/asyncio.py +++ /dev/null @@ -1,593 +0,0 @@ -"""Asynchronous access to MPD using the asyncio methods of Python 3. - -Interaction happens over the mpd.asyncio.MPDClient class, whose connect and -command methods are coroutines. - -Some commands (eg. listall) additionally support the asynchronous iteration -(aiter, `async for`) interface; using it allows the library user to obtain -items of result as soon as they arrive. - -The .idle() method works differently here: It is an asynchronous iterator that -produces a list of changed subsystems whenever a new one is available. The -MPDClient object automatically switches in and out of idle mode depending on -which subsystems there is currently interest in. - -Command lists are currently not supported. - - -This module requires Python 3.5.2 or later to run. -""" - -import warnings -import asyncio -from functools import partial -from typing import Optional, List, Tuple, Iterable, Callable, Union - -from mpd.base import HELLO_PREFIX, ERROR_PREFIX, SUCCESS -from mpd.base import MPDClientBase -from mpd.base import MPDClient as SyncMPDClient -from mpd.base import ProtocolError, ConnectionError, CommandError, CommandListError -from mpd.base import mpd_command_provider - - -class BaseCommandResult(asyncio.Future): - """A future that carries its command/args/callback with it for the - convenience of passing it around to the command queue.""" - - def __init__(self, command, args, callback): - super().__init__() - self._command = command - self._args = args - self._callback = callback - - async def _feed_from(self, mpdclient): - while True: - line = await mpdclient._read_line() - self._feed_line(line) - if line is None: - return - - -class CommandResult(BaseCommandResult): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.__spooled_lines = [] - - def _feed_line(self, line): # FIXME just inline? - """Put the given line into the callback machinery, and set the result on a None line.""" - if line is None: - if self.cancelled(): - # Data was still pulled out of the connection, but the original - # requester has cancelled the request -- no need to filter the - # data through the preprocessing callback - pass - else: - self.set_result(self._callback(self.__spooled_lines)) - else: - self.__spooled_lines.append(line) - - def _feed_error(self, error): - if not self.done(): - self.set_exception(error) - else: - # These do occur (especially during the test suite run) when a - # disconnect was already initialized, but the run task being - # cancelled has not ever yielded at all and thus still needs to run - # through to its first await point (which is then in a situation - # where properties it'd like to access are already cleaned up, - # resulting in an AttributeError) - # - # Rather than quenching them here, they are made visible (so that - # other kinds of double errors raise visibly, even though none are - # known right now); instead, the run loop yields initially with a - # sleep(0) that ensures it can be cancelled properly at any time. - raise error - -class BinaryCommandResult(asyncio.Future): - # Unlike the regular commands that defer to any callback that may be - # defined for them, this uses the predefined _read_binary mechanism of the - # mpdclient - async def _feed_from(self, mpdclient): - # Data must be pulled out no matter whether will later be ignored or not - binary = await mpdclient._read_binary() - if self.cancelled(): - pass - else: - self.set_result(binary) - - _feed_error = CommandResult._feed_error - -class CommandResultIterable(BaseCommandResult): - """Variant of CommandResult where the underlying callback is an - asynchronous` generator, and can thus interpret lines as they come along. - - The result can be used with the aiter interface (`async for`). If it is - still used as a future instead, it eventually results in a list. - - Commands used with this CommandResult must use their passed lines not like - an iterable (as in the synchronous implementation), but as a asyncio.Queue. - Furthermore, they must check whether the queue elements are exceptions, and - raise them. - """ - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.__spooled_lines = asyncio.Queue() - - def _feed_line(self, line): - self.__spooled_lines.put_nowait(line) - - _feed_error = _feed_line - - def __await__(self): - asyncio.Task(self.__feed_future()) - return super().__await__() - - __iter__ = __await__ # for 'yield from' style invocation - - async def __feed_future(self): - result = [] - try: - async for r in self: - result.append(r) - except Exception as e: - self.set_exception(e) - else: - if not self.cancelled(): - self.set_result(result) - - def __aiter__(self): - if self.done(): - raise RuntimeError("Command result is already being consumed") - return self._callback(self.__spooled_lines).__aiter__() - - -@mpd_command_provider -class MPDClient(MPDClientBase): - __run_task = None # doubles as indicator for being connected - - #: Indicator of whether there is a pending idle command that was not terminated yet. - # When in doubt; this is True, thus erring at the side of caution (because - # a "noidle" being sent while racing against an incoming idle notification - # does no harm) - __in_idle = False - - #: Indicator that the last attempted idle failed. - # - # When set, IMMEDIATE_COMMAND_TIMEOUT is ignored in favor of waiting until - # *something* else happens, and only then retried. - # - # Note that the only known condition in which this happens is when between - # start of the connection and the presentation of credentials, more than - # IMMEDIATE_COMMAND_TIMEOUT passes. - __idle_failed = False - - #: Seconds after a command's completion to send idle. Setting this too high - # causes "blind spots" in the client's view of the server, setting it too - # low sends needless idle/noidle after commands in quick succession. - IMMEDIATE_COMMAND_TIMEOUT = 0.1 - - #: FIFO list of processors that may consume the read stream one after the - # other - # - # As we don't have any other form of backpressure in the sending side - # (which is not expected to be limited), its limit of COMMAND_QUEUE_LENGTH - # serves as a limit against commands queuing up indefinitely. (It's not - # *directly* throttling output, but as the convention is to put the - # processor on the queue and then send the command, and commands are of - # limited size, this is practically creating backpressure.) - __command_queue = None - - #: Construction size of __command_queue. The default limit is high enough - # that a client can easily send off all existing commands simultaneously - # without needlessly blocking the TCP flow, but small enough that - # freespinning tasks create warnings. - COMMAND_QUEUE_LENGTH = 128 - - #: Callbacks registered by any current callers of `idle()`. - # - # The first argument lists the changes that the caller is interested in - # (and all current listeners' union is used to populate the `idle` - # command's arguments), the latter is an actual callback that will be - # passed either a set of changes or an exception. - __idle_consumers: Optional[List[Tuple[ - Iterable[str], - Callable[[Union[List[str], Exception]], None] - ]]] = None - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.__rfile = self.__wfile = None - - async def connect(self, host, port=6600, loop=None): - if loop is not None: - warnings.warn("loop passed into MPDClient.connect is ignored, this will become an error", DeprecationWarning) - if host.startswith("@"): - host = "\0" + host[1:] - if host.startswith("\0") or "/" in host: - r, w = await asyncio.open_unix_connection(host) - else: - r, w = await asyncio.open_connection(host, port) - self.__rfile, self.__wfile = r, w - - self.__command_queue = asyncio.Queue(maxsize=self.COMMAND_QUEUE_LENGTH) - self.__idle_consumers = [] #: list of (subsystem-list, callbacks) tuples - - try: - helloline = await asyncio.wait_for(self.__readline(), timeout=5) - except asyncio.TimeoutError: - self.disconnect() - raise ConnectionError("No response from server while reading MPD hello") - # FIXME should be reusable w/o reaching in - SyncMPDClient._hello(self, helloline) - - self.__run_task = asyncio.Task(self.__run()) - - @property - def connected(self): - return self.__run_task is not None - - def disconnect(self): - if ( - self.__run_task is not None - ): # is None eg. when connection fails in .connect() - self.__run_task.cancel() - if self.__wfile is not None: - self.__wfile.close() - self.__rfile = self.__wfile = None - self.__run_task = None - self.__command_queue = None - if self.__idle_consumers is not None: - # copying the list as each raising callback will remove itself from __idle_consumers - for subsystems, callback in list(self.__idle_consumers): - callback(ConnectionError()) - self.__idle_consumers = None - - def _get_idle_interests(self): - """Accumulate a set of interests from the current __idle_consumers. - Returns the union of their subscribed subjects, [] if at least one of - them is the empty catch-all set, or None if there are no interests at - all.""" - - if not self.__idle_consumers: - return None - if any(len(s) == 0 for (s, c) in self.__idle_consumers): - return [] - return set.union(*(set(s) for (s, c) in self.__idle_consumers)) - - def _end_idle(self): - """If the main task is currently idling, make it leave idle and process - the next command (if one is present) or just restart idle""" - - if self.__in_idle: - self.__write("noidle\n") - self.__in_idle = False - - async def __run(self): - # See CommandResult._feed_error documentation - await asyncio.sleep(0) - result = None - - try: - while True: - try: - result = await asyncio.wait_for( - self.__command_queue.get(), - timeout=self.IMMEDIATE_COMMAND_TIMEOUT, - ) - except asyncio.TimeoutError: - # The cancellation of the __command_queue.get() that happens - # in this case is intended, and is just what asyncio.Queue - # suggests for "get with timeout". - - if not self.__command_queue.empty(): - # A __command_queue.put() has happened after the - # asyncio.wait_for() timeout but before execution of - # this coroutine resumed. Looping around again will - # fetch the new entry from the queue. - continue - - if self.__idle_failed: - # We could try for a more elaborate path where we now - # await the command queue indefinitely, but as we're - # already in an error case, this whole situation only - # persists until the error is processed somewhere else, - # so ticking once per idle timeout is OK to keep things - # simple. - continue - - subsystems = self._get_idle_interests() - if subsystems is None: - # The presumably most quiet subsystem -- in this case, - # idle is only used to keep the connection alive. - subsystems = ["database"] - - # Careful: There can't be any await points between the - # except and here, or the sequence between the idle and the - # command processor might be wrong. - result = CommandResult("idle", subsystems, self._parse_list) - result.add_done_callback(self.__idle_result) - self.__in_idle = True - self._write_command(result._command, result._args) - - # A new command was issued, so there's a chance that whatever - # made idle fail is now fixed. - self.__idle_failed = False - - try: - await result._feed_from(self) - except CommandError as e: - result._feed_error(e) - # This kind of error we can tolerate without breaking up - # the connection; any other would fly out, be reported - # through the result and terminate the connection - - except Exception as e: - # Prevent the destruction of the pending task in the shutdown - # function -- it's just shutting down by itself. - self.__run_task = None - self.disconnect() - - if result is not None: - # The last command has failed: Forward that result. - # - # (In idle, that's fine too -- everyone watching see a - # nonspecific event). - result._feed_error(e) - return - else: - raise - # Typically this is a bug in mpd.asyncio. - - def __idle_result(self, result): - try: - idle_changes = result.result() - except CommandError as e: - # Don't retry until something changed - self.__idle_failed = True - - # Not raising this any further: The callbacks are notified that - # "something is up" (which is all their API gives), and whichever - # command is issued to act on it will hopefully run into the same - # condition. - # - # This does swallow the exact error cause. - - idle_changes = set() - for subsystems, _ in self.__idle_consumers: - idle_changes = idle_changes.union(subsystems) - - # make generator accessible multiple times - idle_changes = list(idle_changes) - - for subsystems, callback in self.__idle_consumers: - if not subsystems or any(s in subsystems for s in idle_changes): - callback(idle_changes) - - # helper methods - - async def __readline(self): - """Wrapper around .__rfile.readline that handles encoding""" - data = await self.__rfile.readline() - try: - return data.decode("utf8") - except UnicodeDecodeError: - self.disconnect() - raise ProtocolError("Invalid UTF8 received") - - async def _read_chunk(self, length): - try: - return await self.__rfile.readexactly(length) - except asyncio.IncompleteReadError: - raise ConnectionError("Connection lost while reading binary") - - def __write(self, text): - """Wrapper around .__wfile.write that handles encoding.""" - self.__wfile.write(text.encode("utf8")) - - # copied and subtly modifiedstuff from base - - # This is just a wrapper for the below. - def _write_line(self, text): - self.__write(text + "\n") - - # FIXME This code should be shareable. - _write_command = SyncMPDClient._write_command - - async def _read_line(self): - line = await self.__readline() - if not line.endswith("\n"): - raise ConnectionError("Connection lost while reading line") - line = line.rstrip("\n") - if line.startswith(ERROR_PREFIX): - error = line[len(ERROR_PREFIX) :].strip() - raise CommandError(error) - if line == SUCCESS: - return None - return line - - async def _parse_objects_direct(self, lines, delimiters=[], lookup_delimiter=False): - obj = {} - while True: - line = await lines.get() - if isinstance(line, BaseException): - raise line - if line is None: - break - key, value = self._parse_pair(line, separator=": ") - key = key.lower() - if lookup_delimiter and not delimiters: - delimiters = [key] - if obj: - if key in delimiters: - yield obj - obj = {} - elif key in obj: - if not isinstance(obj[key], list): - obj[key] = [obj[key], value] - else: - obj[key].append(value) - continue - obj[key] = value - if obj: - yield obj - - async def _execute_binary(self, command, args): - # Fun fact: By fetching data in lockstep, this is a bit less efficient - # than it could be (which would be "after having received the first - # chunk, guess that the other chunks are of equal size and request at - # several multiples concurrently, ensuring the TCP connection can stay - # full), but at the other hand it leaves the command queue empty so - # that more time critical commands can be executed right away - - data = None - args = list(args) - assert len(args) == 1 - args.append(0) - final_metadata = None - while True: - partial_result = BinaryCommandResult() - await self.__command_queue.put(partial_result) - self._end_idle() - self._write_command(command, args) - metadata = await partial_result - chunk = metadata.pop('binary', None) - - if final_metadata is None: - data = chunk - final_metadata = metadata - if not data: - break - try: - size = int(final_metadata['size']) - except KeyError: - size = len(chunk) - except ValueError: - raise CommandError("Size data unsuitable for binary transfer") - else: - if metadata != final_metadata: - raise CommandError("Metadata of binary data changed during transfer") - if chunk is None: - raise CommandError("Binary field vanished changed during transfer") - data += chunk - args[-1] = len(data) - if len(data) > size: - raise CommandListError("Binary data announced size exceeded") - elif len(data) == size: - break - - if data is not None: - final_metadata['binary'] = data - - final_metadata.pop('size', None) - - return final_metadata - - # omits _read_chunk checking because the async version already - # raises; otherwise it's just awaits sprinkled in - async def _read_binary(self): - obj = {} - - while True: - line = await self._read_line() - if line is None: - break - - key, value = self._parse_pair(line, ": ") - - if key == "binary": - chunk_size = int(value) - value = await self._read_chunk(chunk_size) - - if await self.__rfile.readexactly(1) != b"\n": - # newline after binary content - self.disconnect() - raise ConnectionError("Connection lost while reading line") - - obj[key] = value - return obj - - # command provider interface - @classmethod - def add_command(cls, name, callback): - if callback.mpd_commands_binary: - async def f(self, *args): - result = await self._execute_binary(name, args) - - # With binary, the callback is applied to the final result - # rather than to the iterator over the lines (cf. - # MPDClient._execute_binary) - return callback(self, result) - else: - command_class = ( - CommandResultIterable if callback.mpd_commands_direct else CommandResult - ) - if hasattr(cls, name): - # Idle and noidle are explicitly implemented, skipping them. - return - - def f(self, *args): - result = command_class(name, args, partial(callback, self)) - if self.__run_task is None: - raise ConnectionError("Can not send command to disconnected client") - - try: - self.__command_queue.put_nowait(result) - except asyncio.QueueFull as e: - e.args = ("Command queue overflowing; this indicates the" - " application sending commands in an uncontrolled" - " fashion without awaiting them, and typically" - " indicates a memory leak.",) - # While we *could* indicate to the queued result that it has - # yet to send its request, that'd practically create a queue of - # awaited items in the user application that's growing - # unlimitedly, eliminating any chance of timely responses. - # Furthermore, the author sees no practical use case that's not - # violating MPD's guidance of "Do not manage a client-side copy - # of MPD's database". If a use case *does* come up, any change - # would need to maintain the property of providing backpressure - # information. That would require an API change. - raise - - self._end_idle() - # Careful: There can't be any await points between the queue - # appending and the write - try: - self._write_command(result._command, result._args) - except BaseException as e: - self.disconnect() - result.set_exception(e) - return result - - escaped_name = name.replace(" ", "_") - f.__name__ = escaped_name - setattr(cls, escaped_name, f) - - # commands that just work differently - async def idle(self, subsystems=()): - if self.__idle_consumers is None: - raise ConnectionError("Can not start idle on a disconnected client") - - interests_before = self._get_idle_interests() - # A queue accepting either a list of things that changed in a single - # idle cycle, or an exception to be raised - changes = asyncio.Queue() - try: - entry = (subsystems, changes.put_nowait) - self.__idle_consumers.append(entry) - if self._get_idle_interests != interests_before: - # Technically this does not enter idle *immediately* but rather - # only after any commands after IMMEDIATE_COMMAND_TIMEOUT; - # practically that should be a good thing. - self._end_idle() - while True: - item = await changes.get() - if isinstance(item, Exception): - raise item - yield item - finally: - if self.__idle_consumers is not None: - self.__idle_consumers.remove(entry) - - def noidle(self): - raise AttributeError("noidle is not supported / required in mpd.asyncio") diff --git a/ui/ctrl_box.py b/ui/ctrl_box.py new file mode 100644 index 0000000..97e4238 --- /dev/null +++ b/ui/ctrl_box.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 + +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, GObject + +from .image_button import ImageButton + +class CtrlBox(Gtk.Box): + + __gsignals__ = { + 'clicked': (GObject.SIGNAL_RUN_FIRST, None, (str,)) + } + + def __init__(self, spacing = 6): + Gtk.Box.__init__(self, spacing = spacing) + + self.modes = ['./usr/share/sonist/all.png','./usr/share/sonist/rand.png','./usr/share/sonist/single.png'] + self.curr_mode = 0 + + self.mode_btn = ImageButton('./usr/share/sonist/all.png') + self.prev_btn = ImageButton('./usr/share/sonist/prev.png') + self.play_btn = ImageButton('./usr/share/sonist/pause.png', 48, 48) + self.next_btn = ImageButton('./usr/share/sonist/next.png') + self.vol_btn = ImageButton('./usr/share/sonist/volume.png') + + self.pack_start(self.mode_btn, False, False, 0) + self.pack_start(self.prev_btn, False, False, 0) + self.pack_start(self.play_btn, False, False, 0) + self.pack_start(self.next_btn, False, False, 0) + self.pack_start(self.vol_btn, False, False, 0) + + + self.mode_btn.connect('clicked', self.on_btn_clicked) + self.prev_btn.connect('clicked', self.on_btn_clicked) + self.play_btn.connect('clicked', self.on_btn_clicked) + self.next_btn.connect('clicked', self.on_btn_clicked) + self.vol_btn.connect('clicked', self.on_btn_clicked) + + + def on_btn_clicked(self, btn): + + if btn == self.play_btn: + self.emit('clicked', 'play_btn') + + elif btn == self.mode_btn: + self.curr_mode += 1 + if self.curr_mode > 2: + self.curr_mode = 0 + self.mode_btn.set_image(self.modes[self.curr_mode]) + self.emit('clicked', 'mode_btn') + + elif btn == self.prev_btn: + self.emit('clicked', 'prev_btn') + + elif btn == self.next_btn: + self.emit('clicked', 'next_btn') + + elif btn == self.vol_btn: + self.emit('clicked','vol_btn') + + + def toggle_play_btn(self, on = True): + if on: + self.play_btn.set_image('./usr/share/sonist/play_a.png') + else: + self.play_btn.set_image('./usr/share/sonist/pause.png') + + + def toggle_mode_btn(self, mode = 'single'): + if mode == 'single': + self.curr_mode = 2 + elif mode == 'random': + self.curr_mode = 1 + else: + self.curr_mode = 0 + + self.mode_btn.set_image(self.modes[self.curr_mode]) diff --git a/ui/image.py b/ui/image.py index 051ebe0..b3b67f1 100644 --- a/ui/image.py +++ b/ui/image.py @@ -8,15 +8,24 @@ from gi.repository import Gtk, Gdk, GdkPixbuf class ScaleImage(Gtk.Image): def __init__(self, filepath): Gtk.Image.__init__(self) + self.width = None + self.height = None + self.reset(filepath) + + def reset(self, filepath): self.origin = GdkPixbuf.Pixbuf.new_from_file(filepath) - self.pixbuf = self.origin - self.width = self.origin.get_width() - self.height = self.origin.get_height() + if self.width is None: + self.pixbuf = self.origin - self.set_from_pixbuf(self.origin) + self.width = self.origin.get_width() + self.height = self.origin.get_height() + else: + self.pixbuf = self.origin.scale_simple(self.width, self.height, GdkPixbuf.InterpType.BILINEAR) + self.set_from_pixbuf(self.pixbuf) + return self def resize(self, width, height): @@ -24,6 +33,7 @@ class ScaleImage(Gtk.Image): self.height = height self.pixbuf = self.origin.scale_simple(width, height, GdkPixbuf.InterpType.BILINEAR) self.set_from_pixbuf(self.pixbuf) + return self def set_radius(self, radius = 0): @@ -34,19 +44,19 @@ class ScaleImage(Gtk.Image): Gdk.cairo_set_source_pixbuf(ctx, self.pixbuf, 0, 0) # 左上角 圆角 - ctx.arc(radius, radius, radius, -math.pi, -math.pi / 2.) + ctx.arc(radius, radius, radius, -math.pi, -math.pi / 2) ctx.line_to(w - radius, 0) # 右上角 圆角 - ctx.arc(w - radius, radius, radius, -math.pi / 2., 0) + ctx.arc(w - radius, radius, radius, -math.pi / 2, 0) ctx.line_to(w, -radius) # 右下角 圆角 - ctx.arc(w - radius, h - radius, radius, 0, math.pi / 2.) + ctx.arc(w - radius, h - radius, radius, 0, math.pi / 2) ctx.line_to(radius, h) # 左下角 圆角 - ctx.arc(radius, h - radius, radius, math.pi / 2., math.pi) + ctx.arc(radius, h - radius, radius, math.pi / 2, math.pi) ctx.close_path() ctx.clip() @@ -54,5 +64,27 @@ class ScaleImage(Gtk.Image): pixbuf = Gdk.pixbuf_get_from_surface(surface, 0, 0, w, h) self.set_from_pixbuf(pixbuf) + return self + # 旋转不生效 + def rotate(self, deg = 0, point = None): + w = self.width + h = self.height + surface = cairo.ImageSurface(cairo.Format.ARGB32, w, h) + ctx = cairo.Context(surface) + Gdk.cairo_set_source_pixbuf(ctx, self.pixbuf, 0, 0) + + # 圆心坐标 + if point is None: + point = (w / 2, h / 2) + + ctx.translate(point[0], point[1]) + ctx.rotate((deg / 180) * math.pi) + + ctx.paint() + + pixbuf = Gdk.pixbuf_get_from_surface(surface, 0, 0, w, h) + + self.set_from_pixbuf(pixbuf) + return self \ No newline at end of file diff --git a/ui/image_button.py b/ui/image_button.py index ff04074..488fcca 100644 --- a/ui/image_button.py +++ b/ui/image_button.py @@ -10,28 +10,45 @@ class ImageButton(Gtk.Button): def __init__(self, filepath, width = 26, height = 26): Gtk.Button.__init__(self) + self.width = width + self.height = height + self._image_path = None + self.set_name('ImageButton') self.set_size_request(width, height) - image = ScaleImage(filepath) - image.resize(width, height) + self.set_valign(Gtk.Align.CENTER) # 针对macos的设置, 但只解决了普通状态下的边框问题, 鼠标经过的样式还在 self.set_relief(Gtk.ReliefStyle.NONE) - self.set_image(image) + self.set_image(filepath) css_provider = Gtk.CssProvider() style = f""" - #ImageButton, #ImageButton:hover {{ + #ImageButton {{ border: 0; + border-radius: 50%; background-color: transparent; border-color:transparent; outline: transparent; }} + #ImageButton:hover {{ + background-color: rgba(255,255,255,.1); + }} """ css_provider.load_from_data(style.encode('UTF-8')) context = self.get_style_context() path = context.get_path() context.add_provider(css_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER) + + def set_image(self, filepath): + if self._image_path == filepath: + return + + self._image_path = filepath + image = ScaleImage(filepath) + image.resize(self.width, self.height) + Gtk.Button.set_image(self, image) + return self \ No newline at end of file diff --git a/ui/slider.py b/ui/slider.py index 75179d9..e28ab22 100644 --- a/ui/slider.py +++ b/ui/slider.py @@ -28,7 +28,8 @@ class Slider(Gtk.Scale): background-color: rgba(163, 190, 140, 0.75); }} #Slider slider {{ - background-color: rgba(163, 190, 140, 0.75); + background-color: transparent; + border-color: transparent; outline: none; }} """ diff --git a/ui/text.py b/ui/text.py index 1c861d4..9e7277f 100644 --- a/ui/text.py +++ b/ui/text.py @@ -5,19 +5,17 @@ gi.require_version('Gtk', '3.0') from gi.repository import Gtk -class TextBox(Gtk.EventBox): +class TextBox(Gtk.Box): def __init__(self, width, height): - Gtk.EventBox.__init__(self) + Gtk.Box.__init__(self) self.set_size_request(width, height) self.label = Gtk.Label() - - align = Gtk.Alignment(xalign=0.5, yalign=0.5) - self.add(self.label) - self.add(align) + def set_text(self, string): - self.label.set_text('孙晓 - 丹歌惊鸿') \ No newline at end of file + self.label.set_text('孙晓 - 丹歌惊鸿') + return self \ No newline at end of file diff --git a/usr/share/sonist/all_a.png b/usr/share/sonist/all_a.png deleted file mode 100644 index 402dd6ebbe12700fa2bf3f85afcacb1bf397a803..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3366 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 J50@Rn{|{k~)r$ZC literal 0 HcmV?d00001 diff --git a/usr/share/sonist/handler_a.png b/usr/share/sonist/handler_a.png new file mode 100644 index 0000000000000000000000000000000000000000..7feee91e8650ffd039801157f47a601dcb200f88 GIT binary patch literal 3820 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 literal 0 HcmV?d00001 diff --git a/usr/share/sonist/mute_a.png b/usr/share/sonist/mute_a.png deleted file mode 100644 index 02630dcbaf1ec46e50f14667dc94f89394e4ccee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3942 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 F_z(T~{2u@S diff --git a/usr/share/sonist/pause_a.png b/usr/share/sonist/pause_a.png deleted file mode 100644 index 5404e159ae44a45ca161b04b8fb2529d5dce9d8f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3682 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 literal 4083 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 Vg&ol&vd?>nhl>y8v~$#se*=jVbHD%q diff --git a/usr/share/sonist/prev_a.png b/usr/share/sonist/prev_a.png deleted file mode 100644 index f23365cc84efe8ec954eac4e932fde4ec175ed54..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3061 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 Jb970R>Mvs+>X`rl diff --git a/usr/share/sonist/volume_a.png b/usr/share/sonist/volume_a.png deleted file mode 100644 index 5c18e57e8e5ac91c3536cc6dd1843cb55a54e270..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3290 zcmbVP2~bn#7LHUFi&l_LR4@dwM#v7?G%OMdse~o$3Sx3|fj~$?5+EQ#l`5z}TUte2 z0E-0?#3GeNHU&YdRP=$$LzG1!q7W7lDNyVU0@J>+eVu0J=KjlfzVn^)pa0C16%)%8*@GcGr&uxQ z5$oa!#D)PBF2cqdzFkIz2*N=r8!ijy3nWw-4KdG4g~rNd3<5q6k%rL_c1nkEU#1(} zUML3PR%jv$z*&;vBnlcww8RpK7H~WkN5Npp7#snG#Zj?Dsuc;o@Iye}#M~e%i~iYy zFK9+Xgh-_#Dh3lB9gU7ApoQXK430v9IPe%e9t9y#k{E%MEkg+;ri%=8Py&c~A}LQO zfGZi<9ATuChJcj54SDq|!EjI@ zl|WeB5>^x@P z6b^^N5f$|2A`Fs7v&EoYFqEx-P&EO_usMD1fNNPg1u!#^zh=y)jHOSkLd4PR3G9Ry?j#e3=c*IK4-Nzn2ZslrduQq$mk zm&A*GS}xz;T3PoR-8!CU@8RJQdacd(g@Npvho+}3%5B+ITh(?IJ+}``8YIIx1y6RC zs(G8WX5W0)@z~xbsZVEZqnXTd@UkTesC-weW%@$oe>eeHZ(RR?T$EI6oxCFD8M~&dY8{pB{Pk;^`hM&`l+^C$<`obY+)n^}8~jn2>XmYU@&hIUi-UwAZOe zZ*F~&X{TYC@@n+3^OMchE8ZS0c+lHS*)Fep-%R#+VivgpGG?` z8olwziJX$<&5|VktGD70Xr7;Zu+@ZRM#u?N2{<`x`1$SCJzTxiikP0=6>HVg$M4q< z?D8s0jMuPCyBswA^svWRxApeIl{!=Bq|>!b=Nqh+a$9-dE0)1P0$C>^TQ2wPZ<`7e zKe=vjD8SnZ8CX<6OMBZ}IqqO|TtTlAeWF0VPO*NiVZFI*R?pXzUgdk^r&hfS1^dYY zoykTMy*)2h^wQ@lcAVZSO=)QL3Xx=JT>w(Dl16$r#-tw2>j)~b^46Ar(ZNY@JIBqS5TP=Xf zr)ANp^<8)Q}@RiTRKIU;qjR_8KIlE3g_h0d#FgsllqF$oAe0s;gwM+gV&*<_jW*Kd5 z4sE_eDrXUqdU>LlpQA?7T}qOE%zvDE%r8f~_ot}O>Z1zo``%=*d|nE)O->$f+Yaamc%r1ZO?>xOp#B$D^%?7aX%9fL9)sk-=RYUEdAT}4{hU7C2Emot~qsFgIl-);i8(!&&5= zstA5wbuGw`-ji63B&Kcq($=xBDQ^m)O(wtTI$~wXYo>8oF zH6>gS8i-aIU8OBV?z=v?rsR&l=lI;ku6Fy=txgSBjK1Bw+dcRDfT5injqd!;8wbIu zqLV7F>Mpw4&38v?c5nC@xhdKp%NHHH zSvJ)=ZYbK)-+rQVV0V+T?%2nt5clEWO zUzQqIWfwO+<2;%6$x|31FkYK!ZJMjT*;;3iAN;aB*;3MPO8#xKadSipIpAUMrKtjP zJl-Nb-pIIZTj3$UG0nEG-p#GM{Oz-^2XI>ghkocQG|v9cRbPJ$#qo@M(ZAt-GI>+% zAt(Fjle(G{ezb{SbrUBB0@J#84j1H(GI-bHl0?+PA6XrQN15omE)mvZmp<&2=++8O z`_O6kI`!`BLi5S;&I!||eT}-?)VdX8M)@ULV>yIIg*e~$v_eas_vp>NN1sH%LcelS z%i5Y&*7Dfy*rB89;q|SJ>Zd%dy$Ii)<*Yl4&XZSX+ZtYcH