mirror of https://github.com/KLayout/klayout.git
Qt signals can be bound to functions with less args in Python
With this change it is possible to bind signals to functions
accepting less arguments. For example:
def triggered():
...
b = pya.QPushButton()
b.clicked(triggered)
b.emit_clicked(True)
wasn't working before since triggered() gets one parameter
(checked) and the call fails. Now, additional parameters are
ignored.
This commit is contained in:
parent
ede58ae728
commit
1f60e7729e
|
|
@ -44,13 +44,14 @@ reuse=0
|
||||||
qt="/opt/qt/4.6.3/include"
|
qt="/opt/qt/4.6.3/include"
|
||||||
qt5="/opt/qt/5.5.1/include"
|
qt5="/opt/qt/5.5.1/include"
|
||||||
inst_dir_common=`pwd`/scripts/mkqtdecl_common
|
inst_dir_common=`pwd`/scripts/mkqtdecl_common
|
||||||
inst_dir=`pwd`/scripts/mkqtdecl
|
inst_dir4=`pwd`/scripts/mkqtdecl4
|
||||||
inst_dir5=`pwd`/scripts/mkqtdecl5
|
inst_dir5=`pwd`/scripts/mkqtdecl5
|
||||||
src_dir=`pwd`/src
|
src_dir=`pwd`/src
|
||||||
src_name4=gsiqt4
|
src_name4=gsiqt4
|
||||||
src_name5=gsiqt5
|
src_name5=gsiqt5
|
||||||
|
|
||||||
src_name=$src_name4
|
src_name=$src_name4
|
||||||
|
inst_dir=$inst_dir4
|
||||||
|
|
||||||
while [ "$1" != "" ]; do
|
while [ "$1" != "" ]; do
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@
|
||||||
#include "pyaObject.h"
|
#include "pyaObject.h"
|
||||||
#include "pyaMarshal.h"
|
#include "pyaMarshal.h"
|
||||||
#include "pyaUtils.h"
|
#include "pyaUtils.h"
|
||||||
|
#include "pyaConvert.h"
|
||||||
#include "pya.h"
|
#include "pya.h"
|
||||||
|
|
||||||
#include "tlLog.h"
|
#include "tlLog.h"
|
||||||
|
|
@ -214,19 +215,46 @@ void SignalHandler::call (const gsi::MethodBase *meth, gsi::SerialArgs &args, gs
|
||||||
|
|
||||||
tl::Heap heap;
|
tl::Heap heap;
|
||||||
|
|
||||||
PythonRef argv (PyTuple_New (std::distance (meth->begin_arguments (), meth->end_arguments ())));
|
int args_avail = int (std::distance (meth->begin_arguments (), meth->end_arguments ()));
|
||||||
|
PythonRef argv (PyTuple_New (args_avail));
|
||||||
// TODO: callbacks with default arguments?
|
|
||||||
for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); args && a != meth->end_arguments (); ++a) {
|
for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); args && a != meth->end_arguments (); ++a) {
|
||||||
PyTuple_SetItem (argv.get (), int (a - meth->begin_arguments ()), pop_arg (*a, args, NULL, heap).release ());
|
PyTuple_SetItem (argv.get (), int (a - meth->begin_arguments ()), pop_arg (*a, args, NULL, heap).release ());
|
||||||
}
|
}
|
||||||
|
|
||||||
PythonRef result;
|
PythonRef result;
|
||||||
for (std::vector<CallbackFunction>::const_iterator c = m_cbfuncs.begin (); c != m_cbfuncs.end (); ++c) {
|
for (std::vector<CallbackFunction>::const_iterator c = m_cbfuncs.begin (); c != m_cbfuncs.end (); ++c) {
|
||||||
result = PythonRef (PyObject_CallObject (c->callable ().get (), argv.get ()));
|
|
||||||
|
// determine the number of arguments required
|
||||||
|
int arg_count = args_avail;
|
||||||
|
if (args_avail > 0) {
|
||||||
|
|
||||||
|
PythonRef fc (PyObject_GetAttrString (c->callable ().get (), "__code__"));
|
||||||
|
if (fc) {
|
||||||
|
PythonRef ac (PyObject_GetAttrString (fc.get (), "co_argcount"));
|
||||||
|
if (ac) {
|
||||||
|
arg_count = python2c<int> (ac.get ());
|
||||||
|
if (PyObject_HasAttrString (c->callable ().get (), "__self__")) {
|
||||||
|
arg_count -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// use less arguments if applicable
|
||||||
|
if (arg_count == 0) {
|
||||||
|
result = PythonRef (PyObject_CallObject (c->callable ().get (), NULL));
|
||||||
|
} else if (arg_count < args_avail) {
|
||||||
|
PythonRef argv_less (PyTuple_GetSlice (argv.get (), 0, arg_count));
|
||||||
|
result = PythonRef (PyObject_CallObject (c->callable ().get (), argv_less.get ()));
|
||||||
|
} else {
|
||||||
|
result = PythonRef (PyObject_CallObject (c->callable ().get (), argv.get ()));
|
||||||
|
}
|
||||||
|
|
||||||
if (! result) {
|
if (! result) {
|
||||||
check_error ();
|
check_error ();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
push_arg (meth->ret_type (), ret, result.get (), heap);
|
push_arg (meth->ret_type (), ret, result.get (), heap);
|
||||||
|
|
|
||||||
|
|
@ -480,6 +480,73 @@ class QtBindingTest(unittest.TestCase):
|
||||||
self.assertEqual(parent._destroyed(), True)
|
self.assertEqual(parent._destroyed(), True)
|
||||||
self.assertEqual(child._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
|
# run unit tests
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
||||||
|
|
@ -567,6 +567,31 @@ class QtBinding_TestClass < TestBase
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_45
|
||||||
|
|
||||||
|
# Ability to connect to signals while ignoring arguments and
|
||||||
|
# to emit signals
|
||||||
|
|
||||||
|
b = RBA::QPushButton::new
|
||||||
|
|
||||||
|
triggered = ""
|
||||||
|
b.clicked { |checked| triggered += (checked ? "1" : "0") }
|
||||||
|
|
||||||
|
assert_equal(triggered, "")
|
||||||
|
b.emit_clicked(true)
|
||||||
|
assert_equal(triggered, "1")
|
||||||
|
b.emit_clicked(false)
|
||||||
|
assert_equal(triggered, "10")
|
||||||
|
|
||||||
|
b.clicked { triggered += "*" }
|
||||||
|
|
||||||
|
b.emit_clicked(true)
|
||||||
|
assert_equal(triggered, "10*")
|
||||||
|
b.emit_clicked(false)
|
||||||
|
assert_equal(triggered, "10**")
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
load("test_epilogue.rb")
|
load("test_epilogue.rb")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue