<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>.:: Marcos Dione/StyXman's glob ::. (Posts about javascript)</title><link>https://www.grulic.org.ar/~mdione/glob/</link><description></description><atom:link href="https://www.grulic.org.ar/~mdione/glob/categories/javascript.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2025 &lt;a href="mailto:mdione@grulic.org.ar"&gt;Marcos Dione&lt;/a&gt; </copyright><lastBuildDate>Sat, 15 Nov 2025 20:52:05 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Implementing Selenium with Python and Qt</title><link>https://www.grulic.org.ar/~mdione/glob/posts/implementing-selenium-with-python-and-qt/</link><dc:creator>Marcos Dione</dc:creator><description>&lt;p&gt;I'm writing a python module that allows me to 'drive' a site using Qt. This
means that I can navigate the site, fill forms, submit them and read the
resulting pages and scrape them, Selenium style. The reasons I'm using
Qt are that it has enough support for the site I'm driving (it's the web
frontend of the SIP telephony solution we're using, which has an incomplete API
and I have to automatize several aspects not covered by it); there are python bindings; and
because I can do it headless:
instead of using browser instances, I simply instanciate one
&lt;a href="https://doc.qt.io/archives/qt-5.5/qwebpage.html"&gt;&lt;code&gt;QWebPage&lt;/code&gt;&lt;/a&gt;&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="https://www.grulic.org.ar/~mdione/glob/posts/implementing-selenium-with-python-and-qt/#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;
per thread and that's it.&lt;/p&gt;
&lt;p&gt;The first thing I learned today is that JS objects representing the DOM elements
have two sets of value holders:
&lt;a href="https://stackoverflow.com/questions/5874652/prop-vs-attr"&gt;attributes and properties&lt;/a&gt;.
The properties is what in Python we call
attributes: the object's elements which are accesible with the '.' operator
and hold instance values. The attributes are in fact the HTML element's
attributes that gave the properties' initial values. That is, given the following
HTML element:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"foo° id="&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="s"&gt;" value="&lt;/span&gt;&lt;span class="nx"&gt;quux&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;the initial JS object's attributes &lt;strong&gt;and&lt;/strong&gt; properties will have those values.
If you change the value with your browser, the &lt;code&gt;value&lt;/code&gt; property of that element
will be changed, but not the &lt;code&gt;value&lt;/code&gt; attribute.
When you submit the form, the &lt;code&gt;value&lt;/code&gt; properties of all the form elements are
used, so if you "only' change the &lt;code&gt;value&lt;/code&gt; &lt;em&gt;attribute&lt;/em&gt;, that won't be used.
So, forget attributes. Also, the DOM is the
representation of the actual state of the page, but this state is never reflected in
the HTML source that you can ask your browser to show, but you see those changes
reflected in the browser's debugger. It's like they really
wanted&lt;sup id="fnref:3"&gt;&lt;a class="footnote-ref" href="https://www.grulic.org.ar/~mdione/glob/posts/implementing-selenium-with-python-and-qt/#fn:3"&gt;3&lt;/a&gt;&lt;/sup&gt; to keep initial values apart from current state&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="https://www.grulic.org.ar/~mdione/glob/posts/implementing-selenium-with-python-and-qt/#fn:2"&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;On the Qt side, &lt;a href="https://doc.qt.io/archives/qt-5.5/qwebelement.html"&gt;&lt;code&gt;QWebElement&lt;/code&gt;&lt;/a&gt;
is only the DOM element representation, not the JS object&lt;sup id="fnref:4"&gt;&lt;a class="footnote-ref" href="https://www.grulic.org.ar/~mdione/glob/posts/implementing-selenium-with-python-and-qt/#fn:4"&gt;4&lt;/a&gt;&lt;/sup&gt;, so you
can't access the properties via its API, but by executing JS&lt;sup id="fnref:5"&gt;&lt;a class="footnote-ref" href="https://www.grulic.org.ar/~mdione/glob/posts/implementing-selenium-with-python-and-qt/#fn:5"&gt;5&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;e = DOMRoot.findFisrt('[name="foo"]')
e.evaluateJavaScript("this.value = 'abracadabra'")
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Tonight I finished fixing the most annoying bug I had with this site. To add a
user I have to fill a form that is split in 7 'tabs' (which means 7 &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;s
with fields where only one is shown at a time). One of the fields on the second
tab has a complex
JS interaction and I was cracking my skull trying to make it work. Because the JS
is reacting to key presses, setting the &lt;code&gt;value&lt;/code&gt; property was not triggering it.
Next I tried
&lt;a href="https://stackoverflow.com/questions/961532/firing-a-keyboard-event-in-javascript"&gt;firing a &lt;code&gt;KeyboardEvent&lt;/code&gt; in JS&lt;/a&gt;,
but I didn't succeed. Maybe it was me, maybe the fact that the engine behind
&lt;code&gt;QWebPage&lt;/code&gt; is the original Webkit and for some reason its JS support is lacking
there, who knows.&lt;/p&gt;
&lt;p&gt;But the good guys from &lt;code&gt;#qtwebkit&lt;/code&gt; gave me a third option: just send plain
&lt;code&gt;QKeyEvent&lt;/code&gt;s to the input element. Luckily we can do that, the web engine is
completely built in Qt and supports its event system and more. I only had to
give focus to the widget.&lt;/p&gt;
&lt;p&gt;Again, I tried with JS and failed&lt;sup id="fnref:7"&gt;&lt;a class="footnote-ref" href="https://www.grulic.org.ar/~mdione/glob/posts/implementing-selenium-with-python-and-qt/#fn:7"&gt;7&lt;/a&gt;&lt;/sup&gt;, so I went back cheating with Qt
behind curtains. &lt;code&gt;QWebElemnt.geometry()&lt;/code&gt; returns the &lt;code&gt;QRect&lt;/code&gt; of the &lt;code&gt;QWidget&lt;/code&gt;
that implements the input element; I just took the &lt;code&gt;.center()&lt;/code&gt; of it, and
generated a pair of mouse button press/release events in that point. One further
detail is that the &lt;code&gt;.geometry()&lt;/code&gt; won't be right unless I force the second tab to
be shown, forcing the field to be drawn. Still, for some reason getting a
reference to the input field on
page load (when I'm trying to figure out which fields are available, which in
the long run does not make sense, as fields could easily be created or destroyed
on demand with
JS) does not return an object that will be updated after the widget is
repositioned, so asking its geometry returns &lt;code&gt;((0, -1), (-1, 0))&lt;/code&gt;, which amounts to
an invalid geometry. The solution is to just get the reference to the input field
after forcing the div/tab to be shown.&lt;/p&gt;
&lt;p&gt;Finally, I create a
pair of key press/release events for each character of the string I wanted as
value, and seasoned everything with a lot of &lt;code&gt;QMainLoop.processEvents()&lt;/code&gt;.
Another advantage of using the Qt stuff is that while I was testing I could plug
a &lt;code&gt;QWebView&lt;/code&gt;, sprinkle some &lt;code&gt;time.sleep()&lt;/code&gt; of various lengths, and see how it
behaved. Now I can simply remove that to be back to headlessness.&lt;/p&gt;
&lt;p&gt;I'm not sure I'll publish the code; as you can see, it's quite hacky and it will
require a lot of cleanup to be able to publish it without a brown paper bag in
my head.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Yes, I'm using qt5.5 because that's what I will have available in the
  production server. &lt;a class="footnote-backref" href="https://www.grulic.org.ar/~mdione/glob/posts/implementing-selenium-with-python-and-qt/#fnref:1" title="Jump back to footnote 1 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Although as I said, you can change the attributes and so you lose the
  original values. &lt;a class="footnote-backref" href="https://www.grulic.org.ar/~mdione/glob/posts/implementing-selenium-with-python-and-qt/#fnref:2" title="Jump back to footnote 2 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;I guess the answer is in in the spec. &lt;a class="footnote-backref" href="https://www.grulic.org.ar/~mdione/glob/posts/implementing-selenium-with-python-and-qt/#fnref:3" title="Jump back to footnote 3 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;I think i got it: &lt;code&gt;QWebElement&lt;/code&gt; is the C++ class that is used in
  WebKit to represent the HTML tree, the real DOM, while somewhere deeper in
there are the classes representing the JS objects which you just can't reach&lt;sup id="fnref:6"&gt;&lt;a class="footnote-ref" href="https://www.grulic.org.ar/~mdione/glob/posts/implementing-selenium-with-python-and-qt/#fn:6"&gt;6&lt;/a&gt;&lt;/sup&gt;:. &lt;a class="footnote-backref" href="https://www.grulic.org.ar/~mdione/glob/posts/implementing-selenium-with-python-and-qt/#fnref:4" title="Jump back to footnote 4 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:5"&gt;
&lt;p&gt;This clearly shows that &lt;em&gt;there is&lt;/em&gt; a connection between the DOM object and
  the JS one, you just can't access it via the API. &lt;a class="footnote-backref" href="https://www.grulic.org.ar/~mdione/glob/posts/implementing-selenium-with-python-and-qt/#fnref:5" title="Jump back to footnote 5 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:6"&gt;
&lt;p&gt;This is the original footnote: Or something like that. Look, I'm an engineer
  and I usually want to know how things work, but since
  my first exposure to HTML, CSS and JS, back in the time when support was flaky
  and fragmented on purpose,
  I always wanted to stay as far away from them as possible. Things got much
  better, but as you can see the details are still somewhat obscure. I guess, I
  hope the answer is in the spec. &lt;a class="footnote-backref" href="https://www.grulic.org.ar/~mdione/glob/posts/implementing-selenium-with-python-and-qt/#fnref:6" title="Jump back to footnote 6 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:7"&gt;
&lt;p&gt;With this I mean that I executed something and it didn't trigger the events
  it should, and there's no practical way to figure out why. &lt;a class="footnote-backref" href="https://www.grulic.org.ar/~mdione/glob/posts/implementing-selenium-with-python-and-qt/#fnref:7" title="Jump back to footnote 7 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>automation</category><category>dom</category><category>javascript</category><category>pyqt</category><category>python</category><guid>https://www.grulic.org.ar/~mdione/glob/posts/implementing-selenium-with-python-and-qt/</guid><pubDate>Tue, 24 Jan 2017 19:25:26 GMT</pubDate></item><item><title>Trip planner</title><link>https://www.grulic.org.ar/~mdione/glob/posts/trip-planner/</link><dc:creator>Marcos Dione</dc:creator><description>&lt;p&gt;For a long time I've been searching for a program that would allow me to plan (car) trips with my friends. Yes, I know of the
existence of Google Maps, but the service has several characteristics that doesn't make it appealing to me, and lacks a couple of
features I expect. This is more or less the list of things I want:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Define the list of points I want to go to. No-brainer.&lt;/li&gt;
&lt;li&gt;Define the specific route I want to take. This is normally implemented by adding more control points, but normally
  they're of the same category as the waypoins of the places you want to visit. I think they shouldn't.&lt;/li&gt;
&lt;li&gt;Define stages; for instance, one stage per day.&lt;/li&gt;
&lt;li&gt;Get the distance and time of each stage; this is important when visiting several cities, for having an idea of how much time
  during the day you'll spend going to the next one.&lt;/li&gt;
&lt;li&gt;Define alternative routes, just in case you don't really have/make the time to visit some points.&lt;/li&gt;
&lt;li&gt;Store the trips in cookies, share them via a URL or central site, but that anybody can easily install in their own server.&lt;/li&gt;
&lt;li&gt;Manage several trips at the same time.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So I sat down to try and create such a thing. Currently is just a mashup of several things GIS:
&lt;a href="http://www.grulic.org.ar/~mdione/glob/tags/elevation/"&gt;my own OSM data rendering&lt;/a&gt;, my own
&lt;a href="http://www.grulic.org.ar/~mdione/glob/posts/local-markers-in-cookies-with-leaflet/"&gt;waypoints-in-cookies idea&lt;/a&gt; (in fact, this is
the expansion of what fired that post) and &lt;a href="http://project-osrm.org/"&gt;OSRM&lt;/a&gt; for the routing. As for the backend, I decided to try
&lt;a href="http://flask.pocoo.org/"&gt;flask&lt;/a&gt; and &lt;a href="http://flask-restful.readthedocs.org/"&gt;flask-restful&lt;/a&gt; for creating a small REST API for
storing all this. So far some basics work (points #1 and #6, partially), and I had some fun during the last week learning RESTful,
some more Javascript (including &lt;a href="http://leafletjs.com/"&gt;LeafLet&lt;/a&gt; and some &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt;) and putting all this
together. Here are some interesting things I found out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RESTful is properly defined, but not for all URL/method pairs. In particular, given that I decided that trip ids are their name,
  I defined a POST to trips/&lt;name&gt; as the
  &lt;a href="https://github.com/StyXman/elevation/blob/166fe89349fce1c3e6dd5a99d3f6d6884fb1811f/Elevation/flask/api.py#L29"&gt;UPSERT&lt;/a&gt; for that
  name. I hope &lt;a href="http://www.sqlalchemy.org/"&gt;SQLAlchemy&lt;/a&gt; &lt;a href="https://bitbucket.org/zzzeek/sqlalchemy/issues?q=upsert"&gt;implements it soon&lt;/a&gt;.&lt;/name&gt;&lt;/li&gt;
&lt;li&gt;Most of the magic of RESTful APIs happen in the model of your service.&lt;/li&gt;
&lt;li&gt;Creating APIs with flask-restful could not be more obvious.&lt;/li&gt;
&lt;li&gt;I still have to get my head around Javascript's &lt;a href="http://www.w3schools.com/js/js_object_prototypes.asp"&gt;prototypes&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Mouse/finger events &lt;a href="https://www.youtube.com/watch?v=wwffqMAS8K8"&gt;are a nightmare in browsers&lt;/a&gt;. In particular, with current
  leafLet, you get &lt;code&gt;clicked&lt;/code&gt; events on double clicks, unless you use the appropriate &lt;code&gt;singleclick&lt;/code&gt; plugin from
  &lt;a href="http://leafletjs.com/plugins.html#events"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Given &lt;a href="https://en.wikipedia.org/wiki/Cross-site_scripting"&gt;XSS&lt;/a&gt; attacks,
  &lt;a href="https://en.wikipedia.org/wiki/Same-origin_policy"&gt;same-origin policy&lt;/a&gt; is enforced for AJAX requests. If you control the
  web service, the easiest way to go around it is &lt;a href="https://en.wikipedia.org/wiki/Cross-origin_resource_sharing"&gt;CORS&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The only way to do such calls with jQuery is using the low level function &lt;a href="http://api.jquery.com/jQuery.ajax/"&gt;&lt;code&gt;$.ajax()&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;jQuery provides a &lt;a href="http://api.jquery.com/jQuery.parseJSON/"&gt;function to parse JSON&lt;/a&gt; but not to serialize to it; use
  &lt;code&gt;window.JSON.stringify()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Javascript's &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters"&gt;default parameters&lt;/a&gt;
  were not recognized by my browser :(.&lt;/li&gt;
&lt;li&gt;OSRM's &lt;a href="https://github.com/Project-OSRM/osrm-backend/wiki/Server-api#service-viaroute"&gt;&lt;code&gt;viaroute&lt;/code&gt;&lt;/a&gt; returns the coordinates
  multiplied by 10 for precision reasons, so you have to
  &lt;a href="https://github.com/StyXman/elevation/blob/166fe89349fce1c3e6dd5a99d3f6d6884fb1811f/Elevation/Save.js#L112"&gt;scale it down&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://wiki.openstreetmap.org/wiki/Nominatim"&gt;Nominatim&lt;/a&gt; and OSRM rock!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I still have lots of things to learn and finish, so stay tunned for updates. Currently the code resides in
&lt;a href="https://github.com/StyXman/elevation"&gt;Elevation's code&lt;/a&gt;, but I'll split it in the future.&lt;/p&gt;
&lt;h2&gt;Update:&lt;/h2&gt;
&lt;p&gt;I have it running &lt;a href="http://grulicueva.homenet.org/~mdione/Elevation/"&gt;here&lt;/a&gt;. You can add waypoints by clicking in the map, delete
them by doublecliking them, save to cookies or the server (for the moment it overwrites what's there, as you can't name the trips
or manage several yet) and ask for the routing.&lt;/p&gt;</description><category>elevation</category><category>flask</category><category>javascript</category><category>jquery</category><category>leaflet</category><category>openstreetmap</category><category>osrm</category><category>python</category><category>trip-planner</category><guid>https://www.grulic.org.ar/~mdione/glob/posts/trip-planner/</guid><pubDate>Mon, 25 Jan 2016 14:52:06 GMT</pubDate></item></channel></rss>