ctkVTKObjectEventsObserver.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. /*=========================================================================
  2. Library: CTK
  3. Copyright (c) Kitware Inc.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0.txt
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. =========================================================================*/
  14. // Qt includes
  15. #include <QStringList>
  16. #include <QVariant>
  17. #include <QList>
  18. #include <QHash>
  19. #include <QDebug>
  20. #include <QMultiMap>
  21. // CTK includes
  22. #include "ctkVTKObjectEventsObserver.h"
  23. #include "ctkVTKConnection.h"
  24. // VTK includes
  25. #include <vtkObject.h>
  26. //-----------------------------------------------------------------------------
  27. CTK_SINGLETON_DEFINE(ctkVTKConnectionFactory)
  28. //-----------------------------------------------------------------------------
  29. // ctkVTKConnectionFactory
  30. //-----------------------------------------------------------------------------
  31. ctkVTKConnectionFactory* ctkVTKConnectionFactory::instance()
  32. {
  33. return Self::Instance;
  34. }
  35. //-----------------------------------------------------------------------------
  36. void ctkVTKConnectionFactory::setInstance(ctkVTKConnectionFactory* newInstance)
  37. {
  38. if (!newInstance)
  39. {
  40. qCritical() << "ctkVTKConnectionFactory::setInstance - Failed to set a null instance !";
  41. return;
  42. }
  43. delete Self::Instance;
  44. Self::Instance = newInstance;
  45. }
  46. //-----------------------------------------------------------------------------
  47. ctkVTKConnectionFactory::ctkVTKConnectionFactory()
  48. {
  49. }
  50. //-----------------------------------------------------------------------------
  51. ctkVTKConnectionFactory::~ctkVTKConnectionFactory()
  52. {
  53. }
  54. //-----------------------------------------------------------------------------
  55. ctkVTKConnection* ctkVTKConnectionFactory::createConnection(ctkVTKObjectEventsObserver* parent)const
  56. {
  57. return new ctkVTKConnection(parent);
  58. }
  59. //-----------------------------------------------------------------------------
  60. // ctkVTKObjectEventsObserverPrivate
  61. //-----------------------------------------------------------------------------
  62. class ctkVTKObjectEventsObserverPrivate
  63. {
  64. Q_DECLARE_PUBLIC(ctkVTKObjectEventsObserver);
  65. protected:
  66. ctkVTKObjectEventsObserver* const q_ptr;
  67. public:
  68. /// ConnectionIndexType:
  69. /// key = hash (generated from the connection parameters)
  70. /// value = QT connection object name
  71. typedef QMultiMap<unsigned long, ctkVTKConnection*> ConnectionIndexType;
  72. ctkVTKObjectEventsObserverPrivate(ctkVTKObjectEventsObserver& object);
  73. ///
  74. /// Return a reference toward the corresponding connection or 0 if doesn't exist
  75. ctkVTKConnection* findConnection(const QString& id)const;
  76. ///
  77. /// Return a reference toward the corresponding connection or 0 if doesn't exist
  78. ctkVTKConnection* findConnection(vtkObject* vtk_obj, unsigned long vtk_event,
  79. const QObject* qt_obj, const char* qt_slot)const;
  80. ///
  81. /// Return all the references that match the given parameters
  82. QList<ctkVTKConnection*> findConnections(vtkObject* vtk_obj, unsigned long vtk_event,
  83. const QObject* qt_obj, const char* qt_slot)const;
  84. inline QList<ctkVTKConnection*> connections()const
  85. {
  86. Q_Q(const ctkVTKObjectEventsObserver);
  87. return q->findChildren<ctkVTKConnection*>();
  88. }
  89. /// Generate a number from the connection parameters that is most often
  90. /// unique for each connection. The slot parameter is not used, as probably
  91. /// the same objects with the same event are not connected to different slot.
  92. static unsigned long generateConnectionIndexHash(const vtkObject* vtk_obj, unsigned long vtk_event, const QObject* qt_obj)
  93. {
  94. return reinterpret_cast<const unsigned char*>(vtk_obj)-reinterpret_cast<const unsigned char*>(qt_obj)+vtk_event;
  95. }
  96. bool StrictTypeCheck;
  97. bool AllBlocked;
  98. bool ObserveDeletion;
  99. /// An associative container to speed up findConnection.
  100. /// No need to iterate through all the existing connections and check if it is
  101. /// equal with the searched one.
  102. /// All the connections are present in the index (to allow quick decision that
  103. /// a connection does not exist), but a lazy deletion method is used (items
  104. /// not necessarily removed from the index immediately when a connection is deleted).
  105. mutable ConnectionIndexType ConnectionIndex;
  106. };
  107. //-----------------------------------------------------------------------------
  108. // ctkVTKObjectEventsObserverPrivate methods
  109. //-----------------------------------------------------------------------------
  110. ctkVTKObjectEventsObserverPrivate::ctkVTKObjectEventsObserverPrivate(ctkVTKObjectEventsObserver& object)
  111. :q_ptr(&object)
  112. {
  113. this->StrictTypeCheck = false;
  114. this->AllBlocked = false;
  115. // ObserveDeletion == false hasn't been that well tested...
  116. this->ObserveDeletion = true;
  117. }
  118. //-----------------------------------------------------------------------------
  119. ctkVTKConnection*
  120. ctkVTKObjectEventsObserverPrivate::findConnection(const QString& id)const
  121. {
  122. foreach(ctkVTKConnection* connection, this->connections())
  123. {
  124. if (connection->id() == id)
  125. {
  126. return connection;
  127. }
  128. }
  129. return 0;
  130. }
  131. //-----------------------------------------------------------------------------
  132. ctkVTKConnection*
  133. ctkVTKObjectEventsObserverPrivate::findConnection(
  134. vtkObject* vtk_obj, unsigned long vtk_event,
  135. const QObject* qt_obj, const char* qt_slot)const
  136. {
  137. // Linear search for connections is prohibitively slow when observing many objects
  138. // (because connection->isEqual is slow)
  139. Q_Q(const ctkVTKObjectEventsObserver);
  140. if(vtk_obj != NULL && qt_slot != NULL &&
  141. qt_obj != NULL && vtk_event != vtkCommand::NoEvent)
  142. {
  143. // All information is specified, so we can use the index to find the connection
  144. unsigned long hash=generateConnectionIndexHash(vtk_obj, vtk_event, qt_obj);
  145. ConnectionIndexType::iterator connectionIt = this->ConnectionIndex.find(hash);
  146. while (connectionIt != this->ConnectionIndex.end() && connectionIt.key() == hash)
  147. {
  148. ctkVTKConnection* connection=connectionIt.value();
  149. if (!q->children().contains(connection))
  150. {
  151. // connection has been deleted, so remove it from the index
  152. connectionIt=this->ConnectionIndex.erase(connectionIt);
  153. continue;
  154. }
  155. if (connection->isEqual(vtk_obj, vtk_event, qt_obj, qt_slot))
  156. {
  157. return connection;
  158. }
  159. ++connectionIt;
  160. }
  161. return 0;
  162. }
  163. foreach (ctkVTKConnection* connection, this->connections())
  164. {
  165. if (connection->isEqual(vtk_obj, vtk_event, qt_obj, qt_slot))
  166. {
  167. return connection;
  168. }
  169. }
  170. return 0;
  171. }
  172. //-----------------------------------------------------------------------------
  173. QList<ctkVTKConnection*>
  174. ctkVTKObjectEventsObserverPrivate::findConnections(
  175. vtkObject* vtk_obj, unsigned long vtk_event,
  176. const QObject* qt_obj, const char* qt_slot)const
  177. {
  178. QList<ctkVTKConnection*> foundConnections;
  179. if(vtk_obj != NULL && qt_slot != NULL &&
  180. qt_obj != NULL && vtk_event != vtkCommand::NoEvent)
  181. {
  182. // All information is specified, so we can use the index to find the connection
  183. ctkVTKConnection* connection=findConnection(vtk_obj, vtk_event, qt_obj, qt_slot);
  184. if (connection)
  185. {
  186. foundConnections.append(connection);
  187. }
  188. return foundConnections;
  189. }
  190. // Loop through all connection
  191. foreach (ctkVTKConnection* connection, this->connections())
  192. {
  193. if (connection->isEqual(vtk_obj, vtk_event, qt_obj, qt_slot))
  194. {
  195. foundConnections.append(connection);
  196. }
  197. }
  198. return foundConnections;
  199. }
  200. //-----------------------------------------------------------------------------
  201. // ctkVTKObjectEventsObserver methods
  202. //-----------------------------------------------------------------------------
  203. ctkVTKObjectEventsObserver::ctkVTKObjectEventsObserver(QObject* _parent):Superclass(_parent)
  204. , d_ptr(new ctkVTKObjectEventsObserverPrivate(*this))
  205. {
  206. this->setProperty("QVTK_OBJECT", true);
  207. }
  208. //-----------------------------------------------------------------------------
  209. ctkVTKObjectEventsObserver::~ctkVTKObjectEventsObserver()
  210. {
  211. }
  212. //-----------------------------------------------------------------------------
  213. void ctkVTKObjectEventsObserver::printAdditionalInfo()
  214. {
  215. this->Superclass::dumpObjectInfo();
  216. Q_D(ctkVTKObjectEventsObserver);
  217. qDebug() << "ctkVTKObjectEventsObserver:" << this << endl
  218. << " AllBlocked:" << d->AllBlocked << endl
  219. << " Parent:" << (this->parent()?this->parent()->objectName():"NULL") << endl
  220. << " Connection count:" << d->connections().count();
  221. // Loop through all connection
  222. foreach (const ctkVTKConnection* connection, d->connections())
  223. {
  224. qDebug() << *connection;
  225. }
  226. }
  227. //-----------------------------------------------------------------------------
  228. bool ctkVTKObjectEventsObserver::strictTypeCheck()const
  229. {
  230. Q_D(const ctkVTKObjectEventsObserver);
  231. return d->StrictTypeCheck;
  232. }
  233. //-----------------------------------------------------------------------------
  234. void ctkVTKObjectEventsObserver::setStrictTypeCheck(bool check)
  235. {
  236. Q_D(ctkVTKObjectEventsObserver);
  237. d->StrictTypeCheck = check;
  238. }
  239. //-----------------------------------------------------------------------------
  240. QString ctkVTKObjectEventsObserver::addConnection(vtkObject* old_vtk_obj, vtkObject* vtk_obj,
  241. unsigned long vtk_event, const QObject* qt_obj, const char* qt_slot, float priority,
  242. Qt::ConnectionType connectionType)
  243. {
  244. Q_D(ctkVTKObjectEventsObserver);
  245. if (old_vtk_obj)
  246. {
  247. // Check that old_object and new_object are the same type
  248. // If you have a crash when accessing old_vtk_obj->GetClassName(), that means
  249. // old_vtk_obj has been deleted and you should probably have keep
  250. // old_vtk_obj into a vtkWeakPointer:
  251. // vtkWeakPointer<vtkObject> obj1 = myobj1;
  252. // this->addConnection(obj1, vtkCommand::Modified...)
  253. // myobj1->Delete();
  254. // vtkWeakPointer<vtkObject> obj2 = myobj2;
  255. // this->addConnection(obj1, obj2, vtkCommand::Modified...)
  256. // ...
  257. // Or just call addConnection with a new
  258. // vtk_obj of 0 before the vtk_obj is deleted.
  259. // vtkObject* obj1 = vtkObject::New();
  260. // this->addConnection(obj1, vtkCommand::Modified...)
  261. // this->addConnection(obj1, 0, vtkCommand::Modified...)
  262. // obj1->Delete();
  263. // vtkObject* obj2 = vtkObject::New();
  264. // this->addConnection(0, obj2, vtkCommand::Modified...)
  265. // ...
  266. if (d->StrictTypeCheck && vtk_obj
  267. && !vtk_obj->IsA(old_vtk_obj->GetClassName()))
  268. {
  269. qWarning() << "Previous vtkObject (type:" << old_vtk_obj->GetClassName()
  270. << ") to disconnect"
  271. << "and new vtkObject (type:" << vtk_obj->GetClassName()
  272. << ") to connect"
  273. << "have a different type.";
  274. return QString();
  275. }
  276. // Disconnect old vtkObject
  277. this->removeConnection(old_vtk_obj, vtk_event, qt_obj, qt_slot);
  278. }
  279. return this->addConnection(
  280. vtk_obj, vtk_event, qt_obj, qt_slot, priority, connectionType);
  281. }
  282. //-----------------------------------------------------------------------------
  283. QString ctkVTKObjectEventsObserver::reconnection(vtkObject* vtk_obj,
  284. unsigned long vtk_event, const QObject* qt_obj,
  285. const char* qt_slot, float priority, Qt::ConnectionType connectionType)
  286. {
  287. this->removeConnection(0, vtk_event, qt_obj, qt_slot);
  288. return this->addConnection(
  289. vtk_obj, vtk_event, qt_obj, qt_slot, priority, connectionType);
  290. }
  291. //-----------------------------------------------------------------------------
  292. QString ctkVTKObjectEventsObserver::addConnection(vtkObject* vtk_obj, unsigned long vtk_event,
  293. const QObject* qt_obj, const char* qt_slot, float priority, Qt::ConnectionType connectionType)
  294. {
  295. Q_D(ctkVTKObjectEventsObserver);
  296. // If no vtk_obj is provided, there is no way we can create a connection.
  297. if (!vtk_obj)
  298. {
  299. return QString();
  300. }
  301. if (!ctkVTKConnection::isValid(vtk_obj, vtk_event, qt_obj, qt_slot))
  302. {
  303. qDebug() << "ctkVTKObjectEventsObserver::addConnection(...) - Invalid parameters - "
  304. << ctkVTKConnection::shortDescription(vtk_obj, vtk_event, qt_obj, qt_slot);
  305. return QString();
  306. }
  307. // Check if such event is already observed
  308. if (this->containsConnection(vtk_obj, vtk_event, qt_obj, qt_slot))
  309. {
  310. // if you need to have more than 1 connection, then it's probably time to
  311. // add the same mechanism than Qt does: Qt::UniqueConnection
  312. //qWarning() << "ctkVTKObjectEventsObserver::addConnection - [vtkObject:"
  313. // << vtk_obj->GetClassName()
  314. // << ", event:" << vtk_event << "]"
  315. // << " is already connected with [qObject:" << qt_obj->objectName()
  316. // << ", slot:" << qt_slot << "]";
  317. return QString();
  318. }
  319. // Instantiate a new connection, set its parameters and add it to the list
  320. ctkVTKConnection * connection = ctkVTKConnectionFactory::instance()->createConnection(this);
  321. d->ConnectionIndex.insert(ctkVTKObjectEventsObserverPrivate::generateConnectionIndexHash(vtk_obj, vtk_event, qt_obj), connection);
  322. connection->observeDeletion(d->ObserveDeletion);
  323. connection->setup(vtk_obj, vtk_event, qt_obj, qt_slot, priority, connectionType);
  324. // If required, establish connection
  325. connection->setBlocked(d->AllBlocked);
  326. return connection->id();
  327. }
  328. //-----------------------------------------------------------------------------
  329. bool ctkVTKObjectEventsObserver::blockAllConnections(bool block)
  330. {
  331. Q_D(ctkVTKObjectEventsObserver);
  332. if (d->AllBlocked == block)
  333. {
  334. return d->AllBlocked;
  335. }
  336. bool oldAllBlocked = d->AllBlocked;
  337. foreach (ctkVTKConnection* connection, d->connections())
  338. {
  339. connection->setBlocked(block);
  340. }
  341. d->AllBlocked = block;
  342. return oldAllBlocked;
  343. }
  344. //-----------------------------------------------------------------------------
  345. bool ctkVTKObjectEventsObserver::connectionsBlocked()const
  346. {
  347. Q_D(const ctkVTKObjectEventsObserver);
  348. return d->AllBlocked;
  349. }
  350. //-----------------------------------------------------------------------------
  351. bool ctkVTKObjectEventsObserver::blockConnection(const QString& id, bool blocked)
  352. {
  353. Q_D(ctkVTKObjectEventsObserver);
  354. ctkVTKConnection* connection = d->findConnection(id);
  355. if (connection == 0)
  356. {
  357. qWarning() << "no connection for id " << id;
  358. return false;
  359. }
  360. bool oldBlocked = connection->isBlocked();
  361. connection->setBlocked(blocked);
  362. return oldBlocked;
  363. }
  364. //-----------------------------------------------------------------------------
  365. int ctkVTKObjectEventsObserver::blockConnection(bool block, vtkObject* vtk_obj,
  366. unsigned long vtk_event, const QObject* qt_obj)
  367. {
  368. Q_D(ctkVTKObjectEventsObserver);
  369. if (!vtk_obj)
  370. {
  371. qDebug() << "ctkVTKObjectEventsObserver::blockConnectionRecursive"
  372. << "- Failed to " << (block?"block":"unblock") <<" connection"
  373. << "- vtkObject is NULL";
  374. return 0;
  375. }
  376. QList<ctkVTKConnection*> connections =
  377. d->findConnections(vtk_obj, vtk_event, qt_obj, 0);
  378. foreach (ctkVTKConnection* connection, connections)
  379. {
  380. connection->setBlocked(block);
  381. }
  382. return connections.size();
  383. }
  384. //-----------------------------------------------------------------------------
  385. int ctkVTKObjectEventsObserver::removeConnection(vtkObject* vtk_obj, unsigned long vtk_event,
  386. const QObject* qt_obj, const char* qt_slot)
  387. {
  388. Q_D(ctkVTKObjectEventsObserver);
  389. QList<ctkVTKConnection*> connections =
  390. d->findConnections(vtk_obj, vtk_event, qt_obj, qt_slot);
  391. foreach (ctkVTKConnection* connection, connections)
  392. {
  393. delete connection;
  394. }
  395. // Only remove shadow connections (connections in the index without a corresponding actual connection)
  396. // from the index if the index size grew too big (shadow elements ratio >50% and minimum 100)
  397. if (static_cast<int>(d->ConnectionIndex.size())>100+children().count()*2)
  398. {
  399. for (ctkVTKObjectEventsObserverPrivate::ConnectionIndexType::iterator connectionIt=d->ConnectionIndex.begin();
  400. connectionIt!=d->ConnectionIndex.end();
  401. /*upon deletion the increment is done already, so don't increment here*/)
  402. {
  403. ctkVTKConnection* connection=connectionIt.value();
  404. if (!children().contains(connection))
  405. {
  406. // connection has been deleted, so remove it from the index
  407. connectionIt=d->ConnectionIndex.erase(connectionIt);
  408. continue;
  409. }
  410. ++connectionIt;
  411. }
  412. }
  413. return connections.count();
  414. }
  415. //-----------------------------------------------------------------------------
  416. int ctkVTKObjectEventsObserver::removeAllConnections()
  417. {
  418. return this->removeConnection(0, vtkCommand::NoEvent, 0, 0);
  419. }
  420. //-----------------------------------------------------------------------------
  421. bool ctkVTKObjectEventsObserver::containsConnection(vtkObject* vtk_obj, unsigned long vtk_event,
  422. const QObject* qt_obj, const char* qt_slot)const
  423. {
  424. Q_D(const ctkVTKObjectEventsObserver);
  425. return (d->findConnection(vtk_obj, vtk_event, qt_obj, qt_slot) != 0);
  426. }