当我从数据库收到更改通知时,我正在尝试更新我的QTableView。通知系统似乎按预期工作,但在某些情况下,它会使应用程序崩溃,但有以下异常:
Message : QObject::installEventFilter(): Cannot filter events for objects in a different thread.
在我的应用程序中,当用户尝试更新TableView中的行时,会出现一个弹出窗口询问确认。但是,如果2个人同时执行此操作,则第二个应用程序会因给定的错误消息而崩溃。
情景::
假设: 我想问题是该软件试图从非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
答案 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)