class SerialPortSelector(QWidget): open_port = pyqtSignal(str, int) close_port = pyqtSignal() def __init__(self, *args): super(SerialPortSelector, self).__init__(*args) self.disabled = False self.init_ui() self.add_ports() def init_ui(self): layout = QHBoxLayout() self.setLayout(layout) self.ports_list_combobox = QComboBox() layout.addWidget(self.ports_list_combobox) self.baud_rate_combobox = QComboBox() self.baud_rate_combobox.addItems([ '300', '600', '1200', '2400', '4800', '9600', '19200', '38400', '43000', '56000', '57600', '115200' ]) self.baud_rate_combobox.setCurrentText('115200') self.baud_rate_combobox.setEditable(True) layout.addWidget(self.baud_rate_combobox) self.open_btn = QPushButton('打开') self.open_btn.clicked.connect(self.handle_open_port) layout.addWidget(self.open_btn) self.refresh_btn = QPushButton('刷新') self.refresh_btn.clicked.connect(self.add_ports) layout.addWidget(self.refresh_btn) def add_ports(self): self.ports_list_combobox.clear() for port in comports(False): self.ports_list_combobox.addItem(port.name, port) def handle_open_port(self): if self.disabled: self.close_port.emit() else: port = self.ports_list_combobox.currentText() if port == "": return baud_rate = int(self.baud_rate_combobox.currentText()) self.open_port.emit(port, baud_rate) def set_disable(self, b): self.disabled = b self.ports_list_combobox.setDisabled(b) self.baud_rate_combobox.setDisabled(b) if self.disabled: self.open_btn.setText('关闭') else: self.open_btn.setText('打开')
class Annie(QThread): annie_download_complete = pyqtSignal(str) annie_download_err = pyqtSignal() def __init__(self, bvid, output_path): super(Annie, self).__init__() self.bvid = bvid self.output_path = output_path self.process = None if not os.path.exists(output_path): os.mkdir(output_path) def run(self): cmd = f"annie -o {self.output_path} https://www.bilibili.com/video/{self.bvid}" print(cmd) try: self.process = subprocess.Popen( cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ) stdout = _BufferedReaderForFFmpeg(self.process.stdout.raw) while True: line = stdout.readline() if not line: break except: self.annie_download_err.emit() self.annie_download_complete.emit(self.bvid)
class MySerial(QObject): opened = pyqtSignal() closed = pyqtSignal() data = pyqtSignal(bytes) def __init__(self, parent=None, serial: Serial = None): super(MySerial, self).__init__(parent) self.serial = serial self.is_open = False self.thread = threading.Thread() self.thread.run = self.thread_entry def set_dtr(self, b): self.serial.setDTR(b) def set_rts(self, b): self.serial.setRTS(b) def start(self): self.thread.start() def close(self): self.serial.close() def thread_entry(self): while True: if self.serial.is_open: data = self.serial.read_all() if len(data) > 0: self.data.emit(data) time.sleep(0.01) else: break self.closed.emit()
class LoginThread(QThread): finished = pyqtSignal(bool) trial_over = pyqtSignal() def __init__(self, email, password, reauth): QThread.__init__(self) self.email = email self.password = password self.reauth = reauth self.logger = get_logger() def run(self): user = login(self.email, self.password) if not user: self.logger.error("User login failed.") self.finished.emit(False) return allowed_to_backup = user["subscribed"] or user["on_trial"] if not allowed_to_backup: self.logger.error("User trial expired.") self.trial_over.emit() return if self.reauth: update_email_and_password(self.email, self.password) self.logger.info("User reauth successful.") self.finished.emit(True)
class SnapshotThread(QThread): loaded = pyqtSignal(defaultdict) failed = pyqtSignal() def __init__(self, email, password, snapshot_id, computer_id): QThread.__init__(self) self.email = email self.password = password self.snapshot_id = snapshot_id self.computer_id = computer_id self.logger = get_logger() def run(self): computer = get_computer(self.email, self.password, self.computer_id) if is_windows(): process = subprocess.run( get_restic_ls_command(self.snapshot_id), env=get_restic_env(computer, self.password), stdout=subprocess.PIPE, creationflags=CREATE_NO_WINDOW, ) elif is_mac(): process = subprocess.run( get_restic_ls_command(self.snapshot_id), env=get_restic_env(computer, self.password), stdout=subprocess.PIPE, ) if process.returncode == 0: nodes = process.stdout.decode("utf-8").split("\n") tree = prepare_lazy_tree(nodes[1:-1]) self.logger.info("Snapshot loaded.") self.loaded.emit(tree) else: self.logger.error("Snapshot load failed.") self.failed.emit()
class WorkerThread(QThread): # setup response signal worker_response = pyqtSignal(tuple) # setup error signal worker_err_response = pyqtSignal() # additional parameter as url def __init__(self, url): # invoke the __init__ of super as well super(WorkerThread, self).__init__() self.url = url def run(self): try: yt = YouTube(self.url) # load thumbnail image pixmap = QPixmap() pixmap.loadFromData(urlopen(str(yt.thumbnail_url)).read()) # emitting the response signal self.worker_response.emit(( yt, pixmap, yt.title, yt.author, yt.length, yt.publish_date, # populate a list of progressive mp4 resolutions for the download options [f'{res.resolution} - {round(res.filesize/1.049e+6, 1)}MB' for res in yt.streams.filter(progressive='true', file_extension='mp4').order_by('resolution')] )) except: # emitting the error signal self.worker_err_response.emit()
class FetchSearchInputThread(QThread): search_changed = pyqtSignal(str, str) search_finish = pyqtSignal(str) def __init__(self, callback_tag): QThread.__init__(self) self.search_string = "" self.callback_tag = callback_tag self.running_flag = True def run(self): while self.running_flag: in_minibuffer = get_emacs_func_result("minibufferp", []) if in_minibuffer: minibuffer_input = get_emacs_func_result("minibuffer-contents-no-properties", []) if minibuffer_input != self.search_string: self.search_changed.emit(self.callback_tag, minibuffer_input) self.search_string = minibuffer_input else: self.stop() import time time.sleep(0.1) def stop(self): self.search_finish.emit(self.callback_tag) self.running_flag = False
class DownloadThread(QThread): # setup download respomse signal download_response = pyqtSignal(int) # setup download complete signal download_complete = pyqtSignal(str) # setup download error signal download_err = pyqtSignal() def __init__(self, yt, download_type, path): super(DownloadThread, self).__init__() self.yt = yt self.download_type = download_type self.path = path def run(self): # progress callback for progress bar updation def downloadProgress(stream, chunk, bytes_remaining): size = stream.filesize self.download_response.emit(int((float(abs(bytes_remaining-size)/size))*float(100))) # download complete callback to navigate user to download folder def downloadComplete(stream, location): self.download_complete.emit(location) try: # register callbacks self.yt.register_on_progress_callback(downloadProgress) self.yt.register_on_complete_callback(downloadComplete) # audio request if self.download_type == 'audio': self.yt.streams.get_audio_only().download(output_path=self.path, filename_prefix='[Audio] ') # video request else: self.yt.streams.filter(progressive=True, file_extension='mp4').get_by_resolution(self.download_type).download(output_path=self.path, filename_prefix=f'[{self.download_type}] ') except: # emitting the error signal self.download_err.emit()
class ControlBar(QWidget): dtr = pyqtSignal(bool) rts = pyqtSignal(bool) def __init__(self, parent): super(ControlBar, self).__init__(parent) self.init_ui() def init_ui(self): layout = QHBoxLayout() self.setLayout(layout) self.dtr_btn = QCheckBox('DTR') self.dtr_btn.clicked.connect(self.handle_dtr) self.rts_btn = QCheckBox('RTS') self.rts_btn.clicked.connect(self.handle_rts) layout.addWidget(self.dtr_btn) layout.addWidget(self.rts_btn) def handle_dtr(self, checked): self.dtr.emit(checked) def handle_rts(self, checked): self.rts.emit(checked) def reset(self): self.dtr_btn.setChecked(False) self.rts_btn.setChecked(False) self.dtr.disconnect() self.rts.disconnect()
class Search(QLineEdit): do_search = pyqtSignal(object, object) abort_search = pyqtSignal() passthrough_keys = True def __init__(self, parent=None): QLineEdit.__init__(self, parent) self.setAttribute(Qt.WidgetAttribute.WA_MacShowFocusRect, False) self.setStyleSheet( 'QLineEdit { background: transparent; color: %s; selection-background-color: %s }' % ( color('status bar foreground', 'palette(window-text)'), color('status bar selection', 'palette(window-text)'), )) self.search_forward = True self.textEdited.connect(self.text_edited) def text_edited(self, text): self.do_search.emit(text, self.search_forward) def keyPressEvent(self, ev): k = ev.key() if k == Qt.Key.Key_Escape: self.abort_search.emit() ev.accept() return if k in (Qt.Key.Key_Enter, Qt.Key.Key_Return): text = self.text() self.editingFinished.emit() self.do_search.emit(text, self.search_forward) return return QLineEdit.keyPressEvent(self, ev)
class BackendProxy(QObject): def __init__(self, parent=None): super().__init__(parent) self._backend = Application.getInstance().getBackend() self._progress = -1 self._state = BackendState.NotStarted if self._backend: self._backend.processingProgress.connect( self._onProcessingProgress) self._backend.backendStateChange.connect( self._onBackendStateChange) processingProgress = pyqtSignal(float, arguments=["amount"]) @pyqtProperty(float, notify=processingProgress) def progress(self): return self._progress @pyqtProperty(int, constant=True) def NotStarted(self): return 1 @pyqtProperty(int, constant=True) def Processing(self): return 2 @pyqtProperty(int, constant=True) def Done(self): return 3 @pyqtProperty(int, constant=True) def Error(self): return 4 @pyqtProperty(int, constant=True) def Disabled(self): return 5 backendStateChange = pyqtSignal(int, arguments=["state"]) @pyqtProperty(int, notify=backendStateChange) def state(self): """Returns the current state of processing of the backend. :return: :type{IntEnum} The current state of the backend. """ return self._state def _onProcessingProgress(self, amount): if self._progress != amount: self._progress = amount self.processingProgress.emit(amount) def _onBackendStateChange(self, state): if self._state != state: self._state = state self.backendStateChange.emit(state)
class EditAccount(QWidget): changed = pyqtSignal() delete_requested = pyqtSignal() def __init__(self, parent=None): QWidget.__init__(self, parent) self.l = l = QFormLayout(self) self.username = u = QLineEdit(self) u.textChanged.connect(self.changed.emit) l.addRow(_('&Username:'******'Username for this account')) self.password = p = QLineEdit(self) l.addRow(_('&Password:'******'Password for this account')) p.textChanged.connect(self.changed.emit) p.setEchoMode(QLineEdit.EchoMode.Password) self.show_password = sp = QCheckBox(_('&Show password')) l.addWidget(sp) sp.toggled.connect(self.show_password_toggled) self.la = la = QLabel(_('&Notes:')) l.addRow(la) self.notes = n = QPlainTextEdit(self) la.setBuddy(n) n.textChanged.connect(self.changed.emit) l.addRow(n) self.autosubmit = asb = QCheckBox( _('&Auto login with these credentials'), self) l.addRow(asb) asb.stateChanged.connect(self.on_change) self.rb = b = QPushButton(_('&Delete this account')) b.clicked.connect(self.delete_requested.emit) l.addRow(b) def show_password_toggled(self, checked): self.password.setEchoMode(QLineEdit.EchoMode.Normal if checked else QLineEdit.EchoMode.Password) def on_change(self): self.changed.emit() @property def data(self): return { 'username': self.username.text(), 'password': self.password.text(), 'notes': self.notes.toPlainText().strip() or None, 'autologin': self.autosubmit.isChecked(), } @data.setter def data(self, val): self.blockSignals(True) self.username.setText(val.get('username') or '') self.password.setText(val.get('password') or '') self.notes.setPlainText(val.get('notes') or '') self.autosubmit.setChecked(val.get('autologin', False)) self.blockSignals(False)
class ColorImage(QQuickPaintedItem): def __init__(self, parent=None): super().__init__(parent) self._source = QUrl() self._color = QColor() self._svg_data = b"" self._renderer = None sourceChanged = pyqtSignal() colorChanged = pyqtSignal() def _updateSVG(self) -> None: if not self._source or self._source.toLocalFile() == "": return try: with open(self._source.toLocalFile(), "rb") as f: self._svg_data = f.read() except FileNotFoundError: Logger.log( "w", f"Unable to find image located at {self._source.toLocalFile()}" ) return self._svg_data = self._svg_data.replace( b"<svg ", b"<svg fill=\"%s\" " % self._color.name().encode("utf-8")) self._renderer = QSvgRenderer(self._svg_data) self.update() def setSource(self, source: QUrl) -> None: if self._source != source: self._source = source self.sourceChanged.emit() self._updateSVG() @pyqtProperty(QUrl, fset=setSource, notify=sourceChanged) def source(self) -> QUrl: return self._source def setColor(self, color: QColor) -> None: if self._color != color: self._color = color self.colorChanged.emit() self._updateSVG() @pyqtProperty(QColor, fset=setColor, notify=colorChanged) def color(self) -> QColor: return self._color def paint(self, painter: QPainter) -> None: pixel_ratio = Application.getInstance().getMainWindow( ).effectiveDevicePixelRatio() painter.scale(1 / pixel_ratio, 1 / pixel_ratio) if self._renderer: self._renderer.render(painter)
class GetRecListsWorker(QThread): '''获取回收站列表''' folders = pyqtSignal(object) infos = pyqtSignal(object, object) msg = pyqtSignal(str, int) def __init__(self, parent=None): super(GetRecListsWorker, self).__init__(parent) self._disk = None self._mutex = QMutex() self._is_work = False self._folder_id = None def set_disk(self, disk): self._disk = disk def set_values(self, fid): # 用于获取回收站指定文件夹内文件信息 self._folder_id = fid self.start() def __del__(self): self.wait() def stop(self): self._mutex.lock() self._is_work = False self._mutex.unlock() def run(self): if not self._is_work: self._mutex.lock() self._is_work = True try: if self._folder_id: file_lists = self._disk.get_rec_file_list( folder_id=self._folder_id) self._folder_id = None self.folders.emit(file_lists) raise UserWarning dir_lists = self._disk.get_rec_dir_list() file_lists = self._disk.get_rec_file_list(folder_id=-1) self.infos.emit(dir_lists, file_lists) self.msg.emit("刷新列表成功!", 2000) except TimeoutError: self.msg.emit("网络超时,请稍后重试!", 6000) except UserWarning: pass except Exception as e: logger.error(f"GetRecListsWorker error: e={e}") self._is_work = False self._mutex.unlock() else: self.msg.emit("后台正在运行,请稍后重试!", 3100)
class ComboBoxTable(ComboBox): """ Special combo box for use as cell editor in TableView To implement a persistent state for the widgetd cell, must provide `getWidgetedCellState` and `setWidgetedCellState` methods. This is how the WidgetedCell framework can create and destory widget as needed. """ escapePressed = pyqtSignal() returnPressed = pyqtSignal() def __init__(self, parent=None, delegate=None, **kw): super().__init__(parent=parent, **kw) # need parent so TableView knows where to draw editor self.delegate = delegate if not delegate is None: self.escapePressed.connect(self.delegate.close_editor) # self.returnPressed.connect(self.delegate.commitAndCloseEditor) # NOTE not sure if this is still needed 2020-08-21 # def keyPressEvent(self, event): # if event.key() in (Qt.Key.Key_Return, Qt.Key.Key_Enter): # print('returnPressed') # # self.delegate.commitAndCloseEditor() # self.returnPressed.emit() # return # return super().keyPressEvent(event) def eventFilter(self, obj, event): if event.type() == QEvent.Type.KeyPress: if event.key() == Qt.Key.Key_Escape: self.escapePressed.emit() return super().eventFilter(obj, event) def commit_list_data(self): # need to manually set the editors's index/value when list item is pressed, then commit self.setCurrentIndex(self.view().currentIndex().row()) self.delegate.commitAndCloseEditor() def showPopup(self): # need event filter to catch combobox view's ESC event and close editor completely super().showPopup() view = self.view() view.installEventFilter(self) view.pressed.connect(self.commit_list_data) def getWidgetedCellState(self): return self.currentIndex() def setWidgetedCellState(self, state): self.setCurrentIndex(state)
class GraphicZone(QObject): mask: NDArray[(Any, 2), Bool] check_func: Optional[Callable[[float, float], bool]] = None activated: bool = False clicked = pyqtSignal() double_clicked = pyqtSignal() mouse_enter = pyqtSignal() mouse_leave = pyqtSignal() def __init__(self, gr_field: GraphicField, check_func: Callable[[float, float], bool] = None, mask: NDArray[(Any, 2), Bool] = None, mask_file: str = None): super().__init__() self.gr_field = gr_field if check_func is not None: self.check_func = check_func elif mask is not None: self.mask = mask.copy() elif mask_file is not None: self.read_mask_from_file(mask_file) else: self.check_func = lambda x, y: False def read_mask_from_file(self, mask_file: str): image_pil = PIL.Image.open(mask_file).convert('L') image = np.array(image_pil) self.mask = image > 10 self.check_func = None def coordinates_are_in_zone(self, x: float, y: float) -> bool: if self.check_func is not None: return self.check_func(x, y) else: n_y, n_x = self.mask.shape k = n_x / self.gr_field.x_range x_index = round(x * k) y_index = round(y * k) try: return self.mask[y_index, x_index] except IndexError: return False def paint(self, painter: QPainter_ext): pass
class SwaVanMockImport(QWidget): saved = pyqtSignal() canceled = pyqtSignal() def __init__(self, ): super(SwaVanMockImport, self).__init__() template_loader("templates/mock_import.ui", self) self.mock_load_json_btn.clicked.connect(self.save) self.mock_json_pretty_btn.clicked.connect(self.format_json) self.mock_load_json_cancel_btn.clicked.connect(self.canceled) def format_json(self): self.json_validation_lbl.setText("") try: _content = self.mock_json_import_input.toPlainText() _formatted = json.dumps(json.loads(_content), indent=4) self.mock_json_import_input.setText(_formatted) except JSONDecodeError as _: self.json_validation_lbl.setText("Invalid JSON") finally: pass def validated_endpoints(self): try: _raw_endpoints = json.loads( self.mock_json_import_input.toPlainText()) _endpoints = [ Endpoint.from_dict(_raw_endpoint) for _raw_endpoint in _raw_endpoints ] return _endpoints except JSONDecodeError as _: self.json_validation_lbl.setText("Invalid JSON") return [] finally: pass def save(self): _rows = [] for _row in self.validated_endpoints(): _row.id = str(uuid.uuid4()) _row.pid = SwaVanCache.get_selected_env() _rows.append(_row) if len(_rows) > 0: _status = EndpointService.save_all(_rows) if _status: SwaVanLogRecorder.send_log(f"{len(_rows)} endpoint/s imported") self.saved.emit()
class StackedWidget(QStackedWidget): resized = pyqtSignal() def resizeEvent(self, ev): self.resized.emit() return QStackedWidget.resizeEvent(self, ev)
class TaskCpu(QThread): on_cpu_changed = pyqtSignal(int) def run(self): while True: value = psutil.cpu_percent(interval=1) self.on_cpu_changed.emit(int(value)) # noqa
class FileLabel(QtWidgets.QLabel): def __init__(self, parent): super(FileLabel, self).__init__(parent) self.setAcceptDrops(True) self.setText('Поместите сюда файл через drag n drop или нажмите для выбора') self.setCursor(QtGui.QCursor(QtCore.Qt.CursorShape.PointingHandCursor)) self.setSizePolicy(QtWidgets.QSizePolicy.Policy.Maximum, QtWidgets.QSizePolicy.Policy.Maximum) clicked = pyqtSignal() def mousePressEvent(self, event): self.clicked.emit() def dragEnterEvent(self, event): data = event.mimeData() urls = data.urls() if urls and urls[0].scheme() == 'file': event.acceptProposedAction() def dragMoveEvent(self, event): data = event.mimeData() urls = data.urls() if urls and urls[0].scheme() == 'file': event.acceptProposedAction() def dropEvent(self, event): data = event.mimeData() url = data.urls()[0] path = url.toLocalFile() self.setText(str(path))
class ClipboardWatcher(QThread): signal = pyqtSignal('PyQt_PyObject') def __init__(self): QThread.__init__(self) self._pause = 0.5 # watch clipboard interval self._stopping = False self._stop_event = threading.Event() def run(self): recentValue = pyperclip.paste() while not self._stopping: clipboardValue = pyperclip.paste() # if clipboard is changed (copy new text) send that for translate if clipboardValue != recentValue and clipboardValue != '' and clipboardValue != ' ': recentValue = clipboardValue self.signal.emit(clipboardValue) time.sleep(self._pause) def stop(self): self._stopping = True self._stop_event.set() sys.exit() def stopped(self): return self._stop_event.is_set()
class PostGui(QObject): through_thread = pyqtSignal(object, object) def __init__(self, inclass=True): super(PostGui, self).__init__() self.through_thread.connect(self.on_signal_received) self.inclass = inclass def __call__(self, func): self._func = func from functools import wraps @wraps(func) def obj_call(*args, **kwargs): self.emit_signal(args, kwargs) return obj_call def emit_signal(self, args, kwargs): self.through_thread.emit(args, kwargs) def on_signal_received(self, args, kwargs): try: if self.inclass: obj, args = args[0], args[1:] self._func(obj, *args, **kwargs) else: self._func(*args, **kwargs) except Exception: import traceback traceback.print_exc()
class MazeGeneratorWindow(QMainWindow): onMazeSpecChosen = pyqtSignal(MazeGenerationSpecification) def __init__( self, parent: Optional[QWidget] = None, *args: Tuple[Any, Any], **kwargs: Tuple[Any, Any], ) -> None: """ A window presented to the user prompting them for maze specification. """ super(MazeGeneratorWindow, self).__init__(parent=parent, *args, **kwargs) self.setContentsMargins(15, 5, 15, 15) generateMazeView = GenerateMazeGroupView(parent=self) generateMazeView.onMazeSpecChosen.connect(self.onMazeSpecChosen) self.setCentralWidget(generateMazeView) self.setWindowTitle("Generate a Maze") self.show()
class UI(QWidget): update_log_text_signal=pyqtSignal(str) def __init__(self): super().__init__() self.logger=logging.getLogger() handler=QLogger(parent=self,update_log_text_signal=self.update_log_text_signal) self.update_log_text_signal.connect(handler.widget.appendPlainText) handler.widget.textChanged.connect(handler.scroll_widget_to_bottom) self.logger.setLevel(logging.INFO) # Here set the log level you want handler.setFormatter(logging.Formatter(fmt="%(asctime)s-%(levelname)s-%(message)s",datefmt="%Y-%m-%d %H:%M:%S")) self.logger.addHandler(handler) button=QPushButton("&Test") button.clicked.connect(self.button_handler) button.setDefault(True) layout_=QVBoxLayout() layout_.addWidget(handler.widget) layout_.addWidget(button) self.setLayout(layout_) def button_handler(self): self.logger.debug("This is a debug message") self.logger.info("This is an info message") self.logger.warning("This is a warning message") self.logger.error("This is an error message") self.logger.critical("This is a critical message")
class SnapshotsThread(QThread): loaded = pyqtSignal(list) def __init__(self, email, password, computer_id): QThread.__init__(self) self.email = email self.password = password self.computer_id = computer_id self.logger = get_logger() def run(self): computer = get_computer(self.email, self.password, self.computer_id) if is_windows(): snapshots = json.loads( subprocess.run( get_restic_snapshots_command(), env=get_restic_env(computer, self.password), stdout=subprocess.PIPE, creationflags=CREATE_NO_WINDOW, ).stdout) elif is_mac(): snapshots = json.loads( subprocess.run( get_restic_snapshots_command(), env=get_restic_env(computer, self.password), stdout=subprocess.PIPE, ).stdout) sorted_snapshots = sorted(snapshots, key=lambda x: x["time"], reverse=True) self.logger.info("Snapshots loaded.") self.loaded.emit(sorted_snapshots)
class MapStatisticsThread(QThread): statistic_data_update = pyqtSignal(dict) def __init__(self): QThread.__init__(self) self.queue = queue.Queue(maxsize=1) self.active = True def requestStatistics(self): self.queue.put(None) def run(self): evegate.getPlayerSovereignty(fore_refresh=False, show_npc=False) evegate.getIncursionSystemsIds(False) evegate.getCampaignsSystemsIds(False) evegate.getSystemStatistics() while True: self.queue.get() if not self.active: return try: evegate.getIncursionSystemsIds(False) evegate.getCampaignsSystemsIds(False) statistics = evegate.getSystemStatistics() statistics_data = {"result": "ok", "statistics": statistics} except Exception as e: logging.error("Error in MapStatisticsThread: %s", e) statistics_data = {"result": "error", "text": str(e)} self.statistic_data_update.emit(statistics_data) def quit(self): self.active = False self.queue.put(None) QThread.quit(self)
class OperationStackProxy(QObject): def __init__(self, parent=None): super().__init__(parent) self._operation_stack = Application.getInstance().getOperationStack() self._operation_stack.changed.connect(self._onUndoStackChanged) undoStackChanged = pyqtSignal() @pyqtProperty(bool, notify=undoStackChanged) def canUndo(self): return self._operation_stack.canUndo() @pyqtProperty(bool, notify=undoStackChanged) def canRedo(self): return self._operation_stack.canRedo() @pyqtSlot() def undo(self): self._operation_stack.undo() @pyqtSlot() def redo(self): self._operation_stack.redo() def _onUndoStackChanged(self): self.undoStackChanged.emit()
class MyListView(QListView): """加入拖拽功能的列表显示器""" drop_files = pyqtSignal(object) def __init__(self): QListView.__init__(self) self.setDragDropMode(QAbstractItemView.DragDropMode.InternalMove) self.setDragEnabled(True) self.setAcceptDrops(True) self.setDropIndicatorShown(True) def dragEnterEvent(self, event): m = event.mimeData() if m.hasUrls(): for url in m.urls(): if url.isLocalFile(): event.accept() return event.ignore() def dropEvent(self, event): if event.source(): QListView.dropEvent(self, event) else: m = event.mimeData() if m.hasUrls(): urls = [ url.toLocalFile() for url in m.urls() if url.isLocalFile() ] if urls: self.drop_files.emit(urls) event.acceptProposedAction()
class RemoveFilesWorker(QThread): '''删除文件(夹)线程''' msg = pyqtSignal(object, object) finished = pyqtSignal() def __init__(self, parent=None): super(RemoveFilesWorker, self).__init__(parent) self._disk = None self.infos = None self._mutex = QMutex() self._is_work = False def set_disk(self, disk): self._disk = disk def set_values(self, infos): self.infos = infos self.start() def __del__(self): self.wait() def stop(self): self._mutex.lock() self._is_work = False self._mutex.unlock() def run(self): if not self._is_work: self._mutex.lock() self._is_work = True if not self.infos: self._is_work = False self._mutex.unlock() return for i in self.infos: try: self._disk.delete(i['fid'], i['is_file']) except TimeoutError: self.msg.emit(f"删除 {i['name']} 因网络超时失败!", 3000) except Exception as e: logger.error(f"RemoveFileWorker error: e={e}") self.finished.emit() self._is_work = False self._mutex.unlock() else: self.msg.emit("后台正在运行删除指令!", 3100)
class SelectionProxy(QObject): def __init__(self, parent=None): super().__init__(parent) Selection.selectionChanged.connect(self._onSelectionChanged) Selection.selectedFaceChanged.connect(self._onSelectedFaceChanged) selectionChanged = pyqtSignal() selectedFaceChanged = pyqtSignal() @pyqtProperty(bool, notify=selectionChanged) def hasSelection(self): return Selection.hasSelection() @pyqtProperty(bool, notify=selectedFaceChanged) def faceSelectMode(self): return Selection.getFaceSelectMode() @pyqtSlot(bool) def setFaceSelectMode(self, select: bool) -> None: Selection.setFaceSelectMode(select) if not select: Selection.clearFace() @pyqtProperty(bool, notify=selectedFaceChanged) def hasFaceSelected(self): return Selection.getSelectedFace() is not None @pyqtProperty(int, notify=selectionChanged) def selectionCount(self): return Selection.getCount() @pyqtProperty("QVariantList", notify=selectionChanged) def selectionNames(self): return [node.getName() for node in Selection.getAllSelectedObjects()] def _onSelectionChanged(self): self.selectionChanged.emit() def _onSelectedFaceChanged(self): self.selectedFaceChanged.emit() @pyqtProperty(bool, notify=selectionChanged) def isGroupSelected(self): for node in Selection.getAllSelectedObjects(): if node.callDecoration("isGroup"): return True return False