title: Pyinstaller misses modules for packaging an OpenGL application
body: <p>I am writing a PyQt5 application that uses OpenGL to slice an STL into images and displays them as it goes. The application works fine when invoked from the command line (Windows 10, Python 3.7.1, PyInstaller 3.5), but when I try packaging it with <code>pyinstaller</code> it crashes with an error: </p>
  
  <pre><code>Traceback (most recent call last):
    File "app_qt.py", line 45, in initializeGL
      self.gl = self.context().versionFunctions()
  ModuleNotFoundError: No module named 'PyQt5._QOpenGLFunctions_4_1_Core'`
  </code></pre>
  
  <p>The call comes from a <code>QtGui.QOpenGLWindow</code> object. I have followed the suggestions in <a href="https://stackoverflow.com/questions/48596114/pyqt5-gui-exe-made-with-pyinstaller-doesnt-open">this</a> answer to no avail. I have tried importing PyQt5 directly, and adding it to the hidden imports in the <code>.spec</code> file as well for pyinstaller. </p>
  
  <p>Since the application runs fine when invoked normally (i.e. <code>python slicer_gui.py</code>) I am inclined to believe <code>pyinstaller</code> is neglecting to add an import to the package somewhere. </p>
  
  <p>Here is the full code. The GUI is simple (pun intended):  </p>
  
  <pre><code>import PySimpleGUI as sg
  import app_pyopengl
  
  # define GUI layout
  layout = [
          [sg.Text('STL to Slice', size=(16, 1)), sg.Input(), sg.FileBrowse()],
          [sg.Text('Layer Thickness (um) ', size=(16, 1)), sg.InputText('10')],
          [sg.Submit(button_text="Slice"), sg.Cancel(button_text='Quit')]
  ]
  
  # name the window
  window = sg.Window('PyQT STL Slicer').Layout(layout)
  
  # loop until user quits
  while True:
      button, values = window.Read()              # read all values in the window
      if button == "Quit":
          exit()
      if button == "Slice":
          thickness = float(values[1]) / 1000     # convert um to mm
          filename = values[0]
          app_pyopengl.main(filename, thickness)
  
  </code></pre>
  
  <p>Here is the slicing application: </p>
  
  <pre><code>import sys
  import os
  import shutil
  from ctypes import c_float, c_uint, sizeof
  from PyQt5 import QtGui, QtCore, QtWidgets
  from stl import mesh
  import numpy as np
  
  from printer import printer
  
  GLfloat = c_float
  GLuint = c_uint
  
  EPSILON = 0.00001
  SCR_WIDTH = 640
  SCR_HEIGHT = int(SCR_WIDTH * printer.height / printer.width)
  
  
  class Window(QtGui.QOpenGLWindow):
  
      def __init__(self,
                   stlFilename,
                   layerThickness,
                   sliceSavePath,
                   *args,
                   **kwargs):
  
          super().__init__(*args, **kwargs)
  
          self.setTitle('STL Slicer')
  
          self.vertVAO, self.vertVBO = 0, 0
          self.maskVAO, self.maskVBO = 0, 0
          self.numOfVerts = 0
          self.bounds = dict()
          self.totalThickness = 0.
          self.currentLayer = 0
          self.height = 0
  
          self.stlFilename = stlFilename
          self.layerThickness = layerThickness
          self.sliceSavePath = sliceSavePath
  
      def initializeGL(self):
          self.gl = self.context().versionFunctions()
  
          self.shaderProg = QtGui.QOpenGLShaderProgram()
          self.shaderProg.create()
          self.shaderProg.addShaderFromSourceFile(
              QtGui.QOpenGLShader.Vertex, 'shaders/slice.vert')
          self.shaderProg.addShaderFromSourceFile(
              QtGui.QOpenGLShader.Fragment, 'shaders/slice.frag')
          self.shaderProg.link()
  
          self.loadMesh()
  
          self.proj = QtGui.QMatrix4x4()
          self.proj.setToIdentity()
          self.proj.ortho(0, printer.width*printer.pixel,
                          0, printer.height*printer.pixel,
                          -self.totalThickness, self.totalThickness)
  
          self.model = QtGui.QMatrix4x4()
          self.model.setToIdentity()
          self.model.translate(0, 0, self.totalThickness+EPSILON)
  
          self.sliceFbo = QtGui.QOpenGLFramebufferObject(
              printer.width,
              printer.height
          )
          self.sliceFbo.setAttachment(
              QtGui.QOpenGLFramebufferObject.CombinedDepthStencil
          )
  
      def loadMesh(self):
          # Get information about our mesh
          ourMesh = mesh.Mesh.from_file(self.stlFilename)
          self.numOfVerts = ourMesh.vectors.shape[0] * 3
          self.bounds = {
              'xmin': np.min(ourMesh.vectors[:,:,0]),
              'xmax': np.max(ourMesh.vectors[:,:,0]),
              'ymin': np.min(ourMesh.vectors[:,:,1]),
              'ymax': np.max(ourMesh.vectors[:,:,1]),
              'zmin': np.min(ourMesh.vectors[:,:,2]),
              'zmax': np.max(ourMesh.vectors[:,:,2])
          }
          self.totalThickness = self.bounds['zmax'] - self.bounds['zmin']
  
          #######################################
          # make VAO for drawing our mesh
          self.vertVAO = QtGui.QOpenGLVertexArrayObject()
          self.vertVAO.create()
          self.vertVAO.bind()
  
          self.vertVBO = QtGui.QOpenGLBuffer(QtGui.QOpenGLBuffer.VertexBuffer)
          self.vertVBO.create()
          self.vertVBO.bind()
          self.vertVBO.setUsagePattern(QtGui.QOpenGLBuffer.StaticDraw)
          data = ourMesh.vectors.astype(GLfloat).tostring()
          self.vertVBO.allocate(data, len(data))
          self.gl.glVertexAttribPointer(0, 3, self.gl.GL_FLOAT,
              self.gl.GL_FALSE, 3*sizeof(GLfloat), 0)
          self.gl.glEnableVertexAttribArray(0)
  
          self.vertVBO.release()
          self.vertVAO.release()
          #######################################
  
          # a mask vertex array for stencil buffer to subtract
          maskVert = np.array(
              [[0, 0, 0],
               [printer.width*printer.pixel, 0, 0],
               [printer.width*printer.pixel, printer.height*printer.pixel, 0],
  
               [0, 0, 0],
               [printer.width*printer.pixel, printer.height*printer.pixel, 0],
               [0, printer.height*printer.pixel, 0]], dtype=GLfloat
          )
  
          #######################################
          # make VAO for drawing mask
          self.maskVAO = QtGui.QOpenGLVertexArrayObject()
          self.maskVAO.create()
          self.maskVAO.bind()
  
          self.maskVBO = QtGui.QOpenGLBuffer(QtGui.QOpenGLBuffer.VertexBuffer)
          self.maskVBO.create()
          self.maskVBO.bind()
          self.maskVBO.setUsagePattern(QtGui.QOpenGLBuffer.StaticDraw)
          data = maskVert.tostring()
          self.maskVBO.allocate(data, len(data))
          self.gl.glVertexAttribPointer(0, 3, self.gl.GL_FLOAT,
              self.gl.GL_FALSE, 3*sizeof(GLfloat), 0)
          self.gl.glEnableVertexAttribArray(0)
  
          self.maskVBO.release()
          self.maskVAO.release()
          #######################################
  
      def paintGL(self):
          if self.height &gt;= self.totalThickness-EPSILON:
              sys.exit()
          else:
              self.height += self.layerThickness
              self.currentLayer += 1
              self.draw()
              self.renderSlice()
              self.update()
  
      def draw(self):
          self.gl.glViewport(0, 0, self.size().width(), self.size().height())
          self.gl.glEnable(self.gl.GL_STENCIL_TEST)
          self.gl.glClearColor(0., 0., 0., 1.)
          self.gl.glClear(self.gl.GL_COLOR_BUFFER_BIT | self.gl.GL_STENCIL_BUFFER_BIT)
          self.vertVAO.bind()
          self.shaderProg.bind()
  
          self.model.translate(0, 0, -self.layerThickness)
          self.shaderProg.setUniformValue('proj', self.proj)
          self.shaderProg.setUniformValue('model', self.model)
  
          self.gl.glEnable(self.gl.GL_CULL_FACE)
          self.gl.glCullFace(self.gl.GL_FRONT)
          self.gl.glStencilFunc(self.gl.GL_ALWAYS, 0, 0xFF)
          self.gl.glStencilOp(self.gl.GL_KEEP, self.gl.GL_KEEP, self.gl.GL_INCR)
          self.gl.glDrawArrays(self.gl.GL_TRIANGLES, 0, self.numOfVerts)
  
          self.gl.glCullFace(self.gl.GL_BACK)
          self.gl.glStencilOp(self.gl.GL_KEEP, self.gl.GL_KEEP, self.gl.GL_DECR)
          self.gl.glDrawArrays(self.gl.GL_TRIANGLES, 0, self.numOfVerts)
          self.gl.glDisable(self.gl.GL_CULL_FACE)
  
          self.gl.glClear(self.gl.GL_COLOR_BUFFER_BIT)
          self.maskVAO.bind()
          self.gl.glStencilFunc(self.gl.GL_NOTEQUAL, 0, 0xFF)
          self.gl.glStencilOp(self.gl.GL_KEEP, self.gl.GL_KEEP, self.gl.GL_KEEP)
          self.gl.glDrawArrays(self.gl.GL_TRIANGLES, 0, 6)
          self.gl.glDisable(self.gl.GL_STENCIL_TEST)
          self.shaderProg.release()
  
      def renderSlice(self):
          self.sliceFbo.bind()
          self.gl.glViewport(0, 0, printer.width, printer.height)
          self.gl.glEnable(self.gl.GL_STENCIL_TEST)
          self.gl.glClearColor(0., 0., 0., 1.)
          self.gl.glClear(self.gl.GL_COLOR_BUFFER_BIT | self.gl.GL_STENCIL_BUFFER_BIT)
          self.vertVAO.bind()
          self.shaderProg.bind()
  
          self.shaderProg.setUniformValue('proj', self.proj)
          self.shaderProg.setUniformValue('model', self.model)
  
          self.gl.glEnable(self.gl.GL_CULL_FACE)
          self.gl.glCullFace(self.gl.GL_FRONT)
          self.gl.glStencilFunc(self.gl.GL_ALWAYS, 0, 0xFF)
          self.gl.glStencilOp(self.gl.GL_KEEP, self.gl.GL_KEEP, self.gl.GL_INCR)
          self.gl.glDrawArrays(self.gl.GL_TRIANGLES, 0, self.numOfVerts)
  
          self.gl.glCullFace(self.gl.GL_BACK)
          self.gl.glStencilOp(self.gl.GL_KEEP, self.gl.GL_KEEP, self.gl.GL_DECR)
          self.gl.glDrawArrays(self.gl.GL_TRIANGLES, 0, self.numOfVerts)
          self.gl.glDisable(self.gl.GL_CULL_FACE)
  
          self.gl.glClear(self.gl.GL_COLOR_BUFFER_BIT)
          self.maskVAO.bind()
          self.gl.glStencilFunc(self.gl.GL_NOTEQUAL, 0, 0xFF)
          self.gl.glStencilOp(self.gl.GL_KEEP, self.gl.GL_KEEP, self.gl.GL_KEEP)
          self.gl.glDrawArrays(self.gl.GL_TRIANGLES, 0, 6)
          self.gl.glDisable(self.gl.GL_STENCIL_TEST)
  
          image = self.sliceFbo.toImage()
          # makes a QComboBox for different Image Format,
          # namely Format_Mono, Format_MonoLSB, and Format_Grayscale8
          image = image.convertToFormat(QtGui.QImage.Format_Grayscale8)
          image.save(os.path.join(self.sliceSavePath,
                                  'out{:04d}.png'.format(self.currentLayer)))
          self.sliceFbo.release()
  
      def keyPressEvent(self, event):
          if event.key() == QtCore.Qt.Key_Escape:
              sys.exit()
          event.accept()
  
  
  def main(stlFilename, layerThickness):
      temp = os.path.dirname(stlFilename)
      sliceSavePath = os.path.join(temp, 'slices')
  
      # remove old slices folder if there is one, and make a new empty one
      if os.path.exists(sliceSavePath):
          shutil.rmtree(sliceSavePath)        # using shutil here avoids permissions errors
      if not os.path.exists(sliceSavePath):
          os.mkdir(sliceSavePath)
  
      # Set format here, otherwise it throws error
      # `QCocoaGLContext: Falling back to unshared context.`
      # on Mac when use QOpenGLWidgets
      # https://doc.qt.io/qt-5/qopenglwidget.html#details last paragraph
      format = QtGui.QSurfaceFormat()
      format.setRenderableType(QtGui.QSurfaceFormat.OpenGL)
      format.setProfile(QtGui.QSurfaceFormat.CoreProfile)
      format.setVersion(4, 1)
      format.setStencilBufferSize(8)
      QtGui.QSurfaceFormat.setDefaultFormat(format)
  
      app = QtWidgets.QApplication(sys.argv)
      window = Window(stlFilename, layerThickness, sliceSavePath)
      window.resize(SCR_WIDTH, SCR_HEIGHT)
      window.show()
  
      sys.exit(app.exec_())
  
  
  if __name__ == '__main__':
  
      main(sys.argv[1], float(sys.argv[2]))
  
  </code></pre>