missing dependency after running pyinstaller

Question:

I have a large application that was developed in pycharm IDE. From the IDE it can be debugged and run. The application can also (and is typically) launched from a terminal command line and run that way.

I need this application to be an executable, and I can successfully convert this python application to an executable that runs in Ubuntu, using the pyinstaller.

I use the following command to do this:

sudo pyinstaller App.py --onefile

When I run the executable, it comes up and performs all tasks as expected, except it doesn’t draw certain graphics (custom arcs and rectangles calculated by the App).

The console prints the following error:

"TypeError: Couldn't find foreign struct converter for 'cairo.Context'"

The issues seems to be something to do with the packages in the various environments.

From a terminal, I can confirm I have the python3-gi and python3-gi-cairo packages installed.

e.g.:

$sudo apt install python3-gi
Reading package lists... Done
Building dependency tree       
Reading state information... Done
python3-gi is already the newest version (3.36.0-1).
0 upgraded, 0 newly installed, 0 to remove and 34 not upgraded.

When I run App.py, from either the command line or the IDE, the graphics render as expected.

However, when building the executable, there are issues with the graphics.

I’ve tried building the executable without the –onefile and I get the same result, so it doesn’t involve the –onefile parameter.

I believe there is some context being setup through pycharm IDE, that isn’t getting imported or processed by pyinstaller.
I’ve noticed that no where in the source code do we use the word cairo, and when it is imported, the IDE greys it out because it isn’t referenced anywhere.
The GTK form is built with from an XML file constructed by Glade, and that xml file then gets pulled in using gtk_Builder(formfile.glade).

Below is a minimal example of just some graphics I’ve moved into a separate draw.py application to test with:

import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk as gtk
from math import pi

class App:
    def __init__(self):
        self.sw_ver = "0.1"

        try:
            self.builder = gtk.Builder()
            self.builder.add_from_file("testDraw.glade")
            self.plotRingGaugeArea = self.builder.get_object("testView")
            self.plotRingGaugeArea.connect('draw', self.on_draw_ring_gauge)

        except Exception as e:
            print("Exception can_processing_thread: " + str(e))

    def on_draw_ring_gauge(self, da, ctx):

        radius = 275
        gaugeWidth = 1180 / 4
        line_width = radius / 6
        start_angle = pi * 0.7
        pegged = 0.8

        try:
            percent_spd = 50
            spd_end_angle = start_angle + min(0.99, percent_spd) * (2 * pi) * pegged  # max angle at 80% for speed

            ctx.set_source_rgb(20, 20, 200)
            ctx.set_line_width(line_width)
            ctx.set_tolerance(0.1)
            ctx.arc(gaugeWidth,
                    radius,
                    radius * 0.9,
                    start_angle,
                    spd_end_angle)
            ctx.stroke()

            ctx.set_source_rgb(1, 1, 1)
            ctx.fill()

            while(1):
                a = 1000 #hang here...

        except Exception as e:
            print("Error - on_draw_ring_gauge part 1: " + str(e))

if __name__ == "__main__":
    try:
        app = App()
    except Exception as e:
        print(e)

However, this file doesn’t work as it stands, because it is missing packages. I’ve started adding some to the new minimal project, in pycharm, however when I review the list of packages installed in the real application, there is over 100 of them (some of which are probably not used as the development of the application evolved). I’m now in the process of adding packages to enable the minimal draw.py project…

Here is a minimal glade file:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.2 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkWindow" id="testWindow">
    <property name="can_focus">False</property>
    <child type="titlebar">
      <placeholder/>
    </child>
    <child>
      <object class="GtkFixed" id="testFixed">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <child>
          <object class="GtkViewport" id="testView">
            <property name="name">myViewport</property>
            <property name="width_request">500</property>
            <property name="height_request">500</property>
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <child>
              <placeholder/>
            </child>
          </object>
        </child>
      </object>
    </child>
  </object>
</interface>

The key question is: what do I need to do to get the pyinstaller to include everything it needs to run the App from a commandline (outside of the IDE and without the python source).

Asked By: Walt

||

Answers:

The issue turned out to be a python module wasn’t being imported in one of the python files. Added:

import cairo

solved the problem.
Why everything was working correctly from python without that import, I’m not sure, but with it, pyinstaller builds the executable correctly.

Answered By: Walt
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.