<?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 pyqt)</title><link>https://www.grulic.org.ar/~mdione/glob/</link><description></description><atom:link href="https://www.grulic.org.ar/~mdione/glob/categories/pyqt.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:04 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Collating, processing, managing, backing up and serving a gallery of a 350GiB, 60k picture collection</title><link>https://www.grulic.org.ar/~mdione/glob/posts/collating-processing-managing-backing-up-and-serving-a-gallery-of-a-350gib-60k-picture-collection/</link><dc:creator>Marcos Dione</dc:creator><description>&lt;p&gt;In the last
&lt;a href="https://en.osm.town/@mdione/112376851535336773"&gt;two&lt;/a&gt;
&lt;a href="https://en.osm.town/@mdione/112399328668312076"&gt;days&lt;/a&gt;
I have commented a little bit how I process and manage my photos.
I'm not a very avid photographer, I have like 350 gigabytes of photos, most of them are
yet not processed, around 60,000 of them.
So I will comment a little bit more how do I manage all that.&lt;/p&gt;
&lt;p&gt;I start with the camera, a 24Mpx camera, just a couple of lenses, nothing fancy.
Go out, take some pictures, come back home.&lt;/p&gt;
&lt;p&gt;I put the SD camera on my computer and I use my own software to import it.
The import process is not fancy, it just empties the SD card, checks every file for the
EXIF information, uses the date and time to create the filename, a sequence number if needed,
and puts them all in a single
&lt;code&gt;incoming&lt;/code&gt; directory where all the current unprocessed images are&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="https://www.grulic.org.ar/~mdione/glob/posts/collating-processing-managing-backing-up-and-serving-a-gallery-of-a-350gib-60k-picture-collection/#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Then I use this software I developed in &lt;code&gt;PyQt5&lt;/code&gt;.
It's very, very basic, but it's really quick, it's mostly keyboard based. It reads
the EXIF information and present some of the tags at the left of the screen; things like date,
time, size, orientation and then focal length, aperture, ISO and various other data I can
get from the images. It's mostly focused on my current camera and the previous one, both Nikons&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="https://www.grulic.org.ar/~mdione/glob/posts/collating-processing-managing-backing-up-and-serving-a-gallery-of-a-350gib-60k-picture-collection/#fn:2"&gt;2&lt;/a&gt;&lt;/sup&gt;.
The previous one was an N90, right now it's an N7200.
The image occupies most of the window, and the program is always in full screen.
At the bottom there's the filename and a couple of toggles.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://www.grulic.org.ar/~mdione/glob/images/filter.png"&gt;&lt;/p&gt;
&lt;p&gt;I can do several things with this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Go forwards, backwards, by one, by ten, by a hundred
and by a thousand, because that &lt;code&gt;incoming&lt;/code&gt; directory right now has almost seven years of history,
probably ten thousand pictures.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Move randomly, which allows me to pick up a new thing to collate when I get
bored with the current one but I want to keep doing it to reduce the backlog.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Mark the images in different ways. The main ones are about selecting for storing, with two modes:
One is to keep the image in the original size. I usually use this for my best landscape or astro photos. The other one will
resize it down to twelve megapixels&lt;sup id="fnref:3"&gt;&lt;a class="footnote-ref" href="https://www.grulic.org.ar/~mdione/glob/posts/collating-processing-managing-backing-up-and-serving-a-gallery-of-a-350gib-60k-picture-collection/#fn:3"&gt;3&lt;/a&gt;&lt;/sup&gt;, from 6000x4000 pixels to 4500x3000 pixels, 75% on each dimension.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;Rotate the images, just in case the camera did not guess the orientation
correctly, usually when I'm taking pictures right upward or right downwards.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select several pictures for stitching, which will use
&lt;a href="https://hugin.sourceforge.io/"&gt;&lt;code&gt;hugin&lt;/code&gt;&lt;/a&gt; to do so. It's not 100% automatic, but at least puts the pictures in a &lt;code&gt;stitch&lt;/code&gt;
directory and point &lt;code&gt;hugin&lt;/code&gt; there.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select a picture for cropping or editing; I'm not going
to develop a whole image editor, so I just delegate to an existing program, &lt;a href="https://apps.kde.org/gwenview/"&gt;&lt;code&gt;gwenview&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select images for deleting and delete them permanently.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select several images for comparison and enter/exit comparison mode,
which means that going backwards and forwards applies only this this set.
This is good for things like when you take certain pictures, but there are not necessarily
sequences in the original picture sequence, which for me makes culling images faster.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;It has two zoom levels, fit to screen and full size.
I don't have much the need for other options.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;99% of the pictures I take are freehand,
so in a sequence there's always some movement between images. In full size I can put every image on its own position,
aligning the whole sequence and allow culling based on blurriness or other factors.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Also in full size, I can lock the view, so when I pan one of the images
and I switch to another one, it will also pan that second image to that position.
It also helps when I'm checking for details between two different images of the same thing.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Move all the selected images, resize them if needed, and put them in a folder. It also creates a hardlink
between my categorization in folders into a folder that collects all the images by date; there's one folder
for each month and year with all the pictures of that month inside.
It uses hardlinks so it doesn't duplicate the image file, saving space.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It also has a readonly mode, so I can hand the computer to my kids to watch the photos.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When culling, I use the comparison mode and individual position and lock view features a lot,
going back and forth between images, discarding until only one is left.&lt;/p&gt;
&lt;p&gt;That's the first part, the one I must spend my time on, just basic culling, selection and storage.
My main tree is just a tree based on my way of categorizing the images.&lt;/p&gt;
&lt;p&gt;My program doesn't have a directory view; instead, I just use &lt;code&gt;gwenview&lt;/code&gt; again.&lt;/p&gt;
&lt;p&gt;Notice there's no photo editing in this workflow. I rarely shoot in RAW for two reasons: a) I'm really bad at
postprocessing; and b) even if I was good, I don't have the time to do it; my free time is shared among several
hobbies. I only do it for astro photograpy and very few, rare occasions.&lt;/p&gt;
&lt;p&gt;The third tool I use is &lt;a href="https://www.digikam.org/"&gt;&lt;code&gt;digikam&lt;/code&gt;&lt;/a&gt;. I use it for two things, which are related:
semi-automatic and manual tagging.
The semi-automatic is face detection; &lt;code&gt;digikam&lt;/code&gt; can find and guess faces, but requires manual confirmation&lt;sup id="fnref:4"&gt;&lt;a class="footnote-ref" href="https://www.grulic.org.ar/~mdione/glob/posts/collating-processing-managing-backing-up-and-serving-a-gallery-of-a-350gib-60k-picture-collection/#fn:4"&gt;4&lt;/a&gt;&lt;/sup&gt;.
The fully manual part is plain tagging, mostly with location&lt;sup id="fnref:5"&gt;&lt;a class="footnote-ref" href="https://www.grulic.org.ar/~mdione/glob/posts/collating-processing-managing-backing-up-and-serving-a-gallery-of-a-350gib-60k-picture-collection/#fn:5"&gt;5&lt;/a&gt;&lt;/sup&gt; and sometimes some other info.
I sometimes also rate my pictures; I mostly use four and five, sometimes three, only for my best pictures.&lt;/p&gt;
&lt;p&gt;Then there's another script that reads the &lt;code&gt;digikam&lt;/code&gt; database and uses
the tags to create another directory for the tags, which also uses hardlinks.
It still doesn't do anything about the rating, but I could easily add that.&lt;/p&gt;
&lt;p&gt;That's all on my personal computer. I use &lt;code&gt;rsync&lt;/code&gt; to make a copy on my home server that has two purposes.
One, it's a backup, which includes all the original 24Mpx images that I hadn't culled yet,
which I think is the biggest part of my collection.&lt;/p&gt;
&lt;p&gt;The second one, it feeds a
&lt;a href="https://www.files.gallery/"&gt;gallery program&lt;/a&gt; that is developed in PHP by a guy named Karl. It's probably
the single paid software I use. It's a single PHP file that you put at the root of your gallery, you enable
PHP processing by your web server (in my case, Apache), and generates the gallery on the run,
just reading the directories and creating all the necessary thumbnails and all that.
I did a small change to this program. The original algorithm creates thumbnails based on each file's path
(and other attributes, 4 or 5 I think), but because I have all these hard links, it creates duplicated
thumbnail files. So I changed it to use the filename instead of the filepath&lt;sup id="fnref:6"&gt;&lt;a class="footnote-ref" href="https://www.grulic.org.ar/~mdione/glob/posts/collating-processing-managing-backing-up-and-serving-a-gallery-of-a-350gib-60k-picture-collection/#fn:6"&gt;6&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;I don't have any kind of synchronization with my phone.
Most of the pictures I take with it are not the kind of pictures I usually will put in my own gallery, except
the times I go out without my camera and I end up taking pictures anyway.
I still don't have a workflow for that, it's mostly manual.
So if I ever lose my phone, I'm fscked because I have definitely no backups of it.&lt;/p&gt;
&lt;p&gt;That lack of synchronization also means that the only way to see the pictures in my phone is by opening the gallery
in the browser. It's not the best, but I don't do that that often.
I have tried to use alternatives like NextCloud, which I also have installed on my home server.
I have some issues with permissions because, again, this is a backup directory, so it has
all the owner information that belongs to me, instead of the web server.
That means it doesn't have the proper permissions to let NextCloud manage
those files. Luckily &lt;code&gt;files.gallery&lt;/code&gt; just needs a subdirectory.&lt;/p&gt;
&lt;p&gt;Another reason is that before I was using static gallery generators: &lt;code&gt;sigal&lt;/code&gt;, &lt;code&gt;gallerpy&lt;/code&gt; or even &lt;code&gt;nikola&lt;/code&gt;, which
drives this glob. All those can generate the gallery statically, so serving them is so much easier.
My old home server died at some point and I had to come up with something.
I had a spare old laptop laying around and I used that. Now it's enough to generate the gallery on the fly.
I have plans to make something bigger, but that's for another time.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;In fact I have another directory for all the unprocessed photos from another era, and I'm thinking of starting a
  new era. &lt;a class="footnote-backref" href="https://www.grulic.org.ar/~mdione/glob/posts/collating-processing-managing-backing-up-and-serving-a-gallery-of-a-350gib-60k-picture-collection/#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;Even if EXIV is a standard for &lt;em&gt;storing&lt;/em&gt; tags, there's no standard for the tag &lt;em&gt;names&lt;/em&gt;, so every
  manufacturer has its own sets, that even change between camera lines. For a better idea of what I'm talking about,
  just peruse &lt;a href="https://github.com/exiftool/exiftool/tree/master/lib/Image/ExifTool"&gt;&lt;code&gt;Image::ExifTool&lt;/code&gt;'s source code&lt;/a&gt;. &lt;a class="footnote-backref" href="https://www.grulic.org.ar/~mdione/glob/posts/collating-processing-managing-backing-up-and-serving-a-gallery-of-a-350gib-60k-picture-collection/#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 currently own no screen that is 4500 pixels of width, let alone 6000. Maybe my kids will, but by then
  Mpx count will be so different that it won't make any sense to accomodate that. Right now storage for me is
  expensive, so I'll keep it this way. &lt;a class="footnote-backref" href="https://www.grulic.org.ar/~mdione/glob/posts/collating-processing-managing-backing-up-and-serving-a-gallery-of-a-350gib-60k-picture-collection/#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;Or rejection: the false positive rate is bigger that I would like, and it doesn't have a way to say 'yes, this is
  that person, but don't train on this image'. This is the case for pictures where the face is either semi occluded,
  sometimes painted, sometimes bad lightning, and mostly just blurry. &lt;a class="footnote-backref" href="https://www.grulic.org.ar/~mdione/glob/posts/collating-processing-managing-backing-up-and-serving-a-gallery-of-a-350gib-60k-picture-collection/#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;Most of my pictures don't have GPS info, not even the ones in the phone. The latter I only enable when I really
  need the info later, mostly for mapping. Later I either discard the photo or remove the info. &lt;a class="footnote-backref" href="https://www.grulic.org.ar/~mdione/glob/posts/collating-processing-managing-backing-up-and-serving-a-gallery-of-a-350gib-60k-picture-collection/#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;For a while now I'm even making this distinction in my own code, filename vs filepath. &lt;a class="footnote-backref" href="https://www.grulic.org.ar/~mdione/glob/posts/collating-processing-managing-backing-up-and-serving-a-gallery-of-a-350gib-60k-picture-collection/#fnref:6" title="Jump back to footnote 6 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>backup</category><category>gallery</category><category>nextcloud</category><category>photography</category><category>php</category><category>pyqt</category><category>python</category><category>qt</category><guid>https://www.grulic.org.ar/~mdione/glob/posts/collating-processing-managing-backing-up-and-serving-a-gallery-of-a-350gib-60k-picture-collection/</guid><pubDate>Tue, 07 May 2024 11:06:03 GMT</pubDate></item><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></channel></rss>