<br><br><div class="gmail_quote">On Fri, Jun 27, 2008 at 6:23 PM, Paul Giannaros &lt;<a href="mailto:paul@giannaros.org">paul@giannaros.org</a>&gt; wrote:<br><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
Hello,<br>
<br>
I&#39;ve scratched my own itch and written a script engine for Python<br>
using sip/PyQt/PyKDE; I wanted to write a couple of applets in Python<br>
and the Kross bindings wouldn&#39;t work, appeared outdated, and didn&#39;t<br>
provide the kind of API that I think Python users would appreciate<br>
(operating on a global applet object isn&#39;t the nicest thing in the<br>
world when your applet does something interesting).<br>
I have hit an irritating brick wall, though. My MPD cover art applet<br>
is perfect, except that I don&#39;t like its current background -- I&#39;d<br>
much rather it had a translucent background.<br>
Plasma::Applet::setBackgroundHints is a protected method. I cannot<br>
touch it from Python, nor from my AppletScript subclass (class<br>
friendship is not inherited). I&#39;m stuck.<br>
There are plenty of other important things cannot be called:<br>
setIsContainment; setConfigurationRequired; setFailedToLaunch.</blockquote><div>In the Ruby script engine based bindings I have the underlying C++applet in the Plasma::AppletScript class wrapped as a Ruby instance. If any method calls made on the the scripting applet instance are found to be missing they are then forwarded to the underlying applet. <br>
<br>I assume that PyQt works the same way as QtRuby and that SIP will generate a C++ subclass of Plasma::Applet for supporting python with the protected methods mapped onto public. ones which are callable from python.<br>
<br>In Ruby, the code to do the message forwarding looks like this:<br><br>module PlasmaScripting<br>&nbsp; class Applet &lt; Qt::Object<br>&nbsp;&nbsp;&nbsp; ...<br><br>&nbsp;&nbsp;&nbsp; # If a method is called on a PlasmaScripting::Applet instance is found to be missing<br>
&nbsp;&nbsp;&nbsp; # then try calling the method on the underlying Plasma::Applet in the ScriptEngine.<br>&nbsp;&nbsp;&nbsp; def method_missing(method, *args)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super(method, *args)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rescue<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; applet_script.applet.method_missing(method, *args)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end<br>&nbsp;&nbsp;&nbsp; end<br><br>&nbsp;I don&#39;t know that you can do that in python, and maybe you would have to explicitely call each <br>method like setConfigurationRequired() that you wanted to call.<br><br>These calls all work fine in Ruby:<br>
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setAcceptDrops(true)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setBackgroundHints(Plasma::Applet::NoBackground)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setHasConfigurationInterface(true)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setIsContainment(false) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setConfigurationRequired(false)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setFailedToLaunch(false)<br>
<br><br></div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;"> I also<br>
cannot easily handle resize events at my leisure, mouse presses, mouse<br>
moves, etc. I guess that one could install an event filter and create<br>
an intercepting QObject subclass, but that&#39;s not going to make for<br>
nice code at all.</blockquote><div>That is exactly what I&#39;ve done, but from the point of view of the applet application programmer, it looks exactly the same. You can hide the event filtering code, and call event handlers like mousePressEvent() from within the event filter.<br>
<br>In Ruby the code looks like this. I keep a Hash of event types against methods to handle them in @event_handlers. I use Ruby introspection when the applet is initialized to find which event handlers the applet author has implemented, if any:<br>
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if @applet_script.respond_to?(:mousePressEvent)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @event_handlers[Qt::Event::GraphicsSceneMousePress.to_i] = :mousePressEvent<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end<br><br>Then I can quickly retrieve the name of the event handler given the event type and call it with send():<br>
<br>&nbsp;&nbsp;&nbsp; def eventFilter(obj, event)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; handler = @event_handlers[event.type.to_i]<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if handler<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @applet_script.send(handler, event)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return true<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end<br>
&nbsp;&nbsp;&nbsp; end<br>&nbsp;<br>A further issue to be considered is how to call methods in the api which expect a QGraphicsWidget or a QGraphicsLayoutItem, when the script engine applet isn&#39;t really a Plasma::Applet. So I&#39;ve intervened in the api calls and whenever I find an instance of the script applet, I substitute the instance of the applet from the script engine instead. For instance, to make this code work:<br>
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @layout = Qt::GraphicsLinearLayout.new(Qt::Vertical, self)<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @button = Plasma::PushButton.new(self)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @button.text = &quot;Hello world&quot;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; connect(@button, SIGNAL(:clicked), self, SLOT(:button_pressed))<br>
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @label = Plasma::Label.new(self)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @label.text = &quot;My Label&quot;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @layout.addItem(@button)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @layout.addItem(@label)<br><br>&#39;self&#39; is expected to be a kind of Plasma::Applet, and so in the Plasma::PushButton constructor I change the way it is called like this:<br>
<br>&nbsp; class PushButton &lt; Qt::Base<br>&nbsp;&nbsp;&nbsp; def initialize(parent = nil)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if parent.kind_of?(PlasmaScripting::Applet)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super(parent.applet_script.applet)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end<br>&nbsp;&nbsp;&nbsp; end<br>
&nbsp; end<br><br><br></div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;"><br>
Is it intentional that non-native applets are this crippled, or were<br>
tasks as fundamental as these just not considered?</blockquote><div>&nbsp;I&#39;ve implemented both complete bindings for the C++ api, and a ScriptEngine version using techniques like the ones described above to make it pretty well the same as the C++ api. I&#39;m now pretty happy than 95% of users requirements are going to be met by the ScriptEngine version. If you really want to write a containment in Ruby which can&#39;t be done with the ScriptEngine api, you still can, and we&#39;ll have wait to see exactly how common that sort of requirement is going to be.<br>
<br>-- Richard<br><br></div></div><br>