Switching to QML from QWidget using Qt and PyQt

This is high-level, loose assemblage of discussion and links.

Context: you have a cross-platform app written using Qt and PyQt.  It is written using QWidgets.  You want to switch from QWidgets to QML.

The discussion is not exclusive to PyQt.  Much of the discussion also applies to apps writtent in C++, but mentions PyQt also.

This might also have some relevance for apps written purely in QML/Qt Quick.

Why switch to QML from QWidget?

These are possible reasons:

  • QML uses native platform style whereas QWidget fails to have native look and feel on some platforms (iOS)
  • QML more strongly follows the ModelViewController paradigm
  • QML is declarative (more compact and dense)
  • a QML GUI is easier to change (to provide multiple or improved GUI’s for the same business model)

To me, the first reason is the most compelling.  The last reason is least compelling: you probably won’t be changing the GUI often (but then here we are talking about changing at least the implementation of a GUI.)

(Qt apps are excluded from the Windows Store because Qt did not support Windows8/WinRt but Qt5.4 and QML may remedy that?)

Model View Controller paradigm

Qt documentation: Model – View Options in Qt

Wiki: Model–view–controller

It is quite likely that your existing QWidget apps does not strictly adhere to the MVC paradigm.  Thus you will be probably need to refactor your code: separate model code from view code.

A strategy for  developing (code changes) to switch to QML from QWidget

Assuming your app using QWidgets already runs and has tests, a good strategy is to keep the QWidget implementation of the View working, while you develop the QML implementation of the View.   In other words, use test driven development: make small changes, then test the QWidget implementation AND the QML implementation.  The QWidget implementation should always remain fully functional after each change.  The QML implementation will also always work, but in a stubbed-out, minimal fashion.

Changing the existing QWidget app will largely entail:

  • splitting classes along model/view lines
  • converting procedure calls to signals, connections, and properties

Swapping in the QML implementation of the view will be just a small change in the startup code: creating QML instead of QWidgets.  (Its a little more complicated than that, you might be making connections in the QML instead of in the main app.)

Embedding QML in a QWidget app

You can implement the main app window (and all other GUI) in QML.  But you can also embed QML GUI inside a QWidget app.

Briefly, it entails using QQuickView.  But a QQuickView not is-a QWidget.  But you can wrap a QQuickView in a QWidget:  Qt documentation: Introducing QWidget::createWindowContainer() discusses how to wrap a QQuickView in a QWidget.

Why retain a QWidget shell embedding QML?  I suspect there are few reasons to do so.  One reason might be just the strategy discussed above; get your feet wet by embedding QML and make the last step converting the main window from QWidget to QML.

My reason was different: my app uses elaborate UI  picking of objects from a QGraphicsScene.  The QML scene graph doesn’t expose the same API for picking that the QGraphicsScene does.

More on the interface between model and view using QML

Qt documentation:

Where ‘from C++’  more generally means ‘from model objects’ .  And the discussion applies to model objects written in PyQt.

The interaction is two-way, bidirectional:

  •  Control (in MVC) is from the view to the model
  • View is from the model to the view

Your interaction may be one direction or both:

  • a weather app is just a viewer of the weather
  • a styling app is just a controller of a document

Much of the logic in the QML is not business logic, just GUI logic.  For example, a button may switch pages and have no effect on the business model.  The button ‘controls’ the view, but not the business model.

Not all the interaction need be between the model and the view.  For example, if you are embedding QML in a QWidget app, the QWidget app may need to simply activate (show, make visible) a QML view component.  In other words, the interactions may be between the part of the view implemented in QWidgets and the part of the view implemented in QML.

PyQt and QML

PyQt fully supports QML.  You can use Python and PyQt for your model.

PyQt documentation:  Integrating Python and QML

I have shared a hacky Github project where I learn and test PyQt and QML.

For the same reason you use QML for the view, you should consider Python for the model (more expressive in fewer lines of code.)

The QML tradeoff: more portable but less native?

Typically, a cross-platform solution makes certain compromises.  Sometimes its discussed using terms such as “least common denominator” or “adaption.”

I can’t fully discuss this here.  (And probably the set of compromises that have been made are not fully documented in one place.)  But you should consider whether you will be able to live with any such compromises.

For example, I found that a QML Dialog on a desktop (Linux, I haven’t tested OSX) opens in a separate window, not on top of (transient to) the main window.  That probably doesn’t meet the HIG’s for desktops.  Possibly there is a solution that I haven’t yet found.  Or possibly I should look at that as a feature to embrace: I should make my dialogs non-modal and treat them as tear-offs that should not obscure the main window anyway.  But its an example of the compromises you might need to make.

There is much more to discuss here.  Briefly, Qt Quick Controls does use native widgets where possible, then falls back to QWidget implementations if possible, and then to a pure-QML implementation.

But again, its hard to know in advance without trying it.

Structuring your QML source code

In many examples, you see long QML files.  I  think that is an artifact of being an example: put everything in one file for ease of reading.  You can structure you QML source files into smaller files in more directories, using imports.

Qt documentation: Import Statements | QtQml 5.3

Packaging your app

I don’t foresee any show stoppers here.  Just more to learn.  If you are already packaging a PyQt app, you probably are already packaging resources using pyrcc.  The QML is just another resource to package.

But, I’m not sure how third-party QML plugins get packaged?

A strange architecture: PyQt and QML uses two interpreters

A PyQt app using QML uses two interpreters: the Python interpreter and QML’s Javascript interpreter.  (And on Android, there is a third Java interpreter.)  There is much discussion about the safety of, and constraints applied by app stores, re interpreters.   But this is just a curiousity, there are already shipping apps, using interpreters, in the stores .

A template for QML using models

Qt’s example Weather App is a starting place for reading code: Cross-Platform Applications in iOS and Android Stores with Qt

(I hope to reduce it to a simple template.)

Desktop to Mobile GUI difference: sharing

The main problem that I have found is that for certain apps (document editing or creation apps as opposed to reading or browings apps) the desktop:

  • exposes the file system to the user (in the File menu)
  • uses drag and drop

whereas mobile platforms use the notion of ‘sharing’ to the file system and other apps

Notably lacking in Qt Quick Control is a ‘share sheet’.  A github project QtSharingKit seeks to remedy that, with a QML plugin.

Pragmatically, that means more work for you:

More links about QML, Qt Quick, and Quick Controls.

You use these technology pieces:

  • QML a language.
  • Qt Quick: the standard library of types and functionality for QML
  • Qt Quick Controls: canned (pre-built) QUI controls built using QML
  • QML Applications

Modifying the SMD footprint of a Fritzing part for hand soldering

You want to modify the SMD footprint (lands) of a Fritzing part because you found that the stock footprint is not large enough for convenient hand-soldering.  This blog is instructions for doing that.

Why standard SMD lands are inadequate for hand soldering

A land is a pad on a circuit board for an individual leg or tab of an SMD part.  The footprint is a set of lands.

Standard SMD footprints are designed for reflow soldering (apply solder paste to lands, set or glue part down, bake until solder melts and reflows.) Standard SMD footprints may be inadequate for hand soldering with an iron, even with a very fine tip.

In one case, the part has legs and the SMD lands are big enough to hold the legs, with a little extra for slop in placing the part, and for a meniscus of solder to form to the leg.  When hand-soldering, often that is not enough space for the tip of the soldering iron to touch both the land and the leg.  That is a goal when hand soldering: to touch and heat both sides at once.  It is not good to just touch the soldering iron to the leg and thereby heat the land through the leg (because then the part may get too hot before the land gets hot enough for solder to flow to the land.)   Some people put a ball of solder on the tip of their soldering iron and then touch the leg; then the ball may touch and heat up the land and make a good solder joint quickly.

In another case, the part has no legs, only J-shaped tabs that curl under the part.  There may not be enough room for a soldering iron tip to touch either the land or the J tab.

It is not uncommon for prototype and adaptor boards for SMD parts to have non-standard SMD footprints expressly designed for hand soldering.  They allow you to swipe a soldering iron across extended lands so that solder there melts and wicks or flows onto the legs or J-tabs.  Some Fritzing parts may already include such non-standard SMD footprints, but some don’t.

It is not uncommon for circuit boards to be pre-tinned, so that each land already has some solder on it, sometimes enough to make the solder joint, if only you could touch the land and leg concurrently with the soldering iron tip.

While very fine tips are available for soldering irons, they are problematic because a fine tip is colder than a larger tip (a fine tip loses more heat by radiation than a larger tip.)  Also it has less thermal mass and may not retain enough heat to transfer enough heat to the part and land quickly.  To a certain extent, you can get around this by setting your adjustable soldering iron to a higher temperature.

Anyway, you decide that you are not worried about the smallest possible circuit board but want extended lands for ease of hand soldering.  You want to redesign the stock SMD lands of a Fritzing part, enlarging the lands away from the part so you can touch it with a soldering iron.

Note that it is not enough to just use extra thick traces on the board, because the traces will be covered up by solder mask.

It might be possible to accomplish the same goal by adding an extra Core>PCB View> Pad part touching (and connected to) an SMD land.  That’s fine for one part instance, but it gets tedious if you have many part instances.  Then you want to modify the part class so that all instances have extended lands.

Use case variations

Case 1: you don’t care to keep the old footprint at all, you just want to modify the footprint of the existing package.  E.g. you think there is a flaw in the footprint of SOD-23[SMD], you want to fix the footprint but retain the name.

Case 2: you want to create a new named package with a footprint that slightly differs from an existing footprint of an existing package.  E.g. you want to create a ‘SOD-23[HandSMD]’ package by deriving from ‘SOD-23[SMD]’

 

Overview of the steps

  • Copy the part to a new name (Save as new part)
  • Edit the SVG for the footprint
  • Load the edited SVG into the copied part and reconnect connectors to SVG elements

Copy the part to a new name

Start Fritzing.

Open a new sketch.

Drag the part to any view of the sketch (you can’t open the Part Editor except on a part in the sketch.)  In this example, drag a “Parts > Core > NPN-transistor” to the sketch (it has a SMD SOT-23 footprint or package that we want to modify.)

Choose the “PCB” tab.  Expect the view to change to the PCB view.  You should see the “TO92[THT]” package for the part in the view (the through-hole package of the part.)

In the “Inspector” window, choose “Properties>package”.  Expect a pop-up menu to appear.

Choose the “SOT-23[SMD]” value from the pop-up menu.  (You must choose the SMD package so you can edit that package in the Part Editor.  Technically, the Part Editor edits a particular PCB package.)

Choose Part>Edit (new parts editor).   Expect a “Fritzing (New) Part Editor:” window to open.

Click on the “PCB” tab.  Expect the view to change to the PCB view.

Choose “File> Save as new part” from the Part Editor menubar.   Expect a “Filename prefix” dialog to open.

Enter a prefix.  For example “HandSMD”.  Choose “OK”.  Expect the dialog to close and nothing else visual to happen (there is no confirmation dialog telling you a new part was created.)  But at this point, Fritzing has created new files for you, e.g. ~/.config/Fritzing/parts/user/HandSMD_f55abc89e337286af8a90f4ceb6a8404_1.fzp and ~/.config/Fritzing/parts/svg/user/pcb/HandSMD_f55abc89e337286af8a90f4ceb6a8404_1_pcb.svg (and a few other SVG files, but we only want to modify the PCB SVG.  Note these paths are on Linux and may differ on other platforms.)

Choose the ‘Metadata’ tab of the Parts Editor.

Under ‘Properties>package’ change the name of the package, e.g. from SOT-23[SMD]’ to ‘SOT-23[HandSMD]’

This is use case 2: creating a new package.  If you fail to give a new package name, it is just use case 1: altering the footprint of an existing package.

Close the Part Editor.  Expect in the Fritzing main window to see your new part under “Parts>Mine.”

Note that the new part still looks the same as the old part, in all views (icon, breadboard, schematic, and PCB), until you edit the SVG for the footprint.   In this case, you will still see the icon for a NPN transistor in the parts window and in the Inspector window you will still see the old footprint.

Edit the SVG for the footprint

Start Inkscape.

Choose “File>Open”.  Expect a file chooser dialog to open.

Navigate to and choose the PCB SVG file that Fritzing just created.  (You may need to enter “Ctl-h” so that you can see the hidden “.config” directory in the file chooser dialog.)

I won’t discuss the details here, only the brief steps:

  • select and enlarge the yellow lands, away from the center (you may need to double click to select an element instead of its group)
  • resize the canvas to fit all the elements
  • save

You might also need to edit the silkscreen group, but often not (if you enlarge lands away from the centered silkscreen)

Note that if you fail to resize the canvas, when you use the part in Fritzing, the enlarged lands will be cut off.

Note that layers in SVG are not the same as groups.  Fritzing uses named SVG groups for Fritzing layers (e.g. “copper1”).  It is unfortunate that both apps use the same word “layers” for two different things.

Now switch to the Fritzing app (click in one of its windows).  Without having to restart it, the SMD footprint for the new part “Parts>MINE>NPN Transistor” will be updated and look like the edited SVG.  The part instance you already placed in the sketch will still have the old footprint.  But if you drag the new part into the sketch, it will have the new footprint.

More about use case 1: changing the SMD footprint of an existing package

Suppose you created a new part without giving it a new package name and edited the new par’ts footprint (PCB SVG file.)  That changes the footprint for all parts (but not existing part instances in sketches) that use the package called “SOT-23[SMD]” even though the .fzz files for the parts refer to the original SMD_SOT-23.svg file.  In other words, you have changed the SMD footprint for both the original part (Parts>Core>Basic>NPN transistor) and the new part (Parts>MINE>NPN transistor.)

To see that this is so, repeat the above but omitting the step of entering a new package name in the metadata.

Close Fritzing:

  • Choose ‘Save’ to ‘Do you want to save the changes you made in the document “UntitledSketch.fzz”‘
  • Choose ‘Save’ to ‘Do you want to save the changes you made in the bin “My Parts”.’

Restart Fritzing and open the sketch “UntitledSketch.fzz.”  The first part instance will have the old footprint in the PCB view, the second part will have the new, enlarged footprint.

Drag a new part instance “Parts>Core>Basic>NPN transistor” to the sketch.  It will have the new, enlarged footprint.

I can’t explain why this happens.  My guess would be that Fritzing maintains an internal database of packages.   When Fritzing starts, it reads in your custom parts and updates its internal database.  Possibly it uses the last encountered SVG for a package, e.g.  the package “SOT-23[SMD]” is associated with the last PCB SVG file (the altered one in your new part) that is encountered when reading the parts files into the internal database.  Many part classes may refer to that same package by its name/id.  Any part instances you place subsequently will use that SVG footprint, and retain it across Fritzing sessions (even if you later change the SVG again.)

Load the edited SVG into the copied part and reconnect connectors to SVG elements

This step is optional: at this point you can use the new part and it will have extended lands.  However, Fritzing still shows the center of the connectors at the old coordinates (slightly towards the top of the new extended lands.)  The part will usually still work, but wires (copper traces) to the lands won’t point to the center of the lands.  To fix that:

Drag the new part into a temporary sketch.

Select the new part instance.

Choose Part>Edit (new parts editor).   Expect a “Fritzing (New) Part Editor:” window to open.

Click on the “PCB” tab.  Expect the view to change to the PCB view.

Choose “Window>Connectors”.  Expect a new “Connectors” window to open.  In this case, connector “2” is already selected in that window and “Select graphic” text appears in that row.  In the PCB view, the associated land will be highlighted (in purple on my computer, with a dashed white outline.) (You might need to zoom into the PCB view so you can see the lands.)

Click on the highlighted land.  Fritzing will recalculate the center of the land and relocate the connector.

Repeat for the other two connectors (select in the Connector window, then click on a land in the PCB view.)

Save the part and close the Part Editor.

Understanding the Fritzing object model for connectors and connections

This is mostly about Fritzing’s object model for connectors and parts.  It might be pertinent to other EDA tools.  I wrote this quickly. It could be wrong.  It is not a complete object model, for example it doesn’t discuss packages.

Audience

– Fritzing users (and users of other EDA apps.)

– makers of Fritzing parts

– Fritzing developers

About Object Models

Object modeling is a computer programming subject.  Object modeling is math.  It helps you think about the world.  There is truth to it that derives from the real world.

In this blog, I use CamelCase for class names, and lower case for less formal terms.  As is typical in object modeling, I often blur the distinction between an instance of an object and a class of object.

References

This is also discussed  in the developer docs for Fritzing part files.  Where they note that the schema ( i.e. the object model) is not yet formally documented.

FritzingConnector

(Fritzing docs usually just say ‘connector’).

A FritzingConnector is an object where you can make a Connection.  In other words, when using Fritzing, something to which you can drag a wire, or which connects two parts when you  drop one onto the other such that their connectors align or overlap.

The most common FritzingConnector is usually just called a pin.  ‘Pin’ commonly means a needle-like metal piece extending from a part.  But more generally, a FritzingConnector can be:

  • an SMD pad,
  • a female socket
  • an end of a wire.
  • a side of a via ( a depth-dimension ‘wire’ crossing between copper layers.)

A Connector of a FritzingPart has an attribute ‘id.’

Connection

A Connection is an association between exactly two FritzingConnectors.  A FritzingConnector can be in many Connections.

FritzingPart

(Fritzing docs usually just say ‘part’).

A FritzingPart comprises a set of FritzingConnectors ( and other things not discussed here.)  A FritzingConnector instance is owned by exactly only FritzingPart.

Connectors and representations

Fritzing has three different representations of a FritzingPart.  (Fritzing shows breadboard, schematic, or PCB views, where a view is a set of representations of parts.)  In other words, a FritzingPart has three SVG image files, one for each view.  The SVG image file is the representation.

A Connector of a FritzingPart SHOULD appear in each representation (more precisely, there should be some SVG element of the representation that is associated with the Connector.)

When you are using Fritzing to make connections and click the mouse somewhere, the Fritzing app determines which representation (which colored area) you clicked in and whether that area is the representation of some Connector, and thus can be connected to.

When you are creating a Fritzing part using Fritzing app’s Part Editor, there is a GUI action (point-and-click) that associates each Connector with its representation.  Fritzing associates the Connector.id with the SVG attribute named ‘id’ of some SVG element of the SVG file.

Implementation details: Fritzing does this in the XML file that specifies the FritzingPart.  Under the XML ‘connector’ element having an attribute id with the value of the Connector.id e.g. ‘connector0’, it creates another XML element such as:

<p svgId="connector0pad" layer="copper0" />

where ‘connector0pad’ is the id of the SVG element that represents the Connector.

FritzingConnectPart

FritzingConnectPart is a specialized subclass of FritzingPart: it has internal Connections.  Commonly, people use ‘connector’ to mean an electro-mechanical plug, such as on both ends of a cable.  If you think about it, a plug on such a cable has FritzingConnectors:

  • the prongs on the pluggable, business end of the plug
  • terminals hidden inside the plug where the wires of the cable are soldered or crimped

Again, what most people call a connector comprises many formal FritzingConnectors (as defined here in the context of Fritzing.)

A breadboard is a FritzingConnectPart.  A breadboard consists of many female FritzingConnectors, with subsets of them internally connected.

An adaptor is a FritzingConnectPart.  An adaptor is a component that adapts the footprint of one part to another footprint.  In other words, it has two sets of FritzingConnectors, internally connected, where each set of FritzingConnectors has a different footprint.

Wire

A Wire is a FritzingConnectPart.  It has exactly two FritzingConnectors.  It has an internal connection between the Connectors.  You can drag and drop each of the FritzingConnectors onto another FritzingConnector (such as the pin of a part) to make a Connection.

TODO more discussion here about ratsnest wires, connecting two wire ends, bending a wire, etc.

Most people don’t think of a wire as a part, they think of  ‘parts’ as ready-made components instead of something you cut.  Also, wires as they appear in Fritzing views have dynamic representations; most people think of ‘parts’ as having a fixed representation between design sessions.

Via

A Via is a FritzingConnectPart.  It has exactly two Connectors.  It has an internal connection between the Connectors (like a Wire.)  Each of the Connectors are in different copper layers.  Thus a  via helps to make a connection (the informal one, a network) between copper layers.  TODO more discussion.

Parts called ‘Connections’

Fritzing has a group of parts under the label ‘Connection’.  These are FritzingConnectParts.