Q:

calling exec() on a Qdialog in a thread doesn't work well

I want to call my_dialog.exec() in a thread,but it work bad when mainwindow(mean main thread) handling a event,i want know how to deal with this problem
this is my test program:
import sys
from PyQt5.Qt import *
from threading import Thread
from time import sleep

class Main(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setGeometry(500, 500, 500, 200)

        self.dialog = QDialog(self)
        self.dialog.setGeometry(500, 500, 200, 100)

        btn = QPushButton('click', self)
        btn.clicked.connect(self.show_dialog)

        self.show()

    def show_dialog(self):
        Thread(target=self.execute).start()

    def execute(self):
        sleep(2)
        # keep moving mainwindow untill dialog have shown
        self.dialog.exec_()

app = QApplication(sys.argv)
e = Main()
sys.exit(app.exec_())

when i press the button, it will sleep two seconds first.
it's normal when there's no mainwindow's event.
but when i keep moving the mainwindow(or other event,like resize) in the two sleep seconds,both of they will become nonresponsive

A:

No, you cannot modify the GUI from another thread, use the signals.
TL;DR;
I share a golden rule in Qt: You cannot and should not modify the GUI from another thread. For more information read: GUI Thread and Worker Thread.
Considering the above, the natural way to interact between elements that live in different threads in Qt is to use the signals since they are thread-safe as I show below:
class Main(QMainWindow):
    customSignal = pyqtSignal()

    def __init__(self):
        # ...

        self.show()
        self.customSignal.connect(self.dialog.exec_)

    def show_dialog(self):
        Thread(target=self.execute).start()

    def execute(self):
        sleep(2)
        # keep moving mainwindow untill dialog have shown
        self.customSignal.emit()

