[Python] PyQt emit signals not received (or emitted?)

Discussão em 'Python' iniciado por Stack, Abril 10, 2025.

  1. Stack

    Stack Membro Participativo

    I am brand new to PyQt and I am trying to create a small GUI for transferring files which essentially shows the progress of the transfer.

    Problem


    I am trying to figure out why my signals are not received by my handler methods (of the main UI class): updateProgress, logFileChange, updateFileLocations. I can print the signals right before they are emitted but do not see them come in. What am I doing wrong?

    Code


    from PyQt6 import QtWidgets, uic
    from PyQt6 import QtCore
    import sys
    from shutil import rmtree, copy2, copytree
    from pathlib import Path
    from glob import glob

    from PyQt5.QtCore import QObject, QThread, pyqtSignal

    class Transfer(QThread):
    # This signal will be used to update the UI
    progress_signal = pyqtSignal(int)
    progress_log = pyqtSignal(str)
    finished = pyqtSignal(dict)

    def __init__(self, transferItems, tempDir, parent = None):
    super().__init__(parent)
    self.transferItems = transferItems
    self.tempDir = tempDir
    self.transferProgress = 0

    def enumerateFiles(self):
    self.f_dict = {}
    for item in self.transferItems:
    if item.is_dir():
    items = glob(f"{item}/**/*", recursive=True)
    for item in items:
    item = Path(item)
    if item.is_file():
    self.f_dict.update( {item: item.stat().st_size} )
    else:
    self.f_dict.update( {item: item.stat().st_size} )

    self.transferSize = sum(self.f_dict.values())

    def transferLocal(self):

    def customCopy(src, dst):
    self.transferProgress += self.f_dict[Path(src)]
    # Change dictionary key here
    self.f_dict[Path(dst)] = self.f_dict.pop(Path(src))
    self.progress_log.emit(f'Copying {src} -> {dst}')
    base = 10000 # Make sure this matches the definition in the .ui
    print(int(min(base, self.transferProgress / self.transferSize * base)), end='\r')
    self.progress_signal.emit( int(min(base, self.transferProgress / self.transferSize * base)) )
    # Dont actually copy for now
    # copy2(src, dst)

    for source in self.transferItems:
    copytree(source, self.tempDir / source.name, copy_function=customCopy)

    self.finished.emit(self.f_dict)

    def run(self):
    self.enumerateFiles()
    self.transferLocal()


    class Ui(QtWidgets.QMainWindow):
    def __init__(self, items):
    super(Ui, self).__init__()
    uic.loadUi(r"C:\Users\...\progress.ui", self)
    self.show()

    self.transferItems = items
    self.tempDir = Path(r"C:\Users\...\TEMP")

    QtWidgets.QApplication.setStyle(QtWidgets.QStyleFactory.create('Windows11'))

    # Create worker
    self.worker = Transfer(self.transferItems, self.tempDir)

    # Connect Signals
    self.worker.progress_signal.connect(self.updateProgress)
    self.worker.progress_log.connect(self.logFileChange)
    self.worker.finished.connect(self.updateFileLocations)

    # Start thread
    self.worker.start()

    # Dont change this function name - control red-x close behavior
    def closeEvent(self, event):
    self.worker.quit()
    event.accept()

    def updateFileLocations(self, f_dict):
    # self.Confirm.setEnabled(True)
    self.f_dict = f_dict

    def updateProgress(self, val):
    print("Incoming", val)
    self.Progress.setValue(val)

    def logFileChange(self, log_entry):
    print("Incoming", log_entry)
    self.Console.append(log_entry)

    sources = [Path(r"C:\Users\...")]
    # sources = sys.argv[1:]

    app = QtWidgets.QApplication(sys.argv)
    window = Ui(sources)
    window.show()
    sys.exit(app.exec())


    And here is the GUI XML:

    <?xml version="1.0" encoding="UTF-8"?>
    <ui version="4.0">
    <class>MainWindow</class>
    <widget class="QMainWindow" name="MainWindow">
    <property name="geometry">
    <rect>
    <x>0</x>
    <y>0</y>
    <width>810</width>
    <height>700</height>
    </rect>
    </property>
    <property name="minimumSize">
    <size>
    <width>810</width>
    <height>700</height>
    </size>
    </property>
    <property name="maximumSize">
    <size>
    <width>810</width>
    <height>700</height>
    </size>
    </property>
    <property name="windowTitle">
    <string>AFT Automator</string>
    </property>
    <property name="windowOpacity">
    <double>1.000000000000000</double>
    </property>
    <widget class="QWidget" name="centralwidget">
    <widget class="QProgressBar" name="Progress">
    <property name="geometry">
    <rect>
    <x>10</x>
    <y>10</y>
    <width>781</width>
    <height>51</height>
    </rect>
    </property>
    <property name="font">
    <font>
    <pointsize>12</pointsize>
    </font>
    </property>
    <property name="value">
    <number>0</number>
    </property>
    </widget>
    <widget class="QTextBrowser" name="textBrowser">
    <property name="geometry">
    <rect>
    <x>20</x>
    <y>80</y>
    <width>711</width>
    <height>191</height>
    </rect>
    </property>
    </widget>
    </widget>
    <widget class="QMenuBar" name="menubar">
    <property name="enabled">
    <bool>true</bool>
    </property>
    <property name="geometry">
    <rect>
    <x>0</x>
    <y>0</y>
    <width>810</width>
    <height>21</height>
    </rect>
    </property>
    </widget>
    <widget class="QStatusBar" name="statusbar"/>
    </widget>
    <resources/>
    <connections/>
    </ui>


    EDIT: Of note, if I use the @pyqtSlot decorator on my methods, the whole program crashes.

    Continue reading...

Compartilhe esta Página