test_patch_qcombobox.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. from __future__ import absolute_import
  2. import os
  3. import sys
  4. import pytest
  5. from qtpy import PYSIDE2, QtGui, QtWidgets
  6. PY3 = sys.version[0] == "3"
  7. def get_qapp(icon_path=None):
  8. qapp = QtWidgets.QApplication.instance()
  9. if qapp is None:
  10. qapp = QtWidgets.QApplication([''])
  11. return qapp
  12. class Data(object):
  13. """
  14. Test class to store in userData. The __getitem__ is needed in order to
  15. reproduce the segmentation fault.
  16. """
  17. def __getitem__(self, item):
  18. raise ValueError("Failing")
  19. @pytest.mark.skipif(PY3 or (PYSIDE2 and os.environ.get('CI', None) is not None),
  20. reason="It segfaults in Python 3 and in our CIs with PySide2")
  21. def test_patched_qcombobox():
  22. """
  23. In PySide, using Python objects as userData in QComboBox causes
  24. Segmentation faults under certain conditions. Even in cases where it
  25. doesn't, findData does not work correctly. Likewise, findData also
  26. does not work correctly with Python objects when using PyQt4. On the
  27. other hand, PyQt5 deals with this case correctly. We therefore patch
  28. QComboBox when using PyQt4 and PySide to avoid issues.
  29. """
  30. app = get_qapp()
  31. data1 = Data()
  32. data2 = Data()
  33. data3 = Data()
  34. data4 = Data()
  35. data5 = Data()
  36. data6 = Data()
  37. icon1 = QtGui.QIcon()
  38. icon2 = QtGui.QIcon()
  39. widget = QtWidgets.QComboBox()
  40. widget.addItem('a', data1)
  41. widget.insertItem(0, 'b', data2)
  42. widget.addItem('c', data1)
  43. widget.setItemData(2, data3)
  44. widget.addItem(icon1, 'd', data4)
  45. widget.insertItem(3, icon2, 'e', data5)
  46. widget.addItem(icon1, 'f')
  47. widget.insertItem(5, icon2, 'g')
  48. widget.show()
  49. assert widget.findData(data1) == 1
  50. assert widget.findData(data2) == 0
  51. assert widget.findData(data3) == 2
  52. assert widget.findData(data4) == 4
  53. assert widget.findData(data5) == 3
  54. assert widget.findData(data6) == -1
  55. assert widget.itemData(0) == data2
  56. assert widget.itemData(1) == data1
  57. assert widget.itemData(2) == data3
  58. assert widget.itemData(3) == data5
  59. assert widget.itemData(4) == data4
  60. assert widget.itemData(5) is None
  61. assert widget.itemData(6) is None
  62. assert widget.itemText(0) == 'b'
  63. assert widget.itemText(1) == 'a'
  64. assert widget.itemText(2) == 'c'
  65. assert widget.itemText(3) == 'e'
  66. assert widget.itemText(4) == 'd'
  67. assert widget.itemText(5) == 'g'
  68. assert widget.itemText(6) == 'f'
  69. @pytest.mark.skipif((PYSIDE2 and os.environ.get('CI', None) is not None),
  70. reason="It segfaults in our CIs with PYSIDE2")
  71. def test_model_item():
  72. """
  73. This is a regression test for an issue that caused the call to item(0)
  74. below to trigger segmentation faults in PySide. The issue is
  75. non-deterministic when running the call once, so we include a loop to make
  76. sure that we trigger the fault.
  77. """
  78. app = get_qapp()
  79. combo = QtWidgets.QComboBox()
  80. label_data = [('a', None)]
  81. for iter in range(10000):
  82. combo.clear()
  83. for i, (label, data) in enumerate(label_data):
  84. combo.addItem(label, userData=data)
  85. model = combo.model()
  86. model.item(0)