Merge lp:~kissiel/checkbox/use-pyotherside1.4 into lp:checkbox

Proposed by Maciej Kisielewski
Status: Merged
Approved by: Zygmunt Krynicki
Approved revision: 3814
Merged at revision: 3813
Proposed branch: lp:~kissiel/checkbox/use-pyotherside1.4
Merge into: lp:checkbox
Diff against target: 417 lines (+97/-193)
6 files modified
checkbox-touch/checkbox-touch.qml (+3/-7)
checkbox-touch/components/CheckboxTouchApplication.qml (+5/-4)
checkbox-touch/components/PythonLogger.qml (+13/-7)
checkbox-touch/components/PythonObjectHandle.qml (+0/-76)
checkbox-touch/components/PythonObjectRef.qml (+69/-0)
checkbox-touch/py/checkbox_touch.py (+7/-99)
To merge this branch: bzr merge lp:~kissiel/checkbox/use-pyotherside1.4
Reviewer Review Type Date Requested Status
Zygmunt Krynicki (community) Approve
Review via email: mp+260106@code.launchpad.net

Description of the change

Migration from PythonObjectHandle to PythonObjectRef (pyotherside-1.4)

To post a comment you must log in.
3811. By Maciej Kisielewski

checkbox-touch: add PythonObjectRef component

This patch adds qml component that handles referencing python objects from qml
side.

Signed-off-by: Maciej Kisielewski <email address hidden>

3812. By Maciej Kisielewski

checkbox-touch: migrate CheckboxTouchApplication class to PythonObjectRef

This patch moves main CheckboxTouchApplication class to be used through
PythonObjectRef from QML side.

3813. By Maciej Kisielewski

checkbox-touch: migrate PythonLogger to PythonObjectRef

Signed-off-by: Maciej Kisielewski <email address hidden>

3814. By Maciej Kisielewski

checkbox-touch: remove PythonObjectHandle

Signed-off-by: Maciej Kisielewski <email address hidden>

Revision history for this message
Zygmunt Krynicki (zyga) wrote :

Looks good, thanks for changing this. +1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'checkbox-touch/checkbox-touch.qml'
--- checkbox-touch/checkbox-touch.qml 2015-05-20 07:56:01 +0000
+++ checkbox-touch/checkbox-touch.qml 2015-05-27 06:52:22 +0000
@@ -23,7 +23,7 @@
23import Ubuntu.Components 1.123import Ubuntu.Components 1.1
24import Ubuntu.Components.Popups 0.124import Ubuntu.Components.Popups 0.1
25import QtQuick.Layouts 1.125import QtQuick.Layouts 1.1
26import io.thp.pyotherside 1.226import io.thp.pyotherside 1.4
27import "components"27import "components"
28import "components/ErrorLogic.js" as ErrorLogic28import "components/ErrorLogic.js" as ErrorLogic
29import "components/CbtDialogLogic.js" as CbtDialogLogic29import "components/CbtDialogLogic.js" as CbtDialogLogic
@@ -110,9 +110,7 @@
110 // create_app_object() function and assign the resulting handle110 // create_app_object() function and assign the resulting handle
111 // back to the application component.111 // back to the application component.
112 py.importModule("checkbox_touch", function() {112 py.importModule("checkbox_touch", function() {
113 call("checkbox_touch.create_app_object", [], function(handle) {113 app.construct("checkbox_touch.create_app_object", [])
114 app.handle = handle;
115 });
116 });114 });
117 }115 }
118 onError: {116 onError: {
@@ -146,9 +144,7 @@
146 Component.onCompleted: {144 Component.onCompleted: {
147 py.Component.onCompleted.connect(function() {145 py.Component.onCompleted.connect(function() {
148 py.importModule("checkbox_touch", function() {146 py.importModule("checkbox_touch", function() {
149 py.call("checkbox_touch.get_qml_logger", [], function(handle) {147 construct("checkbox_touch.get_qml_logger", []);
150 logger.handle = handle;
151 });
152 });148 });
153 });149 });
154 }150 }
155151
=== modified file 'checkbox-touch/components/CheckboxTouchApplication.qml'
--- checkbox-touch/components/CheckboxTouchApplication.qml 2015-05-12 09:58:35 +0000
+++ checkbox-touch/components/CheckboxTouchApplication.qml 2015-05-27 06:52:22 +0000
@@ -1,10 +1,11 @@
1/*1/*
2 * This file is part of Checkbox2 * This file is part of Checkbox
3 *3 *
4 * Copyright 2014 Canonical Ltd.4 * Copyright 2014-2015 Canonical Ltd.
5 *5 *
6 * Authors:6 * Authors:
7 * - Zygmunt Krynicki <zygmunt.krynicki@canonical.com>7 * - Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
8 * - Maciej Kisielewski <maciej.kisielewski@canonical.com>
8 *9 *
9 * This program is free software; you can redistribute it and/or modify10 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by11 * it under the terms of the GNU General Public License as published by
@@ -24,7 +25,7 @@
24import "ErrorLogic.js" as ErrorLogic25import "ErrorLogic.js" as ErrorLogic
2526
2627
27PythonObjectHandle {28PythonObjectRef {
28 id: app29 id: app
29 // Version of the application30 // Version of the application
30 property string applicationVersion31 property string applicationVersion
@@ -37,7 +38,7 @@
37 signal appReady();38 signal appReady();
3839
39 // Signal sent when a session becomes ready40 // Signal sent when a session becomes ready
40 signal sessionReady();41 signal sessionReady()
4142
42 // Create a new session43 // Create a new session
43 //44 //
@@ -168,7 +169,7 @@
168169
169 // Internal handler that triggers a call to python to query for runtime and170 // Internal handler that triggers a call to python to query for runtime and
170 // application versions.171 // application versions.
171 onHandleReady: {172 onObjectReady: {
172 request("get_version_pair", [], function(result) {173 request("get_version_pair", [], function(result) {
173 app.applicationVersion = result.application_version;174 app.applicationVersion = result.application_version;
174 app.plainboxVersion = result.plainbox_version;175 app.plainboxVersion = result.plainbox_version;
175176
=== modified file 'checkbox-touch/components/PythonLogger.qml'
--- checkbox-touch/components/PythonLogger.qml 2015-02-09 14:19:45 +0000
+++ checkbox-touch/components/PythonLogger.qml 2015-05-27 06:52:22 +0000
@@ -20,7 +20,7 @@
20 */20 */
2121
22/*! \brief Python-driven logger22/*! \brief Python-driven logger
23 \inherits PythonObjectHandle23 \inherits PythonObjectRef
2424
25 This component uses pyotherside to forward logging events to python.25 This component uses pyotherside to forward logging events to python.
26 It monkey-patches console.log and console.error to capture their calls26 It monkey-patches console.log and console.error to capture their calls
@@ -28,7 +28,8 @@
28*/28*/
29import QtQuick 2.029import QtQuick 2.0
3030
31PythonObjectHandle {31PythonObjectRef {
32 id: pythonLogger
3233
33 function debug(msg) {34 function debug(msg) {
34 invoke('debug', [msg], function() {});35 invoke('debug', [msg], function() {});
@@ -63,13 +64,18 @@
6364
64 /** Overridden invoke that doesn't log invoke calls */65 /** Overridden invoke that doesn't log invoke calls */
65 function invoke(func, args, callback) {66 function invoke(func, args, callback) {
66 if (py !== null && handle > 0) {67 if (py !== null && object !== null) {
67 py.call("py_invoke", [handle, func, args], function(response) {68 var callable = py.getattr(object, func);
69 if (!callable) {
70 console.error("Unable to invoke " + func + " on python logger");
71 throw "trying to invoke not existing method"
72 }
73 py.call(callable, args, function(response) {
68 callback(response);74 callback(response);
69 });75 });
70 } else {76 } else {
71 _original_console_error("unable to py_invoke: " + handle + ", " + func + ", " + JSON.stringify(args));77 _original_console_error("unable to invoke " + func + " on python logger");
72 throw "py_invoke called without ready py and handle";78 throw "invoke called without py initiated and/or object constructed";
73 }79 }
74 }80 }
7581
@@ -79,7 +85,7 @@
79 _original_console_error = console.error;85 _original_console_error = console.error;
80 }86 }
8187
82 onHandleReady: {88 onObjectReady: {
83 /* monkey-patch console.log and console.error */89 /* monkey-patch console.log and console.error */
84 console.log = function() { debug(_argsToString(arguments)); };90 console.log = function() { debug(_argsToString(arguments)); };
85 console.error = function() { error(_argsToString(arguments)); };91 console.error = function() { error(_argsToString(arguments)); };
8692
=== removed file 'checkbox-touch/components/PythonObjectHandle.qml'
--- checkbox-touch/components/PythonObjectHandle.qml 2014-09-29 19:18:47 +0000
+++ checkbox-touch/components/PythonObjectHandle.qml 1970-01-01 00:00:00 +0000
@@ -1,76 +0,0 @@
1/*
2 * This file is part of Checkbox
3 *
4 * Copyright 2014 Canonical Ltd.
5 *
6 * Authors:
7 * - Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 3.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21import QtQuick 2.0
22
23
24QtObject {
25 // Reference to pyotherside's Python object
26 property var py: null
27 // Handle to a python object (via RemoteObjectLifecycleManager)
28 // if it is < 0 then it is an error of some kind
29 // if it is 0 (default) then the handle is just invalid
30 // if it is > 0 then it represents a valid object handle
31 property int handle: 0
32 // Flag set just prior to sending the handleReady() signal
33 property bool _ready: false
34
35 // Signal sent when the handle is initially assigned *and* py is already
36 // assigned (so when the python object is ready to be interacted with)
37 signal handleReady();
38
39 // Dereference the python object when we are shutting down
40 Component.onDestruction: _unref()
41 // Maybe send the handleReady signal (once) when handle is changed
42 onHandleChanged: _maybeReady()
43 // Maybe send the handleReady signal (once) when 'py' is changed
44 onPyChanged: _maybeReady()
45
46 /** Send the handleReady() signal (once) if both py and handle are ready */
47 function _maybeReady() {
48 if (handle > 0 && py !== null && _ready == false) {
49 _ready = true;
50 handleReady()
51 }
52 }
53
54 /** Dereference this object */
55 function _unref() {
56 if (py !== null && handle > 0) {
57 // console.log("py_unref: " + handle);
58 py.call("py_unref", [handle]);
59 handle = 0;
60 }
61 }
62
63 /** Call a method on this object */
64 function invoke(func, args, callback) {
65 if (py !== null && handle > 0) {
66 console.log("py_invoke(" + handle + ", " + func + ", " + JSON.stringify(args) + ") ...");
67 py.call("py_invoke", [handle, func, args], function(response) {
68 console.log("py_invoke(" + handle + ", " + func + ", " + JSON.stringify(args) + ") -> " + JSON.stringify(response));
69 callback(response);
70 });
71 } else {
72 console.error("unable to py_invoke: " + handle + ", " + func + ", " + JSON.stringify(args));
73 throw "py_invoke called without ready py and handle";
74 }
75 }
76}
770
=== added file 'checkbox-touch/components/PythonObjectRef.qml'
--- checkbox-touch/components/PythonObjectRef.qml 1970-01-01 00:00:00 +0000
+++ checkbox-touch/components/PythonObjectRef.qml 2015-05-27 06:52:22 +0000
@@ -0,0 +1,69 @@
1/*
2 * This file is part of Checkbox
3 *
4 * Copyright 2015 Canonical Ltd.
5 *
6 * Authors:
7 * - Maciej Kisielewski <maciej.kisielewski@canonical.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 3.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21import QtQuick 2.0
22
23
24QtObject {
25 id: pythonObjectRef
26
27 // Reference to pyotherside's Python object
28 property var py: null
29
30 // PyObjectRef to the object
31 property var object: null
32
33 // Signal sent when the object reference is ready to use
34 signal objectReady()
35
36 // Creation method name that were used to get the reference to the object
37 property var creationMethodName: null
38
39 function construct(creationMethodName, args) {
40 if (!py) {
41 console.error("Trying to get reference to python object without py initiated!");
42 } else {
43 console.info("Getting reference to python object via " + creationMethodName);
44 py.call(creationMethodName, args, function(result) {
45 object = result;
46 pythonObjectRef.creationMethodName = creationMethodName;
47 objectReady();
48 });
49 }
50 }
51 /** Call a method on this object */
52 function invoke(func, args, callback) {
53 if (py !== null && object !== null) {
54 console.log("invoking " + func + " on object created with" + pythonObjectRef.creationMethodName + ", with args: " + JSON.stringify(args) + " ...");
55 var callable = py.getattr(object, func);
56 if (!callable) {
57 console.log("Unable to invoke " + func + " on object " + JSON.stringify(pythonObjectRef));
58 throw "trying to invoke inexistent method"
59 }
60 py.call(callable, args, function(response) {
61 console.log(func + " on object created with" + pythonObjectRef.creationMethodName + ", with args: " + JSON.stringify(args) + " returned: " + JSON.stringify(response));
62 callback(response);
63 });
64 } else {
65 console.error("Unable to invoke " + func + " on object " + JSON.stringify(pythonObjectRef));
66 throw "invoke called without py initiated and/or object constructed";
67 }
68 }
69}
070
=== modified file 'checkbox-touch/py/checkbox_touch.py'
--- checkbox-touch/py/checkbox_touch.py 2015-05-18 11:20:02 +0000
+++ checkbox-touch/py/checkbox_touch.py 2015-05-27 06:52:22 +0000
@@ -65,72 +65,9 @@
65from embedded_providers import EmbeddedProvider1PlugInCollection65from embedded_providers import EmbeddedProvider1PlugInCollection
6666
67_logger = logging.getLogger('checkbox.touch')67_logger = logging.getLogger('checkbox.touch')
68_manager = None68
6969
7070class PlainboxApplication(metaclass=abc.ABCMeta):
71class VerboseLifecycle:
72 """
73 Mix-in class for verbose lifecycle reporting
74 """
75
76 def __new__(cls, *args, **kwargs):
77 self = super().__new__(cls, *args, **kwargs)
78 _logger.debug("new %s %x", cls.__name__, id(self))
79 return self
80
81 def __del__(self):
82 _logger.debug("del %s %x", self.__class__.__name__, id(self))
83
84
85class RemoteObjectLifecycleManager(VerboseLifecycle):
86 """
87 Remote object life-cycle manager
88
89 This class aids in handling non-trivial objects that are referenced from
90 QML (via pyotherside) but really stored on the python side.
91 """
92
93 def __init__(self):
94 self._count = 0
95 self._handle_map = {}
96
97 def unref(self, handle: int):
98 """
99 Remove a reference represented by the specified handle
100 """
101 _logger.debug("unref %s", handle)
102 del self._handle_map[handle]
103
104 def ref(self, obj: object) -> int:
105 """
106 Store a reference to an object and return the handle
107 """
108 self._count += 1
109 handle = self._count
110 self._handle_map[handle] = obj
111 _logger.debug("ref %r -> %s", obj, handle)
112 return handle
113
114 def invoke(self, handle: int, func: str, args):
115 """
116 Call a method on a object represented by the handle
117
118 :param handle:
119 A (numeric) handle to the objecet
120 :param func:
121 The (name of the) function to call
122 :param args:
123 A list of positional arguments to pass
124 :returns:
125 The value returned by the called method
126 """
127 obj = self._handle_map[handle]
128 impl = getattr(obj, func)
129 retval = impl(*args)
130 return retval
131
132
133class PlainboxApplication(VerboseLifecycle, metaclass=abc.ABCMeta):
134 """71 """
135 Base class for plainbox-based applications.72 Base class for plainbox-based applications.
13673
@@ -141,16 +78,6 @@
141 def __repr__(self):78 def __repr__(self):
142 return "<{}>".format(self.__class__.__name__)79 return "<{}>".format(self.__class__.__name__)
14380
144 @classmethod
145 def create_and_get_handle(cls):
146 """
147 Create an instance of the high-level PlainBox object
148
149 :returns:
150 A handle to a fresh instance of :class:`PlainBox`
151 """
152 return _manager.ref(cls())
153
154 @abc.abstractmethod81 @abc.abstractmethod
155 def get_version_pair(self):82 def get_version_pair(self):
156 """83 """
@@ -820,27 +747,8 @@
820 self._password = password747 self._password = password
821748
822749
823def bootstrap():
824 logging.basicConfig(level=logging.INFO, stream=sys.stderr)
825 logging.info("environ: %r", os.environ)
826 logging.info("path: %r", sys.path)
827 # from plainbox.impl.logging import adjust_logging
828 # from plainbox.impl.logging import setup_logging
829 # Setup logging
830 # setup_logging()
831 # adjust_logging(logging.DEBUG, ['checkbox.stack'], True)
832 # Create the Javascript <=> Python remote object lifecycle manager
833 manager = RemoteObjectLifecycleManager()
834 # Expose top-level functions for pyotherside's simplicity
835 builtins.py_ref = manager.ref
836 builtins.py_unref = manager.unref
837 builtins.py_invoke = manager.invoke
838 return manager
839
840
841def get_qml_logger():750def get_qml_logger():
842 return _manager.ref(logging.getLogger('checkbox.touch.qml'))751 return logging.getLogger('checkbox.touch.qml')
843752
844753
845create_app_object = CheckboxTouchApplication.create_and_get_handle754create_app_object = CheckboxTouchApplication
846_manager = bootstrap()

Subscribers

People subscribed via source and target branches