更新QTableView raise:QObject :: installEventFilter():无法过滤不同线程中对象的事件

时间:2017-03-09 13:02:34

标签: python mongodb qt pyqt

当我从数据库收到更改通知时,我正在尝试更新我的QTableView。通知系统似乎按预期工作,但在某些情况下,它会使应用程序崩溃,但有以下异常:

Message : QObject::installEventFilter(): Cannot filter events for objects in a different thread.

在我的应用程序中,当用户尝试更新TableView中的行时,会出现一个弹出窗口询问确认。但是,如果2个人同时执行此操作,则第二个应用程序会因给定的错误消息而崩溃。

情景:

  1. 来自app_instance_1:我在不验证弹出窗口的情况下更新了一行 消息
  2. 来自app_instance_2:我还在不验证弹出窗口的情况下更新了一行 消息
  3. 来自app_instance_1:我验证了验证弹出窗口,第二个验证 应用程序使用第一个应用程序实例中设置的值进行更新。
  4. 来自app_instance_2:我验证了验证弹出窗口, app_instance_2崩溃,但值保存在数据库中。
  5. 假设:    我想问题是该软件试图从非gui线程更新GUI,但我不明白为什么它只发生在这种情况下。 (只运行一个应用程序实例,它不会崩溃)

    数据库观察者:

    class OplogWatcher(object):
    
    def __init__(self, db=None, collection=None, poll_time=1.0, connection=None, start_now=True):
        if collection is not None:
            if db is None:
                raise ValueError('must specify db if you specify a collection')
            self._ns_filter = db + '.' + collection
        elif db is not None:
            self._ns_filter = re.compile(r'^%s\.' % db)
        else:
            self._ns_filter = None
    
        self.poll_time = poll_time
        self.connection = connection or pymongo.MongoClient()
    
        if start_now:
            self.start()
    
    @staticmethod
    def __get_id(op):
        _id = None
        o2 = op.get('o2')
        if o2 is not None:
            _id = o2.get('_id')
    
        if _id is None:
            _id = op['o'].get('_id')
    
        return _id
    
    def start(self):
        oplog = self.connection.local['oplog.$main']
        ts = oplog.find().sort('$natural', -1)[0]['ts']
        while True:
            if self._ns_filter is None:
                _filter = {}
            else:
                _filter = {'ns': self._ns_filter}
            _filter['ts'] = {'$gt': ts}
            try:
                cursor = oplog.find(_filter)
                while True:
                    for op in cursor:
                        ts = op['ts']
                        _id = self.__get_id(op)
                        self.all_with_noop(ns=op['ns'], ts=ts, op=op['op'], _id=_id, raw=op)
                    time.sleep(self.poll_time)
                    if not cursor.alive:
                        break
            except AutoReconnect:
                time.sleep(self.poll_time)
    
    def all_with_noop(self, ns, ts, op, _id, raw):
        if op == 'n':
            self.noop(ts=ts)
        else:
            self.all(ns=ns, ts=ts, op=op, _id=_id, raw=raw)
    
    def all(self, ns, ts, op, _id, raw):
        if op == 'i':
            self.insert(ns=ns, ts=ts, _id=_id, obj=raw['o'], raw=raw)
        elif op == 'u':
            self.update(ns=ns, ts=ts, _id=_id, mod=raw['o'], raw=raw)
        elif op == 'd':
            self.delete(ns=ns, ts=ts, _id=_id, raw=raw)
        elif op == 'c':
            self.command(ns=ns, ts=ts, cmd=raw['o'], raw=raw)
        elif op == 'db':
            self.db_declare(ns=ns, ts=ts, raw=raw)
    
    def update(self, ns, ts, _id, mod, raw, **kw):
        db_signals.existing_document_updated.emit(ns, ts, _id, mod, raw, kw)
    

    我如何创建数据库观察器:

    class DbController(object):
    
    def __init__(self):
        self.db = None
        self.watcher_thread = None
    
    ...
    
    def load_test_db(self):
        self.db = Database(db_name='db_name', host='localhost', port=27017)
        mongo_client = self.db.connection()
    
        watcher = OplogWatcher(db='db_name', collection="device", start_now=False, connection=mongo_client)
        self.watcher_thread = Thread(target=watcher.start, daemon=True)
        self.watcher_thread.setName("DeviceWatcherThread")
        self.watcher_thread.start()
    
    ...
    

    QAbstractTableModel:

    class DeviceTableModel(QAbstractTableModel):
    
    def __init__(self):
        super(DeviceTableModel, self).__init__()
        self.update_field_confirmed = False
        self.headers = ['...']
        self.devices = []
        self.connect_signals()
    
    def connect_signals(self):
        db_signals.existing_document_updated.connect(self.on_document_updated)
    
    def on_document_updated(self, ns, ts, _id, mod, raw, **kw):
        old_device = None
        updated_device = None
        self.beginResetModel()
        if len(mod) is 1:
            updated_device = get_mongoengine_model_from_id(_id)
            old_device = [x for x in self.devices if x.mac_adress == updated_device.mac_adress][0]
        else:  # Complete update from the database
            updated_device = get_mongoengine_model_from_pymongo_obj(mod)
            old_device = [x for x in self.devices if x.mac_adress == mod["mac_adress"]][0]
        idx = self.devices.index(old_device)
        self.devices[idx] = updated_device
        self.endResetModel()
    
    def on_answer_received(self, answer):
        self.update_field_confirmed = answer
    
    def setData(self, qmodel=QModelIndex(), value=None, role=Qt.EditRole):
        if role == Qt.EditRole:
    
            selected_device = self.devices[qmodel.row()]
            field_name = self.get_col_object_name(qmodel.column())
    
            if "serial_numbers" in field_name:
                splitted_fieldname = field_name.split('.')
                serial_numbers = getattr(selected_device, splitted_fieldname[0])
                if getattr(serial_numbers, splitted_fieldname[1]) != value:
    
                    signal_manager.answer_popup_received.connect(self.on_answer_received)
                    signal_manager.show_popup_default.emit("info", "are you sure ?", None)
                    if self.update_field_confirmed:
                        setattr(serial_numbers, splitted_fieldname[1], value)
                    else:
                        return False
                else:
                    return False
    
            else:
                if getattr(selected_device, field_name) != value:
    
                    def on_answer_received(answer):
                        self.update_field_confirmed = answer
    
                    signal_manager.answer_popup_received.connect(on_answer_received)
                    signal_manager.show_popup_default.emit("info", "are you sure ?", None)
                    if self.update_field_confirmed:
                        setattr(selected_device, field_name, value)
                    else:
                        return False
    
            selected_device.save()
    
            top_left = self.index(0, 0)
            bottom_right = self.index(self.rowCount() - 1,
                                      self.columnCount() - 1)
            self.dataChanged.emit(top_left, bottom_right)
            return True
        else:
            return False
    

1 个答案:

答案 0 :(得分:0)

我找到了一个解决方案,但我真的不明白出了什么问题。

在方法中:on_document_updated: 我对这些内容进行了评论:

  

self.beginResetModel()

  

self.endResetModel()

没有更多崩溃,但视图没有更新所以我添加了

  

self.dataChanged.emit(top_left,bottom_right)

现在它正在运作。 如果有人理解为什么?请告诉我。

def on_document_updated(self, ns, ts, _id, mod, raw, **kw):
    old_device = None
    updated_device = None
    # self.beginResetModel()
    if len(mod) is 1: # Simple row update from this application (not necessary this instance)
        updated_device = get_mongoengine_model_from_id(_id)
        old_device = [x for x in self.devices if x.mac_adress == updated_device.mac_adress][0]
    else:  # Complete update from the database
        updated_device = get_mongoengine_model_from_pymongo_obj(mod)

        old_device = [x for x in self.devices if x.mac_adress == mod["mac_adress"]][0]
    idx = self.devices.index(old_device)
    self.devices[idx] = updated_device
    # self.endResetModel()

    top_left = self.index(0, 0)
    bottom_right = self.index(self.rowCount() - 1,
                              self.columnCount() - 1)
    self.dataChanged.emit(top_left, bottom_right)