mirror of https://github.com/KLayout/klayout.git
558 lines
14 KiB
Python
558 lines
14 KiB
Python
|
|
import pya
|
|
import unittest
|
|
import os
|
|
import sys
|
|
import gc
|
|
|
|
testlog = ""
|
|
|
|
# an event filter class
|
|
|
|
class EventFilter(pya.QObject):
|
|
|
|
_log = []
|
|
|
|
def log(self):
|
|
return self._log
|
|
|
|
def eventFilter(self, obj, event):
|
|
self._log.append(type(event).__name__ + ": " + repr(event.type()))
|
|
pya.QObject.eventFilter(self, obj, event)
|
|
|
|
# QAction implementation
|
|
|
|
class MyAction(pya.QAction):
|
|
|
|
ce = None
|
|
|
|
def __init__(self, p, n):
|
|
pya.QAction.__init__(self, p)
|
|
self.objectName = n
|
|
ce = None
|
|
|
|
def childEvent(self, ev):
|
|
if self.ce:
|
|
self.ce(ev)
|
|
|
|
def on_child_event(self, _ce):
|
|
self.ce = _ce
|
|
|
|
class MyStandardItemModel(pya.QStandardItemModel):
|
|
# make this method public
|
|
def srn(self, rn):
|
|
if hasattr(self.__class__, "setRoleNames"):
|
|
self.setRoleNames(rn)
|
|
else:
|
|
self.setItemRoleNames(rn)
|
|
|
|
# Another event filter
|
|
|
|
class MyObject(pya.QObject):
|
|
|
|
ef = None
|
|
|
|
def eventFilter(self, watched, event):
|
|
if self.ef(watched, event):
|
|
return True
|
|
return pya.QObject.eventFilter(self, watched, event)
|
|
|
|
def on_event_filter(self, ef):
|
|
self.ef = ef
|
|
|
|
# The Qt binding tests
|
|
|
|
class QtBindingTest(unittest.TestCase):
|
|
|
|
def test_00(self):
|
|
|
|
# all references of PA are released now:
|
|
pass
|
|
|
|
# ...
|
|
|
|
def test_10(self):
|
|
|
|
a = MyAction(None, "a")
|
|
a.text = "mytext"
|
|
a.checkable = True
|
|
self.assertEqual(a.isChecked(), False)
|
|
a.checked = True
|
|
self.assertEqual(a.text, "mytext")
|
|
self.assertEqual(a.objectName, "a")
|
|
a.text += "."
|
|
self.assertEqual(a.text, "mytext.")
|
|
self.assertEqual(a.checked, True)
|
|
|
|
global testlog
|
|
testlog = ""
|
|
|
|
def f(checked):
|
|
global testlog
|
|
testlog += "[" + str(checked) + "]"
|
|
|
|
a.triggered(f)
|
|
self.assertEqual(testlog, "")
|
|
a.trigger() # also toggles checked state
|
|
self.assertEqual(testlog, "[False]")
|
|
testlog = ""
|
|
a.trigger() # also toggles checked state
|
|
self.assertEqual(testlog, "[True]")
|
|
|
|
def test_11(self):
|
|
|
|
a = pya.QAction(None)
|
|
aa = MyAction(a, "aa")
|
|
self.assertEqual(aa.objectName, "aa")
|
|
|
|
# destroying a will also destroy aa
|
|
a.destroy()
|
|
self.assertEqual(a.destroyed(), True)
|
|
self.assertEqual(aa.destroyed(), True)
|
|
|
|
def test_12(self):
|
|
|
|
a = pya.QAction(None)
|
|
aa = pya.QAction(a)
|
|
aa.objectName = "aa"
|
|
|
|
# destroying a will also destroy aa
|
|
a = None
|
|
|
|
self.assertEqual(aa.destroyed(), True)
|
|
|
|
def test_13(self):
|
|
|
|
a = pya.QAction(None)
|
|
aa = pya.QAction(a)
|
|
aa.objectName = "aa"
|
|
aa.text = "aatext"
|
|
|
|
cc = []
|
|
for c in a.children():
|
|
cc.append(c.objectName)
|
|
self.assertEqual(",".join(cc), "aa")
|
|
|
|
# aa now is kept by a
|
|
aa = None
|
|
|
|
# fetch aa again
|
|
for c in a.children():
|
|
if c.objectName == "aa":
|
|
aa = c
|
|
self.assertEqual(aa != None, True)
|
|
self.assertEqual(type(aa), pya.QAction)
|
|
self.assertEqual(aa.text, "aatext")
|
|
self.assertEqual(aa.destroyed(), False)
|
|
|
|
def test_20(self):
|
|
|
|
global no_event
|
|
global ce_log
|
|
|
|
no_event = False
|
|
|
|
def event_filter(watched, event):
|
|
global no_event
|
|
return no_event
|
|
|
|
ef = MyObject()
|
|
ef.on_event_filter(event_filter)
|
|
|
|
def child_event(ce):
|
|
global ce_log
|
|
ce_log.append(str(ce.added()) + ":" + ce.child().objectName)
|
|
|
|
ce_log = []
|
|
a = MyAction(None, "a")
|
|
a.on_child_event(child_event)
|
|
|
|
a.installEventFilter(ef)
|
|
|
|
aa = MyAction(None, "aa")
|
|
self.assertEqual(",".join(ce_log), "")
|
|
aa.setParent(a)
|
|
self.assertEqual(",".join(ce_log), "True:aa")
|
|
ce_log = []
|
|
|
|
# destroy aa
|
|
aa.destroy()
|
|
aa = None
|
|
self.assertEqual(",".join(ce_log), "False:aa")
|
|
ce_log = []
|
|
|
|
no_event = True
|
|
aa = MyAction(None, "aa")
|
|
aa.setParent(a)
|
|
self.assertEqual(",".join(ce_log), "")
|
|
ce_log = []
|
|
|
|
no_event = False
|
|
aa.destroy()
|
|
aa = None
|
|
self.assertEqual(",".join(ce_log), "False:aa")
|
|
ce_log = []
|
|
|
|
def test_30(self):
|
|
|
|
# dialog construction, cleanup, object dependency ...
|
|
|
|
mw = None
|
|
|
|
dialog = pya.QDialog(mw)
|
|
label = pya.QLabel(dialog)
|
|
layout = pya.QHBoxLayout(dialog)
|
|
layout.addWidget(label)
|
|
|
|
dialog = pya.QDialog(mw)
|
|
label = pya.QLabel(dialog)
|
|
layout = pya.QHBoxLayout(dialog)
|
|
layout.addWidget(label)
|
|
label.destroy()
|
|
|
|
dialog = pya.QDialog(mw)
|
|
label = pya.QLabel(dialog)
|
|
layout = pya.QHBoxLayout(dialog)
|
|
layout.addWidget(label)
|
|
layout.destroy()
|
|
|
|
dialog = pya.QDialog(mw)
|
|
label = pya.QLabel(dialog)
|
|
layout = pya.QHBoxLayout(dialog)
|
|
layout.addWidget(label)
|
|
dialog.destroy()
|
|
|
|
dialog = pya.QDialog(mw)
|
|
label = pya.QLabel(dialog)
|
|
layout = pya.QHBoxLayout(dialog)
|
|
layout.addWidget(label)
|
|
|
|
dialog = None
|
|
label = None
|
|
layout = None
|
|
|
|
def test_31(self):
|
|
|
|
# Optional arguments, enums, QFlag's
|
|
|
|
mw = None
|
|
|
|
mb = pya.QMessageBox(pya.QMessageBox.Critical, "title", "text")
|
|
self.assertEqual(mb.icon.to_i() != pya.QMessageBox.Warning.to_i(), True)
|
|
self.assertEqual(mb.icon.to_i() == pya.QMessageBox.Critical.to_i(), True)
|
|
self.assertEqual(mb.standardButtons.to_i() == pya.QMessageBox.NoButton.to_i(), True)
|
|
|
|
mb = pya.QMessageBox(pya.QMessageBox.Critical, "title", "text", pya.QMessageBox.Ok)
|
|
self.assertEqual(mb.standardButtons.to_i() == pya.QMessageBox.Ok.to_i(), True)
|
|
|
|
mb = pya.QMessageBox(pya.QMessageBox.Critical, "title", "text", pya.QMessageBox.Ok | pya.QMessageBox.Cancel)
|
|
self.assertEqual(mb.standardButtons.to_i() == pya.QMessageBox.Ok.to_i() + pya.QMessageBox.Cancel.to_i(), True)
|
|
|
|
def test_40(self):
|
|
|
|
# Lifetime management of objects/methods not using QObject.parent
|
|
# QTreeWidget (parent)/QTreeWidgetItem (child)
|
|
|
|
# constructor with parent-like argument:
|
|
tw = pya.QTreeWidget()
|
|
ti = pya.QTreeWidgetItem(tw)
|
|
# strange, but true:
|
|
self.assertEqual(ti.parent(), None)
|
|
self.assertEqual(tw.topLevelItemCount, 1)
|
|
ti = None
|
|
# gives 1, because the tree widget item is kept by
|
|
# the tree widget:
|
|
self.assertEqual(tw.topLevelItemCount, 1)
|
|
|
|
# the tree item belongs to the widget, hence it's destroyed with
|
|
# the widget
|
|
ti = tw.topLevelItem(0)
|
|
tw._destroy()
|
|
# gives true, because tw owns ti too.
|
|
self.assertEqual(ti._destroyed(), True)
|
|
|
|
# The same works for insert too
|
|
tw = pya.QTreeWidget()
|
|
ti = pya.QTreeWidgetItem()
|
|
tw.insertTopLevelItem(0, ti)
|
|
self.assertEqual(tw.topLevelItemCount, 1)
|
|
ti = None
|
|
# gives 1, because the tree widget item is kept by
|
|
# the tree widget:
|
|
self.assertEqual(tw.topLevelItemCount, 1)
|
|
|
|
# the tree item belongs to the widget, hence it's destroyed with
|
|
# the widget
|
|
ti = tw.topLevelItem(0)
|
|
tw._destroy()
|
|
# gives true, because tw owns ti
|
|
self.assertEqual(ti._destroyed(), True)
|
|
|
|
# And add:
|
|
tw = pya.QTreeWidget()
|
|
ti = pya.QTreeWidgetItem()
|
|
tw.addTopLevelItem(ti)
|
|
self.assertEqual(tw.topLevelItemCount, 1)
|
|
ti = None
|
|
# gives 1, because the tree widget item is kept by
|
|
# the tree widget:
|
|
self.assertEqual(tw.topLevelItemCount, 1)
|
|
|
|
# the tree item belongs to the widget, hence it's destroyed with
|
|
# the widget
|
|
ti = tw.topLevelItem(0)
|
|
tw._destroy()
|
|
# gives true, because tw owns ti
|
|
self.assertEqual(ti._destroyed(), True)
|
|
|
|
# But the item is released when we take it and add:
|
|
tw = pya.QTreeWidget()
|
|
ti = pya.QTreeWidgetItem()
|
|
tw.addTopLevelItem(ti)
|
|
self.assertEqual(tw.topLevelItemCount, 1)
|
|
ti = None
|
|
# gives 1, because the tree widget item is kept by
|
|
# the tree widget:
|
|
self.assertEqual(tw.topLevelItemCount, 1)
|
|
|
|
ti = tw.takeTopLevelItem(0)
|
|
tw._destroy()
|
|
# gives false, because we took ti and tw no longer owns it
|
|
self.assertEqual(ti._destroyed(), False)
|
|
|
|
# And we can destroy a child too
|
|
tw = pya.QTreeWidget()
|
|
ti = pya.QTreeWidgetItem()
|
|
tw.addTopLevelItem(ti)
|
|
self.assertEqual(tw.topLevelItemCount, 1)
|
|
ti._destroy()
|
|
self.assertEqual(tw.topLevelItemCount, 0)
|
|
|
|
def test_41(self):
|
|
|
|
# Lifetime management of objects/methods not using QObject.parent
|
|
# QTreeWidgetItem (parent)/QTreeWidgetItem (child)
|
|
|
|
# constructor with parent-like argument (supported by QObject parent/child relationship):
|
|
tw = pya.QTreeWidgetItem()
|
|
ti = pya.QTreeWidgetItem(tw)
|
|
# that's not QObject.parent - this one still is 0 (not seen by RBA)
|
|
self.assertEqual(ti.parent(), tw)
|
|
self.assertEqual(tw.childCount(), 1)
|
|
ti = None
|
|
# gives 1, because the tree widget item is kept by
|
|
# the tree widget:
|
|
self.assertEqual(tw.childCount(), 1)
|
|
|
|
# the tree item belongs to the widget, hence it's destroyed with
|
|
# the widget
|
|
ti = tw.child(0)
|
|
tw._destroy()
|
|
# gives true, because tw owns ti too.
|
|
self.assertEqual(ti._destroyed(), True)
|
|
|
|
# The same works for insert too
|
|
tw = pya.QTreeWidgetItem()
|
|
ti = pya.QTreeWidgetItem()
|
|
tw.insertChild(0, ti)
|
|
self.assertEqual(tw.childCount(), 1)
|
|
ti = None
|
|
# gives 1, because the tree widget item is kept by
|
|
# the tree widget:
|
|
self.assertEqual(tw.childCount(), 1)
|
|
|
|
# the tree item belongs to the widget, hence it's destroyed with
|
|
# the widget
|
|
ti = tw.child(0)
|
|
tw._destroy()
|
|
# gives true, because tw owns ti
|
|
self.assertEqual(ti._destroyed(), True)
|
|
|
|
# And add:
|
|
tw = pya.QTreeWidgetItem()
|
|
ti = pya.QTreeWidgetItem()
|
|
tw.addChild(ti)
|
|
self.assertEqual(tw.childCount(), 1)
|
|
ti = None
|
|
# gives 1, because the tree widget item is kept by
|
|
# the tree widget:
|
|
self.assertEqual(tw.childCount(), 1)
|
|
|
|
# the tree item belongs to the widget, hence it's destroyed with
|
|
# the widget
|
|
ti = tw.child(0)
|
|
tw._destroy()
|
|
# gives true, because tw owns ti
|
|
self.assertEqual(ti._destroyed(), True)
|
|
|
|
# But the item is released when we take it and add:
|
|
tw = pya.QTreeWidgetItem()
|
|
ti = pya.QTreeWidgetItem()
|
|
tw.addChild(ti)
|
|
self.assertEqual(tw.childCount(), 1)
|
|
ti = None
|
|
# gives 1, because the tree widget item is kept by
|
|
# the tree widget:
|
|
self.assertEqual(tw.childCount(), 1)
|
|
|
|
ti = tw.takeChild(0)
|
|
tw._destroy()
|
|
# gives false, because we took ti and tw no longer owns it
|
|
self.assertEqual(ti._destroyed(), False)
|
|
|
|
# And we can destroy a child too
|
|
tw = pya.QTreeWidgetItem()
|
|
ti = pya.QTreeWidgetItem()
|
|
tw.addChild(ti)
|
|
self.assertEqual(tw.childCount(), 1)
|
|
ti._destroy()
|
|
self.assertEqual(tw.childCount(), 0)
|
|
|
|
def test_42(self):
|
|
|
|
# QKeyEvent and related issues
|
|
|
|
ef = EventFilter()
|
|
|
|
widget = pya.QLineEdit()
|
|
widget.setText("ABC")
|
|
|
|
pya.QApplication.processEvents()
|
|
|
|
widget.installEventFilter(ef)
|
|
|
|
ke = pya.QKeyEvent(pya.QEvent.KeyPress, pya.Qt.Key_O.to_i(), pya.Qt.ShiftModifier, "O")
|
|
pya.QCoreApplication.postEvent(widget, ke)
|
|
|
|
ke = pya.QKeyEvent(pya.QEvent.KeyPress, pya.Qt.Key_Left.to_i(), pya.Qt.NoModifier)
|
|
pya.QCoreApplication.postEvent(widget, ke)
|
|
|
|
ke = pya.QKeyEvent(pya.QEvent.KeyPress, pya.Qt.Key_P.to_i(), pya.Qt.NoModifier, "p")
|
|
pya.QCoreApplication.postEvent(widget, ke)
|
|
|
|
pya.QApplication.processEvents()
|
|
|
|
s1 = "QKeyEvent: ShortcutOverride (51)\nQKeyEvent: KeyPress (6)\nQKeyEvent: ShortcutOverride (51)\nQKeyEvent: KeyPress (6)\nQKeyEvent: ShortcutOverride (51)\nQKeyEvent: KeyPress (6)"
|
|
s2 = "QKeyEvent: KeyPress (6)\nQKeyEvent: KeyPress (6)\nQKeyEvent: KeyPress (6)"
|
|
self.assertEqual("\n".join(ef.log()) == s1 or "\n".join(ef.log()) == s2, True)
|
|
ef = None
|
|
|
|
self.assertEqual(widget.text, "ABCpO")
|
|
widget = None
|
|
|
|
def test_43(self):
|
|
|
|
# QHash bindings
|
|
|
|
slm = MyStandardItemModel()
|
|
r1 = "{0: \'display\', 1: \'decoration\', 2: \'edit\', 3: \'toolTip\', 4: \'statusTip\', 5: \'whatsThis\'}"
|
|
r2 = "{0L: \'display\', 1L: \'decoration\', 2L: \'edit\', 3L: \'toolTip\', 4L: \'statusTip\', 5L: \'whatsThis\'}"
|
|
self.assertEqual(str(slm.roleNames()) == r1 or str(slm.roleNames()) == r2, True)
|
|
rn = slm.roleNames()
|
|
rn[7] = "blabla"
|
|
slm.srn(rn)
|
|
r1 = "{0: \'display\', 1: \'decoration\', 2: \'edit\', 3: \'toolTip\', 4: \'statusTip\', 5: \'whatsThis\', 7: \'blabla\'}"
|
|
r2 = "{0L: \'display\', 1L: \'decoration\', 2L: \'edit\', 3L: \'toolTip\', 4L: \'statusTip\', 5L: \'whatsThis\', 7L: \'blabla\'}"
|
|
self.assertEqual(str(slm.roleNames()) == r1 or str(slm.roleNames()) == r2, True)
|
|
|
|
def test_44(self):
|
|
|
|
# Ability to monitor native child objects
|
|
|
|
parent = pya.QScrollArea()
|
|
parent.show() # this makes resize actually change the widget's size
|
|
child = parent.viewport
|
|
|
|
# ensure parent and child are working
|
|
self.assertEqual(child._destroyed(), False)
|
|
|
|
parent.resize(200, 200)
|
|
self.assertEqual(child.width > 100, True)
|
|
self.assertEqual(child.height > 100, True)
|
|
|
|
parent.resize(100, 100)
|
|
self.assertEqual(child.width < 100, True)
|
|
self.assertEqual(child.height < 100, True)
|
|
|
|
# now if we delete the parent, the child needs to become disconnected
|
|
|
|
parent._destroy()
|
|
self.assertEqual(parent._destroyed(), True)
|
|
self.assertEqual(child._destroyed(), True)
|
|
|
|
def test_45(self):
|
|
|
|
triggered = ""
|
|
|
|
class TriggerLog:
|
|
triggered = ""
|
|
def triggered1(self, b):
|
|
if b:
|
|
self.triggered += "1"
|
|
else:
|
|
self.triggered += "0"
|
|
def triggered0(self):
|
|
self.triggered += "*"
|
|
|
|
# Ability to connect to signals while ignoring arguments and
|
|
# to emit signals
|
|
|
|
b = pya.QPushButton()
|
|
|
|
log = TriggerLog()
|
|
|
|
b.clicked(log.triggered1)
|
|
|
|
self.assertEqual(log.triggered, "")
|
|
b.emit_clicked(True)
|
|
self.assertEqual(log.triggered, "1")
|
|
b.emit_clicked(False)
|
|
self.assertEqual(log.triggered, "10")
|
|
|
|
b.clicked(log.triggered0)
|
|
|
|
b.emit_clicked(True)
|
|
self.assertEqual(log.triggered, "10*")
|
|
b.emit_clicked(False)
|
|
self.assertEqual(log.triggered, "10**")
|
|
|
|
# We do the same with free functions since they behave differently in Python:
|
|
|
|
global trigger_log
|
|
trigger_log = ""
|
|
|
|
def triggered_f0():
|
|
global trigger_log
|
|
trigger_log += "x"
|
|
|
|
def triggered_f1(b):
|
|
global trigger_log
|
|
if b:
|
|
trigger_log += "+"
|
|
else:
|
|
trigger_log += "-"
|
|
|
|
b.clicked(triggered_f1)
|
|
|
|
self.assertEqual(trigger_log, "")
|
|
b.emit_clicked(True)
|
|
self.assertEqual(trigger_log, "+")
|
|
b.emit_clicked(False)
|
|
self.assertEqual(trigger_log, "+-")
|
|
|
|
b.clicked(triggered_f0)
|
|
|
|
b.emit_clicked(True)
|
|
self.assertEqual(trigger_log, "+-x")
|
|
b.emit_clicked(False)
|
|
self.assertEqual(trigger_log, "+-xx")
|
|
|
|
|
|
# run unit tests
|
|
if __name__ == '__main__':
|
|
suite = unittest.TestLoader().loadTestsFromTestCase(QtBindingTest)
|
|
|
|
if not unittest.TextTestRunner(verbosity = 1).run(suite).wasSuccessful():
|
|
sys.exit(1)
|
|
|