diff options
Diffstat (limited to 'sources/pyside6/tests/signals')
41 files changed, 743 insertions, 515 deletions
diff --git a/sources/pyside6/tests/signals/CMakeLists.txt b/sources/pyside6/tests/signals/CMakeLists.txt index 14936869f..ff342adc7 100644 --- a/sources/pyside6/tests/signals/CMakeLists.txt +++ b/sources/pyside6/tests/signals/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + PYSIDE_TEST(args_dont_match_test.py) PYSIDE_TEST(bug_79.py) PYSIDE_TEST(bug_189.py) @@ -13,6 +16,7 @@ PYSIDE_TEST(leaking_signal_test.py) PYSIDE_TEST(multiple_connections_gui_test.py) PYSIDE_TEST(multiple_connections_test.py) PYSIDE_TEST(pysignal_test.py) +PYSIDE_TEST(qobject_callable_connect_test.py) PYSIDE_TEST(qobject_destroyed_test.py) PYSIDE_TEST(qobject_receivers_test.py) PYSIDE_TEST(qobject_sender_test.py) @@ -29,11 +33,12 @@ PYSIDE_TEST(signal2signal_connect_test.py) PYSIDE_TEST(signal_across_threads.py) PYSIDE_TEST(signal_autoconnect_test.py) PYSIDE_TEST(signal_connectiontype_support_test.py) -PYSIDE_TEST(signal_enum_test.py) PYSIDE_TEST(signal_emission_gui_test.py) PYSIDE_TEST(signal_emission_test.py) +PYSIDE_TEST(signal_enum_test.py) PYSIDE_TEST(signal_func_test.py) PYSIDE_TEST(signal_manager_refcount_test.py) +PYSIDE_TEST(signal_newenum_test.py) PYSIDE_TEST(signal_number_limit_test.py) PYSIDE_TEST(signal_object_test.py) PYSIDE_TEST(signal_signature_test.py) diff --git a/sources/pyside6/tests/signals/anonymous_slot_leak_test.py b/sources/pyside6/tests/signals/anonymous_slot_leak_test.py new file mode 100644 index 000000000..560a08659 --- /dev/null +++ b/sources/pyside6/tests/signals/anonymous_slot_leak_test.py @@ -0,0 +1,55 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from functools import partial +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from init_paths import init_test_paths +init_test_paths(False) + +from PySide6.QtWidgets import QWidget +from helper.usesqapplication import UsesQApplication + + +have_debug = hasattr(sys, "gettotalrefcount") + + +class LeakerLambda(): + def __init__(self, widget): + widget.windowIconChanged.connect(lambda *args: None) + + +class LeakerFunctoolsPartial(): + def __init__(self, widget): + widget.windowIconChanged.connect(partial(int, 0)) + + +class TestBugPYSIDE2299(UsesQApplication): + def leak(self, leaker): + widget = QWidget() + + # Warm-up + leaker(widget) + + refs_before = sys.gettotalrefcount() + for _ in range(1000): + leaker(widget) + refs_after = sys.gettotalrefcount() + + self.assertAlmostEqual(refs_after - refs_before, 0, delta=10) + + @unittest.skipUnless(have_debug, "You need a debug build") + def test_lambda(self): + self.leak(LeakerLambda) + + @unittest.skipUnless(have_debug, "You need a debug build") + def test_functools_partial(self): + self.leak(LeakerFunctoolsPartial) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/pyside6/tests/signals/args_dont_match_test.py b/sources/pyside6/tests/signals/args_dont_match_test.py index e200cedbc..4f56be348 100644 --- a/sources/pyside6/tests/signals/args_dont_match_test.py +++ b/sources/pyside6/tests/signals/args_dont_match_test.py @@ -11,7 +11,11 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from init_paths import init_test_paths init_test_paths(False) -from PySide6.QtCore import QObject, SIGNAL +from PySide6.QtCore import QObject, Signal + + +class Sender(QObject): + the_signal = Signal(int, int, int) class ArgsDontMatch(unittest.TestCase): @@ -21,9 +25,9 @@ class ArgsDontMatch(unittest.TestCase): def testConnectSignalToSlotWithLessArgs(self): self.ok = False - obj1 = QObject() - QObject.connect(obj1, SIGNAL('the_signal(int, int, int)'), self.callback) - obj1.emit(SIGNAL('the_signal(int, int, int)'), 1, 2, 3) + obj1 = Sender() + obj1.the_signal.connect(self.callback) + obj1.the_signal.emit(1, 2, 3) self.assertTrue(self.ok) diff --git a/sources/pyside6/tests/signals/bug_311.py b/sources/pyside6/tests/signals/bug_311.py index 5f8af81c1..e27476172 100644 --- a/sources/pyside6/tests/signals/bug_311.py +++ b/sources/pyside6/tests/signals/bug_311.py @@ -12,7 +12,7 @@ from init_paths import init_test_paths init_test_paths(False) from PySide6.QtCore import QDate, QObject, Signal -from helper.usesqcoreapplication import UsesQCoreApplication +from helper.usesqapplication import UsesQApplication class DerivedDate(QDate): @@ -26,7 +26,7 @@ class Emitter(QObject): tupleSignal = Signal(tuple) -class SignaltoSignalTest(UsesQCoreApplication): +class SignaltoSignalTest(UsesQApplication): def myCb(self, dt): self._dt = dt @@ -52,4 +52,3 @@ class SignaltoSignalTest(UsesQCoreApplication): if __name__ == '__main__': unittest.main() - diff --git a/sources/pyside6/tests/signals/bug_312.py b/sources/pyside6/tests/signals/bug_312.py index de1a2f6a8..80d56a020 100644 --- a/sources/pyside6/tests/signals/bug_312.py +++ b/sources/pyside6/tests/signals/bug_312.py @@ -11,44 +11,29 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from init_paths import init_test_paths init_test_paths(False) -from PySide6.QtCore import QObject, SIGNAL +from PySide6.QtCore import QObject, Signal MAX_LOOPS = 5 MAX_OBJECTS = 200 -class Dummy(object): - def __init__(self, parent): - self._parent = parent - - def callback(self): - self._called = True +class Sender(QObject): + fire = Signal() class MultipleSlots(unittest.TestCase): def myCB(self): self._count += 1 - """ - def testUnboundSignal(self): - o = QObject() - self._count = 0 - for i in range(MAX_OBJECTS): - QObject.connect(o, SIGNAL("fire()"), lambda: self.myCB()) - - o.emit(SIGNAL("fire()")) - self.assertEqual(self._count, MAX_OBJECTS) - - """ def testDisconnectCleanup(self): for c in range(MAX_LOOPS): self._count = 0 self._senders = [] for i in range(MAX_OBJECTS): - o = QObject() - QObject.connect(o, SIGNAL("fire()"), lambda: self.myCB()) + o = Sender() + o.fire.connect(lambda: self.myCB()) self._senders.append(o) - o.emit(SIGNAL("fire()")) + o.fire.emit() self.assertEqual(self._count, MAX_OBJECTS) @@ -58,5 +43,3 @@ class MultipleSlots(unittest.TestCase): if __name__ == '__main__': unittest.main() - - diff --git a/sources/pyside6/tests/signals/bug_319.py b/sources/pyside6/tests/signals/bug_319.py index aaa5ea991..657733afb 100644 --- a/sources/pyside6/tests/signals/bug_319.py +++ b/sources/pyside6/tests/signals/bug_319.py @@ -12,7 +12,7 @@ from init_paths import init_test_paths init_test_paths(False) from PySide6.QtCore import QObject, Signal, Slot -from helper.usesqcoreapplication import UsesQCoreApplication +from helper.usesqapplication import UsesQApplication class Listener(QObject): @@ -31,7 +31,7 @@ class Communicate(QObject): speak = Signal(tuple) -class SignaltoSignalTest(UsesQCoreApplication): +class SignaltoSignalTest(UsesQApplication): def testBug(self): someone = Communicate() someone2 = Listener() @@ -45,4 +45,3 @@ class SignaltoSignalTest(UsesQCoreApplication): if __name__ == '__main__': unittest.main() - diff --git a/sources/pyside6/tests/signals/bug_79.py b/sources/pyside6/tests/signals/bug_79.py index 54bd1f076..77ac621d5 100644 --- a/sources/pyside6/tests/signals/bug_79.py +++ b/sources/pyside6/tests/signals/bug_79.py @@ -30,7 +30,7 @@ class ConnectTest(unittest.TestCase): def testNoLeaks_ConnectAndDisconnect(self): self._called = None - app = QApplication([]) + app = QApplication([]) # noqa: F841 o = QTreeView() o.setModel(QStandardItemModel()) o.selectionModel().destroyed.connect(self.callback) @@ -38,6 +38,11 @@ class ConnectTest(unittest.TestCase): gc.collect() # if this is no debug build, then we check at least that # we do not crash any longer. + for idx in range(200): + # PYSIDE-2230: Warm-up is necessary before measuring, because + # the code changes the constant parts after some time. + o.selectionModel().destroyed.connect(self.callback) + o.selectionModel().destroyed.disconnect(self.callback) if not skiptest: total = gettotalrefcount() for idx in range(1000): diff --git a/sources/pyside6/tests/signals/decorators_test.py b/sources/pyside6/tests/signals/decorators_test.py index 5b3b54690..b29339ee4 100644 --- a/sources/pyside6/tests/signals/decorators_test.py +++ b/sources/pyside6/tests/signals/decorators_test.py @@ -11,7 +11,11 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from init_paths import init_test_paths init_test_paths(False) -from PySide6.QtCore import QObject, Slot, SIGNAL, SLOT +from PySide6.QtCore import QObject, Slot, Signal + + +class Sender(QObject): + mySignal = Signal() class MyObject(QObject): @@ -57,9 +61,10 @@ class StaticMetaObjectTest(unittest.TestCase): self.assertTrue(m.indexOfSlot('mySlot4(QString,int)') > 0) def testEmission(self): + sender = Sender() o = MyObject() - o.connect(SIGNAL("mySignal()"), o, SLOT("mySlot()")) - o.emit(SIGNAL("mySignal()")) + sender.mySignal.connect(o.mySlot) + sender.mySignal.emit() self.assertTrue(o._slotCalledCount == 1) def testResult(self): diff --git a/sources/pyside6/tests/signals/invalid_callback_test.py b/sources/pyside6/tests/signals/invalid_callback_test.py index 0d7e574ef..2788c1d1a 100644 --- a/sources/pyside6/tests/signals/invalid_callback_test.py +++ b/sources/pyside6/tests/signals/invalid_callback_test.py @@ -13,7 +13,7 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from init_paths import init_test_paths init_test_paths(False) -from PySide6.QtCore import QObject, SIGNAL +from PySide6.QtCore import QObject class InvalidCallback(unittest.TestCase): @@ -34,10 +34,8 @@ class InvalidCallback(unittest.TestCase): def testIntegerCb(self): # Test passing an int as callback to QObject.connect - self.assertRaises(TypeError, QObject.connect, self.obj, - SIGNAL('destroyed()'), 42) + self.assertRaises(TypeError, self.obj.destroyed.connect, 42) if __name__ == '__main__': unittest.main() - diff --git a/sources/pyside6/tests/signals/lambda_gui_test.py b/sources/pyside6/tests/signals/lambda_gui_test.py index 45a6d6acf..2123e7206 100644 --- a/sources/pyside6/tests/signals/lambda_gui_test.py +++ b/sources/pyside6/tests/signals/lambda_gui_test.py @@ -12,43 +12,39 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from init_paths import init_test_paths init_test_paths(False) -from PySide6.QtCore import QObject, SIGNAL - -try: - from PySide6.QtWidgets import QSpinBox, QPushButton - hasQtGui = True -except ImportError: - hasQtGui = False +from PySide6.QtWidgets import QSpinBox, QPushButton from helper.usesqapplication import UsesQApplication -if hasQtGui: - class Control: - def __init__(self): - self.arg = False - - class QtGuiSigLambda(UsesQApplication): - - def testButton(self): - # Connecting a lambda to a QPushButton.clicked() - obj = QPushButton('label') - ctr = Control() - func = lambda: setattr(ctr, 'arg', True) - obj.clicked.connect(func) - obj.click() - self.assertTrue(ctr.arg) - QObject.disconnect(obj, SIGNAL('clicked()'), func) - - def testSpinButton(self): - # Connecting a lambda to a QPushButton.clicked() - obj = QSpinBox() - ctr = Control() - arg = 444 - func = lambda x: setattr(ctr, 'arg', 444) - obj.valueChanged.connect(func) - obj.setValue(444) - self.assertEqual(ctr.arg, arg) - QObject.disconnect(obj, SIGNAL('valueChanged(int)'), func) + +class Control: + def __init__(self): + self.arg = False + + +class QtWidgetsSigLambda(UsesQApplication): + + def testButton(self): + # Connecting a lambda to a QPushButton.clicked() + obj = QPushButton('label') + ctr = Control() + func = lambda: setattr(ctr, 'arg', True) # noqa: E731 + obj.clicked.connect(func) + obj.click() + self.assertTrue(ctr.arg) + self.assertTrue(obj.clicked.disconnect(func)) + + def testSpinButton(self): + # Connecting a lambda to a QPushButton.clicked() + obj = QSpinBox() + ctr = Control() + arg = 444 + func = lambda x: setattr(ctr, 'arg', 444) # noqa: E731 + obj.valueChanged.connect(func) + obj.setValue(444) + self.assertEqual(ctr.arg, arg) + self.assertTrue(obj.valueChanged.disconnect(func)) + if __name__ == '__main__': unittest.main() diff --git a/sources/pyside6/tests/signals/lambda_test.py b/sources/pyside6/tests/signals/lambda_test.py index cc4e61ca1..23fcdf5fa 100644 --- a/sources/pyside6/tests/signals/lambda_test.py +++ b/sources/pyside6/tests/signals/lambda_test.py @@ -7,18 +7,34 @@ import os import sys import unittest +import weakref from pathlib import Path sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from init_paths import init_test_paths init_test_paths(False) -from PySide6.QtCore import QObject, SIGNAL, QProcess +from PySide6.QtCore import QCoreApplication, QObject, Signal, SIGNAL, QProcess -from helper.usesqcoreapplication import UsesQCoreApplication +from helper.usesqapplication import UsesQApplication -class Dummy(QObject): +class Sender(QObject): + void_signal = Signal() + int_signal = Signal(int) + + def __init__(self, parent=None): + super().__init__(parent) + self._delayed_int = 0 + + def emit_void(self): + self.void_signal.emit() + + def emit_int(self, v): + self.int_signal.emit(v) + + +class Receiver(QObject): def __init__(self, *args): super().__init__(*args) @@ -28,45 +44,79 @@ class BasicCase(unittest.TestCase): def testSimplePythonSignalNoArgs(self): # Connecting a lambda to a simple python signal without arguments - obj = Dummy() - QObject.connect(obj, SIGNAL('foo()'), - lambda: setattr(obj, 'called', True)) - obj.emit(SIGNAL('foo()')) - self.assertTrue(obj.called) + receiver = Receiver() + sender = Sender() + sender.void_signal.connect(lambda: setattr(receiver, 'called', True)) + sender.emit_void() + self.assertTrue(receiver.called) def testSimplePythonSignal(self): # Connecting a lambda to a simple python signal witharguments - obj = Dummy() + receiver = Receiver() + sender = Sender() + arg = 42 + sender.int_signal.connect(lambda x: setattr(receiver, 'arg', arg)) + sender.emit_int(arg) + self.assertEqual(receiver.arg, arg) + + def testSimplePythonSignalNoArgsString(self): + # Connecting a lambda to a simple python signal without arguments + receiver = Receiver() + sender = Sender() + QObject.connect(sender, SIGNAL('void_signal()'), + lambda: setattr(receiver, 'called', True)) + sender.emit_void() + self.assertTrue(receiver.called) + + def testSimplePythonSignalString(self): + # Connecting a lambda to a simple python signal witharguments + receiver = Receiver() + sender = Sender() arg = 42 - QObject.connect(obj, SIGNAL('foo(int)'), - lambda x: setattr(obj, 'arg', 42)) - obj.emit(SIGNAL('foo(int)'), arg) - self.assertEqual(obj.arg, arg) + QObject.connect(sender, SIGNAL('int_signal(int)'), + lambda x: setattr(receiver, 'arg', arg)) + sender.emit_int(arg) + self.assertEqual(receiver.arg, arg) -class QtSigLambda(UsesQCoreApplication): +class QtSigLambda(UsesQApplication): qapplication = True - def testNoArgs(self): - '''Connecting a lambda to a signal without arguments''' - proc = QProcess() - dummy = Dummy() - QObject.connect(proc, SIGNAL('started()'), - lambda: setattr(dummy, 'called', True)) - proc.start(sys.executable, ['-c', '""']) - proc.waitForFinished() - self.assertTrue(dummy.called) - def testWithArgs(self): - '''Connecting a lambda to a signal with arguments''' + '''Connecting a lambda to a signal with and without arguments''' proc = QProcess() - dummy = Dummy() - QObject.connect(proc, SIGNAL('finished(int)'), - lambda x: setattr(dummy, 'called', x)) + dummy = Receiver() + proc.started.connect(lambda: setattr(dummy, 'called', True)) + proc.finished.connect(lambda x: setattr(dummy, 'exit_code', x)) + proc.start(sys.executable, ['-c', '""']) - proc.waitForFinished() - self.assertEqual(dummy.called, proc.exitCode()) + self.assertTrue(proc.waitForStarted()) + self.assertTrue(proc.waitForFinished()) + + self.assertTrue(dummy.called) + self.assertEqual(dummy.exit_code, proc.exitCode()) + + def testRelease(self): + """PYSIDE-2646: Test whether main thread target slot lambda/methods + (and their captured objects) are released by the signal manager + after a while.""" + + def do_connect(sender): + receiver = Receiver() + sender.void_signal.connect(lambda: setattr(receiver, 'called', True)) + return receiver + + sender = Sender() + receiver = weakref.ref(do_connect(sender)) + sender.emit_void() + self.assertTrue(receiver().called) + del sender + for i in range(3): + if not receiver(): + break + QCoreApplication.processEvents() + self.assertFalse(receiver()) if __name__ == '__main__': diff --git a/sources/pyside6/tests/signals/leaking_signal_test.py b/sources/pyside6/tests/signals/leaking_signal_test.py index e7aff9628..666ae7a13 100644 --- a/sources/pyside6/tests/signals/leaking_signal_test.py +++ b/sources/pyside6/tests/signals/leaking_signal_test.py @@ -4,7 +4,6 @@ import os import sys import unittest -import weakref from pathlib import Path sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) @@ -21,7 +20,7 @@ class LeakingSignal(unittest.TestCase): class Emitter(QObject): my_signal = Signal(object) - emitter = Emitter() + emitter = Emitter() # noqa: F841 if __name__ == '__main__': diff --git a/sources/pyside6/tests/signals/multiple_connections_gui_test.py b/sources/pyside6/tests/signals/multiple_connections_gui_test.py index 097811750..295369b7d 100644 --- a/sources/pyside6/tests/signals/multiple_connections_gui_test.py +++ b/sources/pyside6/tests/signals/multiple_connections_gui_test.py @@ -1,9 +1,7 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -from functools import partial import os -import random import sys import unittest @@ -12,22 +10,16 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from init_paths import init_test_paths init_test_paths(False) -from PySide6.QtCore import QObject, SIGNAL - -try: - from PySide6.QtWidgets import QPushButton, QSpinBox - hasQtGui = True -except ImportError: - hasQtGui = False +from PySide6.QtWidgets import QPushButton, QSpinBox from helper.basicpyslotcase import BasicPySlotCase from helper.usesqapplication import UsesQApplication -class MultipleSignalConnections(unittest.TestCase): - '''Base class for multiple signal connection testing''' +class QtGuiMultipleSlots(UsesQApplication): + '''Multiple connections to QtGui signals''' - def run_many(self, sender, signal, emitter, receivers, args=None): + def run_many(self, signal, emitter, receivers, args=None): """Utility method to connect a list of receivers to a signal. sender - QObject that will emit the signal signal - string with the signal signature @@ -41,7 +33,7 @@ class MultipleSignalConnections(unittest.TestCase): for rec in receivers: rec.setUp() - QObject.connect(sender, SIGNAL(signal), rec.cb) + signal.connect(rec.cb) rec.args = tuple(args) emitter(*args) @@ -49,24 +41,20 @@ class MultipleSignalConnections(unittest.TestCase): for rec in receivers: self.assertTrue(rec.called) + def testButtonClick(self): + """Multiple connections to QPushButton.clicked()""" + sender = QPushButton('button') + receivers = [BasicPySlotCase() for x in range(30)] + self.run_many(sender.clicked, sender.click, receivers) -if hasQtGui: - class QtGuiMultipleSlots(UsesQApplication, MultipleSignalConnections): - '''Multiple connections to QtGui signals''' - - def testButtonClick(self): - """Multiple connections to QPushButton.clicked()""" - sender = QPushButton('button') - receivers = [BasicPySlotCase() for x in range(30)] - self.run_many(sender, 'clicked()', sender.click, receivers) + def testSpinBoxValueChanged(self): + """Multiple connections to QSpinBox.valueChanged(int)""" + sender = QSpinBox() + # FIXME if number of receivers if higher than 50, segfaults + receivers = [BasicPySlotCase() for x in range(10)] + self.run_many(sender.valueChanged, sender.setValue, + receivers, (1,)) - def testSpinBoxValueChanged(self): - """Multiple connections to QSpinBox.valueChanged(int)""" - sender = QSpinBox() - # FIXME if number of receivers if higher than 50, segfaults - receivers = [BasicPySlotCase() for x in range(10)] - self.run_many(sender, 'valueChanged(int)', sender.setValue, - receivers, (1,)) if __name__ == '__main__': unittest.main() diff --git a/sources/pyside6/tests/signals/multiple_connections_test.py b/sources/pyside6/tests/signals/multiple_connections_test.py index 783c9414b..233851797 100644 --- a/sources/pyside6/tests/signals/multiple_connections_test.py +++ b/sources/pyside6/tests/signals/multiple_connections_test.py @@ -11,16 +11,16 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from init_paths import init_test_paths init_test_paths(False) -from PySide6.QtCore import QObject, SIGNAL, QProcess +from PySide6.QtCore import QObject, Signal, QProcess from helper.basicpyslotcase import BasicPySlotCase -from helper.usesqcoreapplication import UsesQCoreApplication +from helper.usesqapplication import UsesQApplication class MultipleSignalConnections(unittest.TestCase): '''Base class for multiple signal connection testing''' - def run_many(self, sender, signal, emitter, receivers, args=None): + def run_many(self, signal, emitter, receivers, args=None): """Utility method to connect a list of receivers to a signal. sender - QObject that will emit the signal signal - string with the signal signature @@ -33,7 +33,7 @@ class MultipleSignalConnections(unittest.TestCase): args = tuple() for rec in receivers: rec.setUp() - self.assertTrue(QObject.connect(sender, SIGNAL(signal), rec.cb)) + self.assertTrue(signal.connect(rec.cb)) rec.args = tuple(args) emitter(*args) @@ -42,22 +42,23 @@ class MultipleSignalConnections(unittest.TestCase): self.assertTrue(rec.called) -class PythonMultipleSlots(UsesQCoreApplication, MultipleSignalConnections): +class PythonMultipleSlots(UsesQApplication, MultipleSignalConnections): '''Multiple connections to python signals''' def testPythonSignal(self): """Multiple connections to a python signal (short-circuit)""" - class Dummy(QObject): - pass + class Sender(QObject): - sender = Dummy() + foobar = Signal(int) + + sender = Sender() receivers = [BasicPySlotCase() for x in range(10)] - self.run_many(sender, 'foobar(int)', partial(sender.emit, - SIGNAL('foobar(int)')), receivers, (0, )) + self.run_many(sender.foobar, partial(sender.foobar.emit), + receivers, (0, )) -class QProcessMultipleSlots(UsesQCoreApplication, MultipleSignalConnections): +class QProcessMultipleSlots(UsesQApplication, MultipleSignalConnections): '''Multiple connections to QProcess signals''' def testQProcessStarted(self): @@ -67,9 +68,10 @@ class QProcessMultipleSlots(UsesQCoreApplication, MultipleSignalConnections): def start_proc(*args): sender.start(sys.executable, ['-c', '""']) - sender.waitForFinished() + self.assertTrue(sender.waitForStarted()) + self.assertTrue(sender.waitForFinished()) - self.run_many(sender, 'started()', start_proc, receivers) + self.run_many(sender.started, start_proc, receivers) def testQProcessFinished(self): '''Multiple connections to QProcess.finished(int)''' @@ -78,9 +80,10 @@ class QProcessMultipleSlots(UsesQCoreApplication, MultipleSignalConnections): def start_proc(*args): sender.start(sys.executable, ['-c', '""']) - sender.waitForFinished() + self.assertTrue(sender.waitForStarted()) + self.assertTrue(sender.waitForFinished()) - self.run_many(sender, 'finished(int)', start_proc, receivers, (0,)) + self.run_many(sender.finished, start_proc, receivers, (0, QProcess.ExitStatus.NormalExit)) if __name__ == '__main__': diff --git a/sources/pyside6/tests/signals/pysignal_test.py b/sources/pyside6/tests/signals/pysignal_test.py index e3d6a55da..d6f44edf8 100644 --- a/sources/pyside6/tests/signals/pysignal_test.py +++ b/sources/pyside6/tests/signals/pysignal_test.py @@ -11,31 +11,31 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from init_paths import init_test_paths init_test_paths(False) -from PySide6.QtCore import QObject, SIGNAL, SLOT, Qt - -try: - from PySide6.QtWidgets import QSpinBox, QApplication, QWidget - hasQtGui = True -except ImportError: - hasQtGui = False +from PySide6.QtCore import QObject, Signal, Qt +from PySide6.QtWidgets import QSpinBox, QApplication, QWidget # noqa: F401 from helper.usesqapplication import UsesQApplication -class Dummy(QObject): - """Dummy class used in this test.""" +TEST_LIST = ["item1", "item2", "item3"] + + +class Sender(QObject): + """Sender class used in this test.""" + + foo = Signal() + foo_int = Signal(int) + dummy = Signal(str) + dummy2 = Signal(str, list) + def __init__(self, parent=None): - QObject.__init__(self, parent) + super().__init__(parent) def callDummy(self): - self.emit(SIGNAL("dummy(PyObject)"), "PyObject") + self.dummy.emit("PyObject") def callDummy2(self): - lst = [] - lst.append("item1") - lst.append("item2") - lst.append("item3") - self.emit(SIGNAL("dummy2(PyObject, PyObject)"), "PyObject0", lst) + self.dummy2.emit("PyObject0", TEST_LIST) class PyObjectType(UsesQApplication): @@ -46,35 +46,33 @@ class PyObjectType(UsesQApplication): def mySlot2(self, arg0, arg1): self.assertEqual(arg0, "PyObject0") - self.assertEqual(arg1[0], "item1") - self.assertEqual(arg1[1], "item2") - self.assertEqual(arg1[2], "item3") + self.assertEqual(arg1, TEST_LIST) self.callCount += 1 if self.running: self.app.quit() def setUp(self): - super(PyObjectType, self).setUp() + super().setUp() self.callCount = 0 self.running = False def testWithOneArg(self): - o = Dummy() - o.connect(SIGNAL("dummy(PyObject)"), self.mySlot) + o = Sender() + o.dummy.connect(self.mySlot) o.callDummy() self.assertEqual(self.callCount, 1) def testWithTwoArg(self): - o = Dummy() - o.connect(SIGNAL("dummy2(PyObject,PyObject)"), self.mySlot2) + o = Sender() + o.dummy2.connect(self.mySlot2) o.callDummy2() self.assertEqual(self.callCount, 1) def testAsyncSignal(self): self.called = False self.running = True - o = Dummy() - o.connect(SIGNAL("dummy2(PyObject,PyObject)"), self.mySlot2, Qt.QueuedConnection) + o = Sender() + o.dummy2.connect(self.mySlot2, Qt.QueuedConnection) o.callDummy2() self.app.exec() self.assertEqual(self.callCount, 1) @@ -82,8 +80,8 @@ class PyObjectType(UsesQApplication): def testTwice(self): self.called = False self.running = True - o = Dummy() - o.connect(SIGNAL("dummy2(PyObject,PyObject)"), self.mySlot2, Qt.QueuedConnection) + o = Sender() + o.dummy2.connect(self.mySlot2, Qt.QueuedConnection) o.callDummy2() o.callDummy2() self.app.exec() @@ -97,7 +95,7 @@ class PythonSigSlot(unittest.TestCase): def tearDown(self): try: del self.args - except: + except: # noqa: E722 pass # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion gc.collect() @@ -108,98 +106,98 @@ class PythonSigSlot(unittest.TestCase): def testNoArgs(self): """Python signal and slots without arguments""" - obj1 = Dummy() + obj1 = Sender() - QObject.connect(obj1, SIGNAL('foo()'), self.callback) + obj1.foo.connect(self.callback) self.args = tuple() - obj1.emit(SIGNAL('foo()'), *self.args) + obj1.foo.emit(*self.args) self.assertTrue(self.called) def testWithArgs(self): """Python signal and slots with integer arguments""" - obj1 = Dummy() + obj1 = Sender() - QObject.connect(obj1, SIGNAL('foo(int)'), self.callback) + obj1.foo_int.connect(self.callback) self.args = (42,) - obj1.emit(SIGNAL('foo(int)'), *self.args) + obj1.foo_int.emit(*self.args) self.assertTrue(self.called) def testDisconnect(self): - obj1 = Dummy() + obj1 = Sender() - QObject.connect(obj1, SIGNAL('foo(int)'), self.callback) - QObject.disconnect(obj1, SIGNAL('foo(int)'), self.callback) + obj1.foo_int.connect(self.callback) + self.assertTrue(obj1.foo_int.disconnect(self.callback)) self.args = (42, ) - obj1.emit(SIGNAL('foo(int)'), *self.args) + obj1.foo_int.emit(*self.args) self.assertTrue(not self.called) -if hasQtGui: - class SpinBoxPySignal(UsesQApplication): - """Tests the connection of python signals to QSpinBox qt slots.""" +class SpinBoxPySignal(UsesQApplication): + """Tests the connection of python signals to QSpinBox qt slots.""" - def setUp(self): - super(SpinBoxPySignal, self).setUp() - self.obj = Dummy() - self.spin = QSpinBox() - self.spin.setValue(0) + def setUp(self): + super().setUp() + self.obj = Sender() + self.spin = QSpinBox() + self.spin.setValue(0) - def tearDown(self): - super(SpinBoxPySignal, self).tearDown() - del self.obj - del self.spin - # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion - gc.collect() + def tearDown(self): + super().tearDown() + del self.obj + del self.spin + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + + def testValueChanged(self): + """Emission of a python signal to QSpinBox setValue(int)""" - def testValueChanged(self): - """Emission of a python signal to QSpinBox setValue(int)""" - QObject.connect(self.obj, SIGNAL('dummy(int)'), self.spin, SLOT('setValue(int)')) - self.assertEqual(self.spin.value(), 0) + self.obj.foo_int.connect(self.spin.setValue) + self.assertEqual(self.spin.value(), 0) - self.obj.emit(SIGNAL('dummy(int)'), 4) - self.assertEqual(self.spin.value(), 4) + self.obj.foo_int.emit(4) + self.assertEqual(self.spin.value(), 4) - def testValueChangedMultiple(self): - """Multiple emissions of a python signal to QSpinBox setValue(int)""" - QObject.connect(self.obj, SIGNAL('dummy(int)'), self.spin, SLOT('setValue(int)')) - self.assertEqual(self.spin.value(), 0) + def testValueChangedMultiple(self): + """Multiple emissions of a python signal to QSpinBox setValue(int)""" + self.obj.foo_int.connect(self.spin.setValue) + self.assertEqual(self.spin.value(), 0) - self.obj.emit(SIGNAL('dummy(int)'), 4) - self.assertEqual(self.spin.value(), 4) + self.obj.foo_int.emit(4) + self.assertEqual(self.spin.value(), 4) - self.obj.emit(SIGNAL('dummy(int)'), 77) - self.assertEqual(self.spin.value(), 77) + self.obj.foo_int.emit(77) + self.assertEqual(self.spin.value(), 77) -if hasQtGui: - class WidgetPySignal(UsesQApplication): - """Tests the connection of python signals to QWidget qt slots.""" +class WidgetPySignal(UsesQApplication): + """Tests the connection of python signals to QWidget qt slots.""" + + def setUp(self): + super(WidgetPySignal, self).setUp() + self.obj = Sender() + self.widget = QWidget() - def setUp(self): - super(WidgetPySignal, self).setUp() - self.obj = Dummy() - self.widget = QWidget() + def tearDown(self): + super(WidgetPySignal, self).tearDown() + del self.obj + del self.widget + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() - def tearDown(self): - super(WidgetPySignal, self).tearDown() - del self.obj - del self.widget - # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion - gc.collect() + def testShow(self): + """Emission of a python signal to QWidget slot show()""" + self.widget.hide() - def testShow(self): - """Emission of a python signal to QWidget slot show()""" - self.widget.hide() + self.obj.foo.connect(self.widget.show) + self.assertTrue(not self.widget.isVisible()) - QObject.connect(self.obj, SIGNAL('dummy()'), self.widget, SLOT('show()')) - self.assertTrue(not self.widget.isVisible()) + self.obj.foo.emit() + self.assertTrue(self.widget.isVisible()) - self.obj.emit(SIGNAL('dummy()')) - self.assertTrue(self.widget.isVisible()) if __name__ == '__main__': unittest.main() diff --git a/sources/pyside6/tests/signals/qobject_callable_connect_test.py b/sources/pyside6/tests/signals/qobject_callable_connect_test.py new file mode 100644 index 000000000..a7a26d6f5 --- /dev/null +++ b/sources/pyside6/tests/signals/qobject_callable_connect_test.py @@ -0,0 +1,45 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from init_paths import init_test_paths +init_test_paths(False) + +from PySide6.QtCore import QObject, Signal + + +class Emitter(QObject): + sig = Signal(int) + + +class CallableObject(QObject): + called = False + x = 0 + + def __call__(self, x: int): + self.called = True + self.x = x + + +class QObjectCallableConnectTest(unittest.TestCase): + '''Test case for QObject.connect() when the callable is also a QObject.''' + + def testCallableConnect(self): + emitter = Emitter() + obj = CallableObject() + x = 1 + + emitter.sig.connect(obj) + emitter.sig.emit(x) + + self.assertTrue(obj.called) + self.assertEqual(obj.x, x) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/pyside6/tests/signals/qobject_destroyed_test.py b/sources/pyside6/tests/signals/qobject_destroyed_test.py index 08807e78e..a21762b41 100644 --- a/sources/pyside6/tests/signals/qobject_destroyed_test.py +++ b/sources/pyside6/tests/signals/qobject_destroyed_test.py @@ -11,7 +11,7 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from init_paths import init_test_paths init_test_paths(False) -from PySide6.QtCore import QObject, SIGNAL +from PySide6.QtCore import QObject class QObjectDestroyed(unittest.TestCase): diff --git a/sources/pyside6/tests/signals/qobject_sender_test.py b/sources/pyside6/tests/signals/qobject_sender_test.py index 1f01b57e5..9c1121eb8 100644 --- a/sources/pyside6/tests/signals/qobject_sender_test.py +++ b/sources/pyside6/tests/signals/qobject_sender_test.py @@ -13,8 +13,8 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from init_paths import init_test_paths init_test_paths(False) -from PySide6.QtCore import QCoreApplication, QObject, QTimer, SIGNAL -from helper.usesqcoreapplication import UsesQCoreApplication +from PySide6.QtCore import QCoreApplication, QObject, QTimer, Signal +from helper.usesqapplication import UsesQApplication class ExtQTimer(QTimer): @@ -22,6 +22,10 @@ class ExtQTimer(QTimer): super().__init__() +class Sender(QObject): + foo = Signal() + + class Receiver(QObject): def __init__(self): super().__init__() @@ -37,10 +41,10 @@ class ObjectSenderTest(unittest.TestCase): '''Test case for QObject.sender() method.''' def testSenderPythonSignal(self): - sender = QObject() + sender = Sender() recv = Receiver() - QObject.connect(sender, SIGNAL('foo()'), recv.callback) - sender.emit(SIGNAL('foo()')) + sender.foo.connect(recv.callback) + sender.foo.emit() self.assertEqual(sender, recv.the_sender) @@ -48,14 +52,14 @@ class ObjectSenderCheckOnReceiverTest(unittest.TestCase): '''Test case for QObject.sender() method, this one tests the equality on the Receiver object.''' def testSenderPythonSignal(self): - sender = QObject() + sender = Sender() recv = Receiver() - QObject.connect(sender, SIGNAL('foo()'), recv.callback) - sender.emit(SIGNAL('foo()')) + sender.foo.connect(recv.callback) + sender.foo.emit() self.assertEqual(sender, recv.the_sender) -class ObjectSenderWithQAppTest(UsesQCoreApplication): +class ObjectSenderWithQAppTest(UsesQApplication): '''Test case for QObject.sender() method with QApplication.''' def testSenderCppSignal(self): @@ -73,6 +77,12 @@ class ObjectSenderWithQAppTest(UsesQCoreApplication): self.app.exec() self.assertTrue(isinstance(recv.the_sender, QObject)) + def testSenderCppSignalSingleShotTimerWithContext(self): + recv = Receiver() + QTimer.singleShot(10, recv, recv.callback) + self.app.exec() + self.assertTrue(isinstance(recv.the_sender, QObject)) + def testSenderCppSignalWithPythonExtendedClass(self): sender = ExtQTimer() recv = Receiver() @@ -82,7 +92,7 @@ class ObjectSenderWithQAppTest(UsesQCoreApplication): self.assertEqual(sender, recv.the_sender) -class ObjectSenderWithQAppCheckOnReceiverTest(UsesQCoreApplication): +class ObjectSenderWithQAppCheckOnReceiverTest(UsesQApplication): '''Test case for QObject.sender() method with QApplication.''' def testSenderCppSignal(self): @@ -105,4 +115,3 @@ class ObjectSenderWithQAppCheckOnReceiverTest(UsesQCoreApplication): if __name__ == '__main__': unittest.main() - diff --git a/sources/pyside6/tests/signals/ref01_test.py b/sources/pyside6/tests/signals/ref01_test.py index a830b55dd..1a62b2218 100644 --- a/sources/pyside6/tests/signals/ref01_test.py +++ b/sources/pyside6/tests/signals/ref01_test.py @@ -37,5 +37,3 @@ class BoundAndUnboundSignalsTest(unittest.TestCase): if __name__ == '__main__': unittest.main() - - diff --git a/sources/pyside6/tests/signals/ref02_test.py b/sources/pyside6/tests/signals/ref02_test.py index db456b35b..54b6f4a52 100644 --- a/sources/pyside6/tests/signals/ref02_test.py +++ b/sources/pyside6/tests/signals/ref02_test.py @@ -12,14 +12,14 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from init_paths import init_test_paths init_test_paths(False) -from PySide6.QtCore import QCoreApplication, QTimeLine -from helper.usesqcoreapplication import UsesQCoreApplication +from PySide6.QtCore import QTimeLine +from helper.usesqapplication import UsesQApplication -class NativeSignalsTest(UsesQCoreApplication): +class NativeSignalsTest(UsesQApplication): def setUp(self): - UsesQCoreApplication.setUp(self) + UsesQApplication.setUp(self) self.called = False self.timeline = QTimeLine(100) @@ -28,7 +28,7 @@ class NativeSignalsTest(UsesQCoreApplication): del self.timeline # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion gc.collect() - UsesQCoreApplication.tearDown(self) + UsesQApplication.tearDown(self) def testSignalWithIntArgument(self): @@ -58,4 +58,3 @@ class NativeSignalsTest(UsesQCoreApplication): if __name__ == '__main__': unittest.main() - diff --git a/sources/pyside6/tests/signals/ref03_test.py b/sources/pyside6/tests/signals/ref03_test.py index 2df2d7cef..c43c2e549 100644 --- a/sources/pyside6/tests/signals/ref03_test.py +++ b/sources/pyside6/tests/signals/ref03_test.py @@ -40,4 +40,3 @@ class DisconnectSignalsTest(unittest.TestCase): if __name__ == '__main__': unittest.main() - diff --git a/sources/pyside6/tests/signals/ref04_test.py b/sources/pyside6/tests/signals/ref04_test.py index b478aff2a..fce801456 100644 --- a/sources/pyside6/tests/signals/ref04_test.py +++ b/sources/pyside6/tests/signals/ref04_test.py @@ -53,17 +53,6 @@ class UserSignalTest(unittest.TestCase): self.emitter.mySignal.emit() self.assertEqual(self.counter, 2) -# def testConnectWithConfigureMethod(self): -# -# def slot(): -# self.counter += 1 -# -# self.emitter.pyqtConfigure(mySignal=slot) -# self.assertEqual(self.counter, 0) -# self.emitter.mySignal.emit() -# self.assertEqual(self.counter, 1) - if __name__ == '__main__': unittest.main() - diff --git a/sources/pyside6/tests/signals/ref05_test.py b/sources/pyside6/tests/signals/ref05_test.py index 13ab1ff65..fb9debf39 100644 --- a/sources/pyside6/tests/signals/ref05_test.py +++ b/sources/pyside6/tests/signals/ref05_test.py @@ -12,8 +12,8 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from init_paths import init_test_paths init_test_paths(False) -from PySide6.QtCore import QObject, QCoreApplication, QTimeLine, Slot -from helper.usesqcoreapplication import UsesQCoreApplication +from PySide6.QtCore import QObject, QTimeLine, Slot +from helper.usesqapplication import UsesQApplication class ExtQObject(QObject): @@ -27,10 +27,10 @@ class ExtQObject(QObject): self.counter += 1 -class UserSlotTest(UsesQCoreApplication): +class UserSlotTest(UsesQApplication): def setUp(self): - UsesQCoreApplication.setUp(self) + UsesQApplication.setUp(self) self.receiver = ExtQObject() self.timeline = QTimeLine(100) @@ -39,7 +39,7 @@ class UserSlotTest(UsesQCoreApplication): del self.receiver # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion gc.collect() - UsesQCoreApplication.tearDown(self) + UsesQApplication.tearDown(self) def testUserSlot(self): self.timeline.setUpdateInterval(10) @@ -56,4 +56,3 @@ class UserSlotTest(UsesQCoreApplication): if __name__ == '__main__': unittest.main() - diff --git a/sources/pyside6/tests/signals/ref06_test.py b/sources/pyside6/tests/signals/ref06_test.py index 56d54c1af..a827131db 100644 --- a/sources/pyside6/tests/signals/ref06_test.py +++ b/sources/pyside6/tests/signals/ref06_test.py @@ -12,8 +12,8 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from init_paths import init_test_paths init_test_paths(False) -from PySide6.QtCore import QObject, QCoreApplication, QTimeLine, Signal, Slot -from helper.usesqcoreapplication import UsesQCoreApplication +from PySide6.QtCore import QObject, QTimeLine, Signal, Slot +from helper.usesqapplication import UsesQApplication class ExtQObject(QObject): @@ -28,10 +28,10 @@ class ExtQObject(QObject): self.counter += 1 -class SignaltoSignalTest(UsesQCoreApplication): +class SignaltoSignalTest(UsesQApplication): def setUp(self): - UsesQCoreApplication.setUp(self) + UsesQApplication.setUp(self) self.receiver = ExtQObject() self.timeline = QTimeLine(100) @@ -40,7 +40,7 @@ class SignaltoSignalTest(UsesQCoreApplication): del self.receiver # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion gc.collect() - UsesQCoreApplication.tearDown(self) + UsesQApplication.tearDown(self) def testSignaltoSignal(self): self.timeline.setUpdateInterval(10) @@ -59,4 +59,3 @@ class SignaltoSignalTest(UsesQCoreApplication): if __name__ == '__main__': unittest.main() - diff --git a/sources/pyside6/tests/signals/segfault_proxyparent_test.py b/sources/pyside6/tests/signals/segfault_proxyparent_test.py index c85ab10c0..cb0df0978 100644 --- a/sources/pyside6/tests/signals/segfault_proxyparent_test.py +++ b/sources/pyside6/tests/signals/segfault_proxyparent_test.py @@ -11,7 +11,7 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from init_paths import init_test_paths init_test_paths(False) -from PySide6.QtCore import QObject, SIGNAL +from PySide6.QtCore import QObject, Signal # Description of the problem # After creating an PyObject that inherits from QObject, connecting it, @@ -20,16 +20,19 @@ from PySide6.QtCore import QObject, SIGNAL # Somehow the underlying QObject also points to the same position. -# In PyQt4, the connection works fine with the same memory behavior, -# so it looks like specific to SIP. +class Sender(QObject): + + bar = Signal(int) -class Dummy(QObject): def __init__(self, parent=None): QObject.__init__(self, parent) class Joe(QObject): + + bar = Signal(int) + def __init__(self, parent=None): QObject.__init__(self, parent) @@ -44,7 +47,7 @@ class SegfaultCase(unittest.TestCase): def tearDown(self): try: del self.args - except: + except: # noqa: E722 pass # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion gc.collect() @@ -55,22 +58,21 @@ class SegfaultCase(unittest.TestCase): def testSegfault(self): """Regression: Segfault for qobjects in the same memory position.""" - obj = Dummy() - QObject.connect(obj, SIGNAL('bar(int)'), self.callback) + obj = Sender() + obj.bar.connect(self.callback) self.args = (33,) - obj.emit(SIGNAL('bar(int)'), self.args[0]) + obj.bar.emit(self.args[0]) self.assertTrue(self.called) del obj # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion gc.collect() obj = Joe() - QObject.connect(obj, SIGNAL('bar(int)'), self.callback) + obj.bar.connect(self.callback) self.args = (33,) - obj.emit(SIGNAL('bar(int)'), self.args[0]) + obj.bar.emit(self.args[0]) self.assertTrue(self.called) if __name__ == '__main__': unittest.main() - diff --git a/sources/pyside6/tests/signals/self_connect_test.py b/sources/pyside6/tests/signals/self_connect_test.py index d6c03db18..08ca725f8 100644 --- a/sources/pyside6/tests/signals/self_connect_test.py +++ b/sources/pyside6/tests/signals/self_connect_test.py @@ -13,17 +13,27 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from init_paths import init_test_paths init_test_paths(False) -from PySide6.QtCore import QObject, SIGNAL, SLOT +from PySide6.QtCore import QObject, Slot from PySide6.QtWidgets import QPushButton, QWidget from helper.usesqapplication import UsesQApplication +class Receiver(QObject): + def __init__(self, p=None): + super().__init__(p) + self.triggered = False + + @Slot(bool, int) + def default_parameter_slot(self, bool_value, int_value=0): + self.triggered = True + + class SelfConnect(UsesQApplication): def testButtonClickClose(self): button = QPushButton() - button.connect(button, SIGNAL('clicked()'), SLOT('close()')) + button.clicked.connect(button.close) button.show() self.assertTrue(button.isVisible()) @@ -33,13 +43,22 @@ class SelfConnect(UsesQApplication): def testWindowButtonClickClose(self): button = QPushButton() window = QWidget() - window.connect(button, SIGNAL('clicked()'), SLOT('close()')) + button.clicked.connect(window.close) window.show() self.assertTrue(window.isVisible()) button.click() self.assertTrue(not window.isVisible()) + def testDefaultParameters(self): + button = QPushButton() + receiver = Receiver(button) + button.clicked.connect(receiver.default_parameter_slot) + button.clicked.connect(button.close) + button.show() + button.click() + self.assertTrue(receiver.triggered) + if __name__ == '__main__': unittest.main() diff --git a/sources/pyside6/tests/signals/short_circuit_test.py b/sources/pyside6/tests/signals/short_circuit_test.py index 434518336..1ad4bc24c 100644 --- a/sources/pyside6/tests/signals/short_circuit_test.py +++ b/sources/pyside6/tests/signals/short_circuit_test.py @@ -11,11 +11,17 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from init_paths import init_test_paths init_test_paths(False) -from PySide6.QtCore import QObject, SIGNAL, SLOT +from PySide6.QtCore import QObject, Signal -class Dummy(QObject): - """Dummy class used in this test.""" +class Sender(QObject): + """Sender class used in this test.""" + + foo = Signal() + foo_int = Signal(int) + foo_int_int_string = Signal(int, int, str) + foo_int_qobject = Signal(int, QObject) + def __init__(self, parent=None): QObject.__init__(self, parent) @@ -27,7 +33,7 @@ class ShortCircuitSignals(unittest.TestCase): def tearDown(self): try: del self.args - except: + except: # noqa: E722 pass # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion gc.collect() @@ -38,40 +44,40 @@ class ShortCircuitSignals(unittest.TestCase): def testNoArgs(self): """Short circuit signal without arguments""" - obj1 = Dummy() - QObject.connect(obj1, SIGNAL('foo()'), self.callback) + obj1 = Sender() + obj1.foo.connect(self.callback) self.args = tuple() - obj1.emit(SIGNAL('foo()'), *self.args) + obj1.foo.emit(*self.args) self.assertTrue(self.called) def testWithArgs(self): """Short circuit signal with integer arguments""" - obj1 = Dummy() + obj1 = Sender() - QObject.connect(obj1, SIGNAL('foo(int)'), self.callback) + obj1.foo_int.connect(self.callback) self.args = (42,) - obj1.emit(SIGNAL('foo(int)'), *self.args) + obj1.foo_int.emit(*self.args) self.assertTrue(self.called) def testMultipleArgs(self): """Short circuit signal with multiple arguments""" - obj1 = Dummy() + obj1 = Sender() - QObject.connect(obj1, SIGNAL('foo(int,int,QString)'), self.callback) + obj1.foo_int_int_string.connect(self.callback) self.args = (42, 33, 'char') - obj1.emit(SIGNAL('foo(int,int,QString)'), *self.args) + obj1.foo_int_int_string.emit(*self.args) self.assertTrue(self.called) def testComplexArgs(self): """Short circuit signal with complex arguments""" - obj1 = Dummy() + obj1 = Sender() - QObject.connect(obj1, SIGNAL('foo(int,QObject*)'), self.callback) + obj1.foo_int_qobject.connect(self.callback) self.args = (42, obj1) - obj1.emit(SIGNAL('foo(int,QObject*)'), *self.args) + obj1.foo_int_qobject.emit(*self.args) self.assertTrue(self.called) diff --git a/sources/pyside6/tests/signals/signal2signal_connect_test.py b/sources/pyside6/tests/signals/signal2signal_connect_test.py index c755a9dca..31129f7a1 100644 --- a/sources/pyside6/tests/signals/signal2signal_connect_test.py +++ b/sources/pyside6/tests/signals/signal2signal_connect_test.py @@ -13,7 +13,20 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from init_paths import init_test_paths init_test_paths(False) -from PySide6.QtCore import QObject, SIGNAL +from PySide6.QtCore import QObject, Signal + + +class Sender(QObject): + + mysignal_int = Signal(int) + mysignal_int_int = Signal(int, int) + mysignal_string = Signal(str) + + +class Forwarder(Sender): + + forward = Signal() + forward_qobject = Signal(QObject) def cute_slot(): @@ -25,8 +38,8 @@ class TestSignal2SignalConnect(unittest.TestCase): def setUp(self): # Set up the basic resources needed - self.sender = QObject() - self.forwarder = QObject() + self.sender = Sender() + self.forwarder = Forwarder() self.args = None self.called = False @@ -34,11 +47,11 @@ class TestSignal2SignalConnect(unittest.TestCase): # Delete used resources try: del self.sender - except: + except: # noqa: E722 pass try: del self.forwarder - except: + except: # noqa: E722 pass del self.args # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion @@ -63,47 +76,37 @@ class TestSignal2SignalConnect(unittest.TestCase): raise TypeError("Invalid arguments") def testSignalWithoutArguments(self): - QObject.connect(self.sender, SIGNAL("destroyed()"), - self.forwarder, SIGNAL("forward()")) - QObject.connect(self.forwarder, SIGNAL("forward()"), - self.callback_noargs) + self.sender.destroyed.connect(self.forwarder.forward) + self.forwarder.forward.connect(self.callback_noargs) del self.sender # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion gc.collect() self.assertTrue(self.called) def testSignalWithOnePrimitiveTypeArgument(self): - QObject.connect(self.sender, SIGNAL("mysignal(int)"), - self.forwarder, SIGNAL("mysignal(int)")) - QObject.connect(self.forwarder, SIGNAL("mysignal(int)"), - self.callback_args) + self.sender.mysignal_int.connect(self.forwarder.mysignal_int) + self.forwarder.mysignal_int.connect(self.callback_args) self.args = (19,) - self.sender.emit(SIGNAL('mysignal(int)'), *self.args) + self.sender.mysignal_int.emit(*self.args) self.assertTrue(self.called) def testSignalWithMultiplePrimitiveTypeArguments(self): - QObject.connect(self.sender, SIGNAL("mysignal(int,int)"), - self.forwarder, SIGNAL("mysignal(int,int)")) - QObject.connect(self.forwarder, SIGNAL("mysignal(int,int)"), - self.callback_args) + self.sender.mysignal_int_int.connect(self.forwarder.mysignal_int_int) + self.forwarder.mysignal_int_int.connect(self.callback_args) self.args = (23, 29) - self.sender.emit(SIGNAL('mysignal(int,int)'), *self.args) + self.sender.mysignal_int_int.emit(*self.args) self.assertTrue(self.called) def testSignalWithOneStringArgument(self): - QObject.connect(self.sender, SIGNAL("mysignal(QString)"), - self.forwarder, SIGNAL("mysignal(QString)")) - QObject.connect(self.forwarder, SIGNAL("mysignal(QString)"), - self.callback_args) + self.sender.mysignal_string.connect(self.forwarder.mysignal_string) + self.forwarder.mysignal_string.connect(self.callback_args) self.args = ('myargument',) - self.sender.emit(SIGNAL('mysignal(QString)'), *self.args) + self.sender.mysignal_string.emit(*self.args) self.assertTrue(self.called) def testSignalWithOneQObjectArgument(self): - QObject.connect(self.sender, SIGNAL('destroyed(QObject*)'), - self.forwarder, SIGNAL('forward(QObject*)')) - QObject.connect(self.forwarder, SIGNAL('forward(QObject*)'), - self.callback_qobject) + self.sender.destroyed.connect(self.forwarder.forward_qobject) + self.forwarder.forward_qobject.connect(self.callback_qobject) obj_name = 'sender' self.sender.setObjectName(obj_name) @@ -116,5 +119,3 @@ class TestSignal2SignalConnect(unittest.TestCase): if __name__ == '__main__': unittest.main() - - diff --git a/sources/pyside6/tests/signals/signal_across_threads.py b/sources/pyside6/tests/signals/signal_across_threads.py index ad4b806ed..91b1ca986 100644 --- a/sources/pyside6/tests/signals/signal_across_threads.py +++ b/sources/pyside6/tests/signals/signal_across_threads.py @@ -14,7 +14,7 @@ from init_paths import init_test_paths init_test_paths(False) from PySide6.QtCore import QObject, QThread, QTimer, Slot -from helper.usesqcoreapplication import UsesQCoreApplication +from helper.usesqapplication import UsesQApplication class ReceiverBase(QObject): @@ -39,9 +39,9 @@ class TestThread(QThread): pass -class SignalAcrossThreads(UsesQCoreApplication): +class SignalAcrossThreads(UsesQApplication): def setUp(self): - UsesQCoreApplication.setUp(self) + UsesQApplication.setUp(self) self._timer_tick = 0 self._timer = QTimer() self._timer.setInterval(20) @@ -49,7 +49,7 @@ class SignalAcrossThreads(UsesQCoreApplication): self._worker_thread = TestThread() def tearDown(self): - UsesQCoreApplication.tearDown(self) + UsesQApplication.tearDown(self) @Slot() def _control_test(self): diff --git a/sources/pyside6/tests/signals/signal_autoconnect_test.py b/sources/pyside6/tests/signals/signal_autoconnect_test.py index 820a77444..51d1cea3a 100644 --- a/sources/pyside6/tests/signals/signal_autoconnect_test.py +++ b/sources/pyside6/tests/signals/signal_autoconnect_test.py @@ -27,7 +27,7 @@ class MyObject(QWidget): class AutoConnectionTest(unittest.TestCase): def testConnection(self): - app = QApplication([]) + app = QApplication([]) # noqa: F841 win = MyObject() btn = QPushButton("click", win) diff --git a/sources/pyside6/tests/signals/signal_connectiontype_support_test.py b/sources/pyside6/tests/signals/signal_connectiontype_support_test.py index 95ce1fa4f..0a69c1e02 100644 --- a/sources/pyside6/tests/signals/signal_connectiontype_support_test.py +++ b/sources/pyside6/tests/signals/signal_connectiontype_support_test.py @@ -10,13 +10,16 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from init_paths import init_test_paths init_test_paths(False) -from PySide6.QtCore import QObject, SIGNAL, Qt +from PySide6.QtCore import QObject, Signal, Qt -class Dummy(QObject): +class Sender(QObject): """Dummy class used in this test.""" + + foo = Signal() + def __init__(self, parent=None): - QObject.__init__(self, parent) + super().__init__(parent) class TestConnectionTypeSupport(unittest.TestCase): @@ -26,11 +29,11 @@ class TestConnectionTypeSupport(unittest.TestCase): def testNoArgs(self): """Connect signal using a Qt.ConnectionType as argument""" - obj1 = Dummy() + obj1 = Sender() - QObject.connect(obj1, SIGNAL('foo()'), self.callback, Qt.DirectConnection) + obj1.foo.connect(self.callback, Qt.DirectConnection) self.args = tuple() - obj1.emit(SIGNAL('foo()'), *self.args) + obj1.foo.emit(*self.args) self.assertTrue(self.called) diff --git a/sources/pyside6/tests/signals/signal_emission_gui_test.py b/sources/pyside6/tests/signals/signal_emission_gui_test.py index aae96f236..5a49b9d12 100644 --- a/sources/pyside6/tests/signals/signal_emission_gui_test.py +++ b/sources/pyside6/tests/signals/signal_emission_gui_test.py @@ -14,112 +14,104 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from init_paths import init_test_paths init_test_paths(False) -from PySide6.QtCore import QObject, SIGNAL, SLOT - -try: - from PySide6.QtWidgets import QSpinBox, QPushButton - hasQtGui = True -except ImportError: - hasQtGui = False +from PySide6.QtWidgets import QSpinBox, QPushButton from helper.basicpyslotcase import BasicPySlotCase from helper.usesqapplication import UsesQApplication -if hasQtGui: - class ButtonPySlot(UsesQApplication, BasicPySlotCase): - """Tests the connection of python slots to QPushButton signals""" - - def testButtonClicked(self): - """Connection of a python slot to QPushButton.clicked()""" - button = QPushButton('Mylabel') - button.clicked.connect(self.cb) - self.args = tuple() - button.emit(SIGNAL('clicked(bool)'), False) - self.assertTrue(self.called) - - def testButtonClick(self): - """Indirect qt signal emission using the QPushButton.click() method """ - button = QPushButton('label') - button.clicked.connect(self.cb) - self.args = tuple() - button.click() - self.assertTrue(self.called) - - -if hasQtGui: - class SpinBoxPySlot(UsesQApplication, BasicPySlotCase): - """Tests the connection of python slots to QSpinBox signals""" - - def setUp(self): - super(SpinBoxPySlot, self).setUp() - self.spin = QSpinBox() - - def tearDown(self): - del self.spin - # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion - gc.collect() - super(SpinBoxPySlot, self).tearDown() - - def testSpinBoxValueChanged(self): - """Connection of a python slot to QSpinBox.valueChanged(int)""" - self.spin.valueChanged.connect(self.cb) - self.args = [3] - self.spin.emit(SIGNAL('valueChanged(int)'), *self.args) - self.assertTrue(self.called) - - def testSpinBoxValueChangedImplicit(self): - """Indirect qt signal emission using QSpinBox.setValue(int)""" - self.spin.valueChanged.connect(self.cb) - self.args = [42] - self.spin.setValue(self.args[0]) - self.assertTrue(self.called) - - def atestSpinBoxValueChangedFewArgs(self): - """Emission of signals with fewer arguments than needed""" - # XXX: PyQt4 crashes on the assertRaises - self.spin.valueChanged.connect(self.cb) - self.args = (554,) - self.assertRaises(TypeError, self.spin.emit, SIGNAL('valueChanged(int)')) - -if hasQtGui: - class QSpinBoxQtSlots(UsesQApplication): - """Tests the connection to QSpinBox qt slots""" - - qapplication = True - - def testSetValueIndirect(self): - """Indirect signal emission: QSpinBox using valueChanged(int)/setValue(int)""" - spinSend = QSpinBox() - spinRec = QSpinBox() - - spinRec.setValue(5) - - spinSend.valueChanged.connect(spinRec.setValue) - self.assertEqual(spinRec.value(), 5) - spinSend.setValue(3) - self.assertEqual(spinRec.value(), 3) - self.assertEqual(spinSend.value(), 3) - - def testSetValue(self): - """Direct signal emission: QSpinBox using valueChanged(int)/setValue(int)""" - spinSend = QSpinBox() - spinRec = QSpinBox() - - spinRec.setValue(5) - spinSend.setValue(42) - - spinSend.valueChanged.connect(spinRec.setValue) - self.assertEqual(spinRec.value(), 5) - self.assertEqual(spinSend.value(), 42) - spinSend.emit(SIGNAL('valueChanged(int)'), 3) - - self.assertEqual(spinRec.value(), 3) - # Direct emission shouldn't change the value of the emitter - self.assertEqual(spinSend.value(), 42) - - spinSend.emit(SIGNAL('valueChanged(int)'), 66) - self.assertEqual(spinRec.value(), 66) - self.assertEqual(spinSend.value(), 42) + +class ButtonPySlot(UsesQApplication, BasicPySlotCase): + """Tests the connection of python slots to QPushButton signals""" + + def testButtonClicked(self): + """Connection of a python slot to QPushButton.clicked()""" + button = QPushButton('Mylabel') + button.clicked.connect(self.cb) + self.args = tuple() + button.clicked.emit() + self.assertTrue(self.called) + + def testButtonClick(self): + """Indirect qt signal emission using the QPushButton.click() method """ + button = QPushButton('label') + button.clicked.connect(self.cb) + self.args = tuple() + button.click() + self.assertTrue(self.called) + + +class SpinBoxPySlot(UsesQApplication, BasicPySlotCase): + """Tests the connection of python slots to QSpinBox signals""" + + def setUp(self): + super(SpinBoxPySlot, self).setUp() + self.spin = QSpinBox() + + def tearDown(self): + del self.spin + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + super(SpinBoxPySlot, self).tearDown() + + def testSpinBoxValueChanged(self): + """Connection of a python slot to QSpinBox.valueChanged(int)""" + self.spin.valueChanged.connect(self.cb) + self.args = [3] + self.spin.valueChanged.emit(*self.args) + self.assertTrue(self.called) + + def testSpinBoxValueChangedImplicit(self): + """Indirect qt signal emission using QSpinBox.setValue(int)""" + self.spin.valueChanged.connect(self.cb) + self.args = [42] + self.spin.setValue(self.args[0]) + self.assertTrue(self.called) + + def atestSpinBoxValueChangedFewArgs(self): + """Emission of signals with fewer arguments than needed""" + self.spin.valueChanged.connect(self.cb) + self.args = (554,) + self.assertRaises(TypeError, self.spin.valueChanged.emit) + + +class QSpinBoxQtSlots(UsesQApplication): + """Tests the connection to QSpinBox qt slots""" + + qapplication = True + + def testSetValueIndirect(self): + """Indirect signal emission: QSpinBox using valueChanged(int)/setValue(int)""" + spinSend = QSpinBox() + spinRec = QSpinBox() + + spinRec.setValue(5) + + spinSend.valueChanged.connect(spinRec.setValue) + self.assertEqual(spinRec.value(), 5) + spinSend.setValue(3) + self.assertEqual(spinRec.value(), 3) + self.assertEqual(spinSend.value(), 3) + + def testSetValue(self): + """Direct signal emission: QSpinBox using valueChanged(int)/setValue(int)""" + spinSend = QSpinBox() + spinRec = QSpinBox() + + spinRec.setValue(5) + spinSend.setValue(42) + + spinSend.valueChanged.connect(spinRec.setValue) + self.assertEqual(spinRec.value(), 5) + self.assertEqual(spinSend.value(), 42) + spinSend.valueChanged.emit(3) + + self.assertEqual(spinRec.value(), 3) + # Direct emission shouldn't change the value of the emitter + self.assertEqual(spinSend.value(), 42) + + spinSend.valueChanged.emit(66) + self.assertEqual(spinRec.value(), 66) + self.assertEqual(spinSend.value(), 42) if __name__ == '__main__': diff --git a/sources/pyside6/tests/signals/signal_emission_test.py b/sources/pyside6/tests/signals/signal_emission_test.py index 862a03faa..b31d89c2f 100644 --- a/sources/pyside6/tests/signals/signal_emission_test.py +++ b/sources/pyside6/tests/signals/signal_emission_test.py @@ -14,47 +14,47 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from init_paths import init_test_paths init_test_paths(False) -from PySide6.QtCore import QObject, SIGNAL, SLOT, QProcess, QTimeLine +from PySide6.QtCore import QObject, Signal, SIGNAL, QProcess, QTimeLine -from helper.basicpyslotcase import BasicPySlotCase -from helper.usesqcoreapplication import UsesQCoreApplication +from helper.usesqapplication import UsesQApplication -class ArgsOnEmptySignal(UsesQCoreApplication): +class ArgsOnEmptySignal(UsesQApplication): '''Trying to emit a signal without arguments passing some arguments''' def testArgsToNoArgsSignal(self): '''Passing arguments to a signal without arguments''' process = QProcess() - self.assertRaises(TypeError, process.emit, SIGNAL('started()'), 42) + self.assertRaises(TypeError, process.started.emit, 42) -class MoreArgsOnEmit(UsesQCoreApplication): +class MoreArgsOnEmit(UsesQApplication): '''Trying to pass more args than needed to emit (signals with args)''' def testMoreArgs(self): '''Passing more arguments than needed''' process = QProcess() - self.assertRaises(TypeError, process.emit, SIGNAL('finished(int)'), 55, 55) + self.assertRaises(TypeError, process.finished.emit, 55, QProcess.ExitStatus.NormalExit, 42) -class Dummy(QObject): - '''Dummy class''' - pass +class Sender(QObject): + '''Sender class''' + dummy = Signal() + dummy_int = Signal(int) -class PythonSignalToCppSlots(UsesQCoreApplication): + +class PythonSignalToCppSlots(UsesQApplication): '''Connect python signals to C++ slots''' def testWithoutArgs(self): '''Connect python signal to QTimeLine.toggleDirection()''' timeline = QTimeLine() - dummy = Dummy() - QObject.connect(dummy, SIGNAL('dummy()'), - timeline, SLOT('toggleDirection()')) + sender = Sender() + sender.dummy.connect(timeline.toggleDirection) orig_dir = timeline.direction() - dummy.emit(SIGNAL('dummy()')) + sender.dummy.emit() new_dir = timeline.direction() if orig_dir == QTimeLine.Forward: @@ -65,17 +65,16 @@ class PythonSignalToCppSlots(UsesQCoreApplication): def testWithArgs(self): '''Connect python signals to QTimeLine.setCurrentTime(int)''' timeline = QTimeLine() - dummy = Dummy() + sender = Sender() - QObject.connect(dummy, SIGNAL('dummy(int)'), - timeline, SLOT('setCurrentTime(int)')) + sender.dummy_int.connect(timeline.setCurrentTime) current = timeline.currentTime() - dummy.emit(SIGNAL('dummy(int)'), current + 42) + sender.dummy_int.emit(current + 42) self.assertEqual(timeline.currentTime(), current + 42) -class CppSignalsToCppSlots(UsesQCoreApplication): +class CppSignalsToCppSlots(UsesQApplication): '''Connection between C++ slots and signals''' def testWithoutArgs(self): @@ -83,13 +82,13 @@ class CppSignalsToCppSlots(UsesQCoreApplication): process = QProcess() timeline = QTimeLine() - QObject.connect(process, SIGNAL('finished(int, QProcess::ExitStatus)'), - timeline, SLOT('toggleDirection()')) + process.finished.connect(timeline.toggleDirection) orig_dir = timeline.direction() process.start(sys.executable, ['-c', '"print 42"']) - process.waitForFinished() + self.assertTrue(process.waitForStarted()) + self.assertTrue(process.waitForFinished()) new_dir = timeline.direction() @@ -107,25 +106,25 @@ def someSlot(args=None): called = True -class DynamicSignalsToFuncPartial(UsesQCoreApplication): +class DynamicSignalsToFuncPartial(UsesQApplication): def testIt(self): global called called = False - o = QObject() - o.connect(o, SIGNAL("ASignal()"), functools.partial(someSlot, "partial ..")) - o.emit(SIGNAL("ASignal()")) + o = Sender() + o.dummy.connect(functools.partial(someSlot, "partial ..")) + o.dummy.emit() self.assertTrue(called) -class EmitUnknownType(UsesQCoreApplication): +class EmitUnknownType(UsesQApplication): def testIt(self): a = QObject() a.connect(SIGNAL('foobar(Dummy)'), lambda x: 42) # Just connect with an unknown type self.assertRaises(TypeError, a.emit, SIGNAL('foobar(Dummy)'), 22) -class EmitEnum(UsesQCoreApplication): +class EmitEnum(UsesQApplication): """Test emission of enum arguments""" def slot(self, arg): diff --git a/sources/pyside6/tests/signals/signal_manager_refcount_test.py b/sources/pyside6/tests/signals/signal_manager_refcount_test.py index 31d3bc85e..955d5b65b 100644 --- a/sources/pyside6/tests/signals/signal_manager_refcount_test.py +++ b/sources/pyside6/tests/signals/signal_manager_refcount_test.py @@ -12,11 +12,12 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from init_paths import init_test_paths init_test_paths(False) -from PySide6.QtCore import QObject, SIGNAL +from PySide6.QtCore import QObject class SignalManagerRefCount(unittest.TestCase): - """Simple test case to check if the signal_manager is erroneously incrementing the object refcounter""" + """Simple test case to check if the signal_manager is erroneously incrementing the + object refcounter.""" @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount") def testObjectRefcount(self): @@ -27,10 +28,9 @@ class SignalManagerRefCount(unittest.TestCase): refcount = sys.getrefcount(obj) obj.destroyed.connect(callback) self.assertEqual(refcount, sys.getrefcount(obj)) - QObject.disconnect(obj, SIGNAL('destroyed()'), callback) + obj.destroyed.disconnect(callback) self.assertEqual(refcount, sys.getrefcount(obj)) if __name__ == '__main__': unittest.main() - diff --git a/sources/pyside6/tests/signals/signal_newenum_test.py b/sources/pyside6/tests/signals/signal_newenum_test.py new file mode 100644 index 000000000..5fbb875af --- /dev/null +++ b/sources/pyside6/tests/signals/signal_newenum_test.py @@ -0,0 +1,50 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from init_paths import init_test_paths +init_test_paths(False) + +from PySide6.QtCore import QObject, Qt, Slot, Signal + + +class Receiver(QObject): + def __init__(self): + super().__init__() + self.result = 0 + + @Slot(Qt.Alignment, str) + def handler(self, e, s): + print('handler', e, "type=", type(e).__name__, s) + self.result += 1 + + +class Sender(QObject): + test_sig = Signal(Qt.AlignmentFlag, str) + + def __init__(self): + super().__init__() + + def emit_test_sig(self): + self.test_sig.emit(Qt.AlignLeft, "bla") + + +class TestSignalNewEnum(unittest.TestCase): + """Test for PYSIDE-2095, signals with new enums in Python 3.11.""" + + def testIt(self): + sender = Sender() + receiver = Receiver() + sender.test_sig.connect(receiver.handler) + + sender.emit_test_sig() + self.assertEqual(receiver.result, 1) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/pyside6/tests/signals/signal_object_test.py b/sources/pyside6/tests/signals/signal_object_test.py index 1f60b4c3a..607f51813 100644 --- a/sources/pyside6/tests/signals/signal_object_test.py +++ b/sources/pyside6/tests/signals/signal_object_test.py @@ -12,7 +12,7 @@ from init_paths import init_test_paths init_test_paths(False) from PySide6.QtCore import QTimer, Signal, QObject, Slot, Qt -from helper.usesqcoreapplication import UsesQCoreApplication +from helper.usesqapplication import UsesQApplication class MyObject(QTimer): @@ -37,7 +37,7 @@ class MyObject(QTimer): self._o = o -class SignalObjectTest(UsesQCoreApplication): +class SignalObjectTest(UsesQApplication): def cb(self): self._cb_called = True self.app.exit() diff --git a/sources/pyside6/tests/signals/signal_signature_test.py b/sources/pyside6/tests/signals/signal_signature_test.py index 7477f6243..e8f08b2d9 100644 --- a/sources/pyside6/tests/signals/signal_signature_test.py +++ b/sources/pyside6/tests/signals/signal_signature_test.py @@ -13,13 +13,17 @@ from init_paths import init_test_paths init_test_paths(False) from PySide6.QtCore import QObject, Signal, SIGNAL, SLOT -from helper.usesqcoreapplication import UsesQCoreApplication +from helper.usesqapplication import UsesQApplication called = False name = "Old" +class Sender(QObject): + dummySignal = Signal() + + class Obj(QObject): dummySignalArgs = Signal(str) numberSignal = Signal(int) @@ -50,7 +54,7 @@ def callback_empty(): pass -class TestConnectNotifyWithNewStyleSignals(UsesQCoreApplication): +class TestConnectNotifyWithNewStyleSignals(UsesQApplication): '''Test case for signal signature received by QObject::connectNotify().''' def testOldStyle(self): @@ -78,9 +82,9 @@ class TestConnectNotifyWithNewStyleSignals(UsesQCoreApplication): def testStaticSlot(self): global called - sender = Obj() - sender.connect(sender, SIGNAL("dummySignal()"), Obj.static_method) - sender.emit(SIGNAL("dummySignal()")) + sender = Sender() + sender.dummySignal.connect(Obj.static_method) + sender.dummySignal.emit() self.assertTrue(called) def testStaticSlotArgs(self): @@ -99,4 +103,3 @@ class TestConnectNotifyWithNewStyleSignals(UsesQCoreApplication): if __name__ == '__main__': unittest.main() - diff --git a/sources/pyside6/tests/signals/signal_with_primitive_type_test.py b/sources/pyside6/tests/signals/signal_with_primitive_type_test.py index a4d5f33a7..01492b333 100644 --- a/sources/pyside6/tests/signals/signal_with_primitive_type_test.py +++ b/sources/pyside6/tests/signals/signal_with_primitive_type_test.py @@ -10,7 +10,7 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from init_paths import init_test_paths init_test_paths(False) -from PySide6.QtCore import QCoreApplication, QObject, QTimeLine, SIGNAL +from PySide6.QtCore import QCoreApplication, QTimeLine class SignalPrimitiveTypeTest(unittest.TestCase): @@ -36,5 +36,3 @@ class SignalPrimitiveTypeTest(unittest.TestCase): if __name__ == '__main__': unittest.main() - - diff --git a/sources/pyside6/tests/signals/signals.pyproject b/sources/pyside6/tests/signals/signals.pyproject new file mode 100644 index 000000000..b63724eaf --- /dev/null +++ b/sources/pyside6/tests/signals/signals.pyproject @@ -0,0 +1,19 @@ +{ + "files": ["anonymous_slot_leak_test.py", "args_dont_match_test.py", + "bug_189.py", "bug_311.py", "bug_312.py", "bug_319.py", "bug_79.py", + "decorators_test.py", "disconnect_test.py", "invalid_callback_test.py", + "lambda_gui_test.py", "lambda_test.py", "leaking_signal_test.py", + "multiple_connections_gui_test.py", "multiple_connections_test.py", + "pysignal_test.py", "qobject_callable_connect_test.py", "qobject_destroyed_test.py", + "qobject_receivers_test.py", "qobject_sender_test.py", "ref01_test.py", + "ref02_test.py", "ref03_test.py", "ref04_test.py", "ref05_test.py", + "ref06_test.py", "segfault_proxyparent_test.py", + "self_connect_test.py", "short_circuit_test.py", + "signal2signal_connect_test.py", "signal_across_threads.py", + "signal_autoconnect_test.py", "signal_connectiontype_support_test.py", + "signal_emission_gui_test.py", "signal_emission_test.py", + "signal_enum_test.py", "signal_func_test.py", "signal_manager_refcount_test.py", + "signal_newenum_test.py", "signal_number_limit_test.py", + "signal_object_test.py", "signal_signature_test.py", "signal_with_primitive_type_test.py", + "slot_reference_count_test.py", "static_metaobject_test.py"] +} diff --git a/sources/pyside6/tests/signals/slot_reference_count_test.py b/sources/pyside6/tests/signals/slot_reference_count_test.py index 10a40597e..9d5c73652 100644 --- a/sources/pyside6/tests/signals/slot_reference_count_test.py +++ b/sources/pyside6/tests/signals/slot_reference_count_test.py @@ -12,12 +12,14 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from init_paths import init_test_paths init_test_paths(False) -from PySide6.QtCore import QObject, SIGNAL, SLOT +from PySide6.QtCore import QObject, Signal class Dummy(QObject): + foo = Signal() + def dispatch(self): - self.emit(SIGNAL('foo()')) + self.foo.emit() class PythonSignalRefCount(unittest.TestCase): @@ -35,10 +37,10 @@ class PythonSignalRefCount(unittest.TestCase): self.assertEqual(sys.getrefcount(cb), 2) - QObject.connect(self.emitter, SIGNAL('foo()'), cb) + self.emitter.foo.connect(cb) self.assertEqual(sys.getrefcount(cb), 3) - QObject.disconnect(self.emitter, SIGNAL('foo()'), cb) + self.emitter.foo.disconnect(cb) self.assertEqual(sys.getrefcount(cb), 2) @@ -60,7 +62,7 @@ class CppSignalRefCount(unittest.TestCase): self.emitter.destroyed.connect(cb) self.assertEqual(sys.getrefcount(cb), 3) - QObject.disconnect(self.emitter, SIGNAL('destroyed()'), cb) + self.emitter.destroyed.disconnect(cb) self.assertEqual(sys.getrefcount(cb), 2) diff --git a/sources/pyside6/tests/signals/static_metaobject_test.py b/sources/pyside6/tests/signals/static_metaobject_test.py index 44d10cd7d..d7bf73e44 100644 --- a/sources/pyside6/tests/signals/static_metaobject_test.py +++ b/sources/pyside6/tests/signals/static_metaobject_test.py @@ -14,13 +14,22 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from init_paths import init_test_paths init_test_paths(False) -from PySide6.QtCore import QObject, SIGNAL, Slot -from helper.usesqcoreapplication import UsesQCoreApplication +from PySide6.QtCore import QObject, Signal, Slot, SIGNAL +from helper.usesqapplication import UsesQApplication + + +class Sender(QObject): + + foo = Signal() + foo2 = Signal() class MyObject(QObject): + + foo2 = Signal() + def __init__(self, parent=None): - QObject.__init__(self, parent) + super().__init__(parent) self._slotCalledCount = 0 # this '@Slot()' is needed to get the right sort order in testSharedSignalEmission. @@ -30,10 +39,11 @@ class MyObject(QObject): self._slotCalledCount = self._slotCalledCount + 1 -class StaticMetaObjectTest(UsesQCoreApplication): +class StaticMetaObjectTest(UsesQApplication): def testSignalPropagation(self): - o = MyObject() + """Old style, dynamic signal creation.""" + o = QObject() o2 = MyObject() # SIGNAL foo not created yet @@ -55,17 +65,17 @@ class StaticMetaObjectTest(UsesQCoreApplication): self.assertEqual(o.metaObject().indexOfSignal("foo()"), -1) def testSharedSignalEmission(self): - o = QObject() + o = Sender() m = MyObject() - o.connect(SIGNAL("foo2()"), m.mySlot) - m.connect(SIGNAL("foo2()"), m.mySlot) - o.emit(SIGNAL("foo2()")) + o.foo2.connect(m.mySlot) + m.foo2.connect(m.mySlot) + o.foo2.emit() self.assertEqual(m._slotCalledCount, 1) del o # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion gc.collect() - m.emit(SIGNAL("foo2()")) + m.foo2.emit() self.assertEqual(m._slotCalledCount, 2) |