Ejemplo n.º 1
0
 def connect_to_signal(signal: pyqtSignal, callbacks):
     if not callbacks:
         return
     if callable(callbacks):
         callbacks = [callbacks]
     for callback in callbacks:
         signal.connect(callback)
Ejemplo n.º 2
0
def _chunked_download(
    filename: str, response: HTTPResponse, size: int, progress_signal: pyqtSignal
) -> None:
    """
    Downloads the zip file and emits progress.

    :param response: the HTTP response to download the file from
    :param size: the size of the download, in bytes; used for progress
    :param progress_signal: the PyQt signal with which to emit download progress
    """
    if not size:
        size = 10 ** 6 * 44

    with open(filename, "wb") as f:
        bytes_downloaded = 0
        block_size = 1024 * 10

        while True:
            buffer = response.read(block_size)
            if not buffer:
                break

            f.write(buffer)
            bytes_downloaded += block_size

            progress_signal.emit(bytes_downloaded / size * 100)
Ejemplo n.º 3
0
def connect(signal: pyqtSignal, callback: Callable):
    """
    By default calling ``signal.connect(callback)`` will dispose of context information.

    Practically, this leads to single line tracebacks when ``signal.emit()`` is invoked.
    This is very hard to debug.

    This function wraps the ``connect()`` call to give you additional traceback information, if the callback does crash.

    :param signal: the signal to ``connect()`` to.
    :param callback: the callback to connect (will be called after ``signal.emit(...)``.
    """

    # Step 1: At time of calling this function: get the stack frames.
    #         We reconstruct the stack as a ``traceback`` object.
    source = None
    for frame in list(inspect.stack())[1:]:
        source = types.TracebackType(source, frame.frame, frame.index or 0,
                                     frame.lineno)

    # Step 2: construct a lightweight StackSummary object which does not contain
    # actual frames or locals, to avoid memory leak
    try:
        summary = traceback.StackSummary.extract(traceback.walk_tb(source),
                                                 capture_locals=False)
    finally:
        del source

    # Step 3: Wrap the ``callback`` and inject our creation stack if an error occurs.
    #         The BaseException instead of Exception is intentional: this makes sure interrupts of infinite loops show
    #         the source callback stack for debugging.
    def trackback_wrapper(*args, **kwargs):
        try:
            callback(*args, **kwargs)
        except BaseException as exc:
            traceback_str = '\n' + ''.join(summary.format())
            raise exc from CreationTraceback(traceback_str)

    try:
        setattr(callback, "tb_wrapper", trackback_wrapper)
    except AttributeError:
        # This is not a free function, but either an external library or a method bound to an instance.
        if not hasattr(callback, "tb_wrapper") and hasattr(
                callback, "__self__") and hasattr(callback, "__func__"):
            # methods are finicky: you can't set attributes on them.
            # Instead, we inject the handlers for each method in a dictionary on the instance.
            bound_obj = callback.__self__
            if not hasattr(bound_obj, "tb_mapping"):
                setattr(bound_obj, "tb_mapping", {})
            bound_obj.tb_mapping[
                callback.__func__.__name__] = trackback_wrapper
        else:
            logging.warning(
                "Unable to hook up connect() info to %s. Probably a 'builtin_function_or_method'.",
                repr(callback))

    # Step 3: Connect our wrapper to the signal.
    signal.connect(trackback_wrapper)
def task(signal: QtCore.pyqtSignal):
    """
    多线程任务函数
    :param signal: Qt信号
    :return: None
    """
    while True:
        time.sleep(1)
        signal.emit(json.dumps(time.time()))
Ejemplo n.º 5
0
def _mock_download(progress_signal: pyqtSignal) -> None:
    """
    Pretends to download a zip file. Used for testing when the zip file is
    already stored in the current working directory.
    """
    counter = 0
    while counter <= 100:
        progress_signal.emit(counter)
        counter += 1
        time.sleep(2 / 100)
Ejemplo n.º 6
0
def buffered_send(stream: Iterable[object], signal: pyqtSignal, size=2000):
    buff = []
    i = 0
    for data in stream:
        buff.append(data)
        i += 1
        if i == size:
            signal.emit(buff)
            i = 0
            buff = []
    if i > 0:
        signal.emit(buff)
Ejemplo n.º 7
0
def download_images(signal: pyqtSignal, core: LegendaryCore):
    if not os.path.isdir(IMAGE_DIR):
        os.makedirs(IMAGE_DIR)
        logger.info("Create Image dir")

    # Download Images
    for i, game in enumerate(
            sorted(core.get_game_list(), key=lambda x: x.app_title)):

        try:
            download_image(game)
        except json.decoder.JSONDecodeError:
            shutil.rmtree(f"{IMAGE_DIR}/{game.app_name}")
            download_image(game)
        signal.emit(i)
Ejemplo n.º 8
0
def disconnect(signal: pyqtSignal, callback: Callable):
    """
    After using ``connect()`` to link a signal, use this function to disconnect from the given signal.

    This function will also work if the ``callback`` was connected directly with ``signal.connect()``.

    :param signal: the signal to ``disconnect()`` from.
    :param callback: the callback to connect (will be called after ``signal.emit(...)``.
    """
    if hasattr(callback, 'tb_wrapper'):
        disconnectable = callback.tb_wrapper
    elif hasattr(callback, "__self__") and hasattr(callback, "__func__"):
        disconnectable = callback.__self__.tb_mapping[callback.__func__.__name__]
    else:
        disconnectable = callback
    signal.disconnect(disconnectable)
Ejemplo n.º 9
0
 def __init__(self,
              conditions: List[SelectionCondition],
              control: QWidget,
              signal: pyqtSignal,
              action: Callable,
              activate_action: Callable = None,
              deactivate_action: Callable = None,
              model_change_handler: Callable = None):
     self.__conditions = conditions
     self.__control = control
     self.__action = action
     self.__activate_action = activate_action
     self.__deactivate_action = deactivate_action
     self.__model_change_handler = model_change_handler
     signal.connect(lambda *args: action(self._selection, *args))
     self.update_selection([])
Ejemplo n.º 10
0
    def __init__(self, settings: Settings, db: DB, on_create: Callable,
                 on_date_click: Callable, tasks_changed_event: qtc.pyqtSignal,
                 factory: TaskWidgetFactory):
        super().__init__()
        tasks_changed_event.connect(self._redraw_tasks)

        self._db = db
        self._factory = factory
        self._settings = settings

        main_layout = qtw.QVBoxLayout()
        self.setLayout(main_layout)

        self._current_date = date.today()
        self._date_button = qtw.QPushButton(clicked=on_date_click)
        font = self._date_button.font()
        font.setPixelSize(20)
        self._date_button.setFont(font)
        self._update_date_label()

        tasks_scroll_area = qtw.QScrollArea(widgetResizable=True)
        tasks_scroll_area_content = qtw.QWidget()
        tasks_scroll_area.setWidget(tasks_scroll_area_content)

        self._tasks_layout = qtw.QVBoxLayout(tasks_scroll_area_content)
        self._tasks_layout.setAlignment(qtc.Qt.AlignTop)

        self._prev_day_button = qtw.QPushButton(
            text=settings.PREVIOUS_DAY_BUTTON_TEXT,
            clicked=lambda: self._change_day(-1))

        self._create_task_button = qtw.QPushButton(
            text=settings.CREATE_TASK_BUTTON_TEXT, clicked=on_create)

        self._next_day_button = qtw.QPushButton(
            text=settings.NEXT_DAY_BUTTON_TEXT,
            clicked=lambda: self._change_day(1))

        buttons_layout = qtw.QHBoxLayout()
        buttons_layout.addWidget(self._prev_day_button)
        buttons_layout.addWidget(self._create_task_button)
        buttons_layout.addWidget(self._next_day_button)

        main_layout.addWidget(self._date_button)
        main_layout.addWidget(tasks_scroll_area)
        main_layout.addLayout(buttons_layout)
Ejemplo n.º 11
0
def is_connected(signal: pyqtSignal, slot: pyqtSlot) -> bool:
    """
    Determine if a (bound) signal is connected to a slot.
    :param signal: signal (a return value from a call to pyqtSignal(...))
    :param slot: slot (a method on a QObject, decorated with pyqtSlot)
    :return: True if connected, False otherwise
    """
    try:
        signal.connect(slot, Qt.UniqueConnection)

    except TypeError as exc:
        assert str(exc) in (
            'connection is not unique',
            'connect() failed between MyObj.sig_test[] and meth()')
        return True

    else:
        signal.disconnect(slot)
        return False
Ejemplo n.º 12
0
def chunked_download(
    filename: str,
    response: HTTPResponse,
    size: int = None,
    progress_signal: pyqtSignal = None,
) -> None:
    """
    Downloads the zip file and emits progress.

    :param response: the HTTP response to download the file from
    :param size: the size of the download, in bytes; used for progress
    :param progress_signal: the PyQt signal with which to emit download progress
    """
    if progress_signal is None:
        logging.warning(f"Progress signal is None.")

    if not size:
        length = response.headers.get("content-length")
        if length:
            length = int(length)
            logging.info(f"Download size is {length / 1024 ** 2:.1f}MB.")

            size = length
        else:
            size = 10**6 * 44
            logging.warning(f"Guessing download size.")

    with open(filename, "wb") as f:
        bytes_downloaded = 0
        block_size = 1024 * 10

        while True:
            buffer = response.read(block_size)
            if not buffer:
                break

            f.write(buffer)
            bytes_downloaded += block_size

            if progress_signal:
                progress_signal.emit(bytes_downloaded / size * 100)
Ejemplo n.º 13
0
    def _notifying_setter(
        self, 
        attr_name: str, 
        value: Any, 
        on_set: pyqtSignal, 
        on_unset: pyqtSignal,
        send_value: bool = False
    ):
        """ Utility for a setter that emits Qt signals when the attribute in 
        question changes state.

        Parameters
        ----------
        attr_name :
            identifies attribute to be set
        value : 
            set attribute to this value
        on_set : 
            emitted when the new value is not None
        on_unset :
            emitted when the new value is None
        send_value : 
            if True, the new value will be included in the emitted signal

        """
        setattr(self, attr_name, value)

        if value is None:
            on_unset.emit()
        else:
            if send_value:
                on_set.emit(value)
            else:
                on_set.emit()
Ejemplo n.º 14
0
    def _obtain_transaction_data(self, format: TxSerialisationFormat,
                                 completion_signal: Optional[pyqtSignal],
                                 done_signal: pyqtSignal,
                                 completion_text: str) -> None:
        tx_data = self.tx.to_format(format)
        if not isinstance(tx_data, dict):
            # This is not ideal, but it covers both bases.
            if completion_signal is not None:
                completion_signal.emit(format, tx_data)
            done_signal.emit(format, tx_data)
            return

        steps = self._account.estimate_extend_serialised_transaction_steps(
            format, self.tx, tx_data)

        # The done callbacks should happen in the context of the GUI thread.
        def on_done(weakwindow: 'ElectrumWindow',
                    future: concurrent.futures.Future) -> None:
            nonlocal format, done_signal
            try:
                data = future.result()
            except concurrent.futures.CancelledError:
                done_signal.emit(format, None)
            except Exception as exc:
                weakwindow.on_exception(exc)
            else:
                done_signal.emit(format, data)

        title = _("Obtaining transaction data")
        func = partial(self._obtain_transaction_data_worker, format, tx_data,
                       completion_signal, completion_text)
        weakwindow = weakref.proxy(self._main_window)
        WaitingDialog(self,
                      title,
                      func,
                      on_done=partial(on_done, weakwindow),
                      progress_steps=steps,
                      allow_cancel=True,
                      close_delay=5)
Ejemplo n.º 15
0
def downloadFile(name, url, signals: pyqtSignal = None):
    headers = {'Proxy-Connection': 'keep-alive'}
    r = requests.get(url, stream=True, headers=headers)
    length = float(r.headers['content-length'])
    f = open(name, 'wb')
    count = 0
    count_tmp = 0
    time1 = time.time()
    for chunk in r.iter_content(chunk_size=512):
        if chunk:
            f.write(chunk)
            count += len(chunk)
            if time.time() - time1 > 1:
                p = count / length * 100
                speed = (count - count_tmp) / 1024 / 1024 / 2
                count_tmp = count
                Logger.println(f'已下载 {formatFloat(p)}% (下载速度:{formatFloat(speed)}M/s)')
                time1 = time.time()
                if signals:
                    signals.emit(f'已下载 {formatFloat(p)}% (下载速度:{formatFloat(speed)}M/s)')
    signals.emit(f'已下载 {formatFloat(100)}%')
    f.close()
Ejemplo n.º 16
0
def download_images(signal: pyqtSignal, core: LegendaryCore):
    if not os.path.isdir(IMAGE_DIR):
        os.makedirs(IMAGE_DIR)
        logger.info("Create Image dir")

    # Download Images
    for i, game in enumerate(
            sorted(core.get_game_list(), key=lambda x: x.app_title)):

        if not os.path.isdir(f"{IMAGE_DIR}/" + game.app_name):
            os.mkdir(f"{IMAGE_DIR}/" + game.app_name)

        if not os.path.isfile(f"{IMAGE_DIR}/{game.app_name}/image.json"):
            json_data = {"DieselGameBoxTall": None, "DieselGameBoxLogo": None}
        else:
            json_data = json.load(
                open(f"{IMAGE_DIR}/{game.app_name}/image.json", "r"))

        for image in game.metadata["keyImages"]:
            if image["type"] == "DieselGameBoxTall" or image[
                    "type"] == "DieselGameBoxLogo":

                if json_data[
                        image["type"]] != image["md5"] or not os.path.isfile(
                            f"{IMAGE_DIR}/{game.app_name}/{image['type']}.png"
                        ):
                    # Download
                    json_data[image["type"]] = image["md5"]
                    # os.remove(f"{IMAGE_DIR}/{game.app_name}/{image['type']}.png")
                    json.dump(
                        json_data,
                        open(f"{IMAGE_DIR}/{game.app_name}/image.json", "w"))
                    logger.info(f"Download Image for Game: {game.app_title}")
                    url = image["url"]
                    with open(
                            f"{IMAGE_DIR}/{game.app_name}/{image['type']}.png",
                            "wb") as f:
                        f.write(requests.get(url).content)
                        f.close()

        if not os.path.isfile(f'{IMAGE_DIR}/' + game.app_name +
                              '/UninstalledArt.png'):

            if os.path.isfile(f'{IMAGE_DIR}/' + game.app_name +
                              '/DieselGameBoxTall.png'):
                # finalArt = Image.open(f'{IMAGE_DIR}/' + game.app_name + '/DieselGameBoxTall.png')
                # finalArt.save(f'{IMAGE_DIR}/{game.app_name}/FinalArt.png')
                # And same with the grayscale one

                bg = Image.open(
                    f"{IMAGE_DIR}/{game.app_name}/DieselGameBoxTall.png")
                uninstalledArt = bg.convert('L')
                uninstalledArt.save(
                    f'{IMAGE_DIR}/{game.app_name}/UninstalledArt.png')
            elif os.path.isfile(
                    f"{IMAGE_DIR}/{game.app_name}/DieselGameBoxLogo.png"):
                bg: Image.Image = Image.open(
                    f"{IMAGE_DIR}/{game.app_name}/DieselGameBoxLogo.png")
                bg = bg.resize((int(bg.size[1] * 3 / 4), bg.size[1]))
                logo = Image.open(
                    f'{IMAGE_DIR}/{game.app_name}/DieselGameBoxLogo.png'
                ).convert('RGBA')
                wpercent = ((bg.size[0] * (3 / 4)) / float(logo.size[0]))
                hsize = int((float(logo.size[1]) * float(wpercent)))
                logo = logo.resize((int(bg.size[0] * (3 / 4)), hsize),
                                   Image.ANTIALIAS)
                # Calculate where the image has to be placed
                pasteX = int((bg.size[0] - logo.size[0]) / 2)
                pasteY = int((bg.size[1] - logo.size[1]) / 2)
                # And finally copy the background and paste in the image
                # finalArt = bg.copy()
                # finalArt.paste(logo, (pasteX, pasteY), logo)
                # Write out the file
                # finalArt.save(f'{IMAGE_DIR}/' + game.app_name + '/FinalArt.png')
                logoCopy = logo.copy()
                logoCopy.putalpha(int(256 * 3 / 4))
                logo.paste(logoCopy, logo)
                uninstalledArt = bg.copy()
                uninstalledArt.paste(logo, (pasteX, pasteY), logo)
                uninstalledArt = uninstalledArt.convert('L')
                uninstalledArt.save(f'{IMAGE_DIR}/' + game.app_name +
                                    '/UninstalledArt.png')
            else:
                logger.warning(
                    f"File {IMAGE_DIR}/{game.app_name}/DieselGameBoxTall.png dowsn't exist"
                )
        signal.emit(i)
Ejemplo n.º 17
0
def disconnect_lambda_slots(*, signal: pyqtSignal) -> None:
    # ignore first run - when no slots connected
    with contextlib.suppress(TypeError):
        signal.disconnect()
Ejemplo n.º 18
0
    def __init__(self, text : str, newWindow_signal:QtCore.pyqtSignal, menuIndex : int, parent=None):
        super(Qtw.QPushButton, self).__init__(text, parent, cursor=QtCore.Qt.PointingHandCursor, toolTip='Change current editor', clicked=lambda: newWindow_signal.emit(menuIndex))
        self.newWindow_signal = newWindow_signal
        self.menuIndex = menuIndex

        self.setSizePolicy(Qtw.QSizePolicy.Expanding, Qtw.QSizePolicy.Preferred)
        self.setObjectName('Custom_Window_Button')
        self.setStyleSheet(self.stylesheet)
Ejemplo n.º 19
0
 def worker(widget: QtWidgets.QWidget, trigger: QtCore.pyqtSignal) -> None:
     while True:
         widget.refresh()
         trigger.emit(widget.render)
         if widget.delay > 0:
             time.sleep(widget.delay)