Razer
Razer

Reputation: 8211

QMutexLocker() causes UI to freeze

I have a controller class which controls a robot (attached over serial interface). This controller is attached to a view. In addition to that I have a thread derived from QThread which periodically reads out the status of the robot.

Reading out the status must not colide with robot commands which get tiggered from the userinterface. Therefore I locked every robot acces with a mutex using QMutexLocker but this causes my userinterface to freeze if such a mutex block gets executed.

class RobotControl(QObject):
    def __init__(self, view):
        super(RobotControl, self).__init__()
        self.view = view
        self.updatethread = UpdatePositionAndStatus(self.robot)
        self.mutex = QMutex()
        self.connect(self.updatethread, SIGNAL("updateStatus( QString ) "), self.print_error)
        self.updatethread.start()

@pyqtSlot()  
def init_robot(self):
    """
    Initializes the serial interface to the robot interface and checks if
    there is really a robot interface available.
    """
    with QMutexLocker(self.mutex):
        # Open interface
        try:
            index = self.view.robotcontrolui.get_selected_interface_index()
            interface = self.interfaces.item(index).text()
            self.robot = RobotController(interface)
        except DeviceError:
            self.view.error_dlg(self.tr("Couldn't open interface {0}!".format(interface)))
            self.robot = None
            return

        # Check if there is really a robot interface on the selected serial
        # interface with trying to read status byte
        try:
            self.robot.status()
        except DeviceError:
            # In case of failure release interface
            self.close_robot()
            self.view.error_dlg(self.tr("Couldn't initialize robot interface!"))
            return

        self.view.robotcontrolui.bt_open_interface.setEnabled(False)
        self.view.robotcontrolui.bt_close_interface.setEnabled(True)

class UpdatePositionAndStatus(QThread):
    def __init__(self, robot, parent=None):
        QThread.__init__(self, parent) 
        self.robot = robot
        self.mutex = QMutex()
    def run(self):
        """ 
        This function continously reads out the position and the status to for 
        updating it on the userinterface.
        """
        try:
            while True:
                if self.robot is not None:
                    # Do robot communication under a lock
                    self.mutex.lock()
                    (_, rel_pos) = self.robot.read_position()
                    status = self.robot.status()
                    self.mutex.unlock()

                    # Display position and status on userinterface
                    self.view.robotcontrolui.update_position_and_status(rel_pos, status)

                # Wait 1 seccond for next update
                QThread.sleep(1.0)
        except DeviceError:
            # Release lock on robot
            self.mutex.unlock()
            self.emit(SIGNAL("updateStatus( QString )"), self.tr("Error while updating current position and status!"))

After triggering the init method the userinterface freezes and the program crashes: Why is this so? How can I avoid that?

Upvotes: 1

Views: 1607

Answers (1)

Luke
Luke

Reputation: 11644

It's hard to tell because your code sample is incomplete, but I see two fundamental problems with this code:

  1. You are locking two different QMutex objects. In order for mutual exclusion to work properly, both threads must be locking the same mutex object.

  2. You appear to be directly interacting with the GUI from the update thread at this line:

    self.view.robotcontrolui.update_position_and_status(rel_pos, status)
    

    Performing GUI operations may only be done from the GUI thread in Qt. It's a fair bet that this is causing your crash. See: http://qt-project.org/doc/qt-4.8/threads.html

Upvotes: 3

Related Questions