wxPython/ReportLab: How to create and open a .pdf file on button clicked
Question: Good day all, For about 3days I have been trying hardest to get my way around
this! I have a perfectly working wxFrame as well as a perfectly working
ReportLab pdf script. See the code files below respectively (Note: data1.py is
the GUI while data2.py is the running pdf script). My problems are:- 1) I want
the pdf script to run only after I press the “Save To PDF” button 2) The value
of the name field (as stored in a variable “NameString”) should be added to
the generated pdf file.

At the moment, if I run the script (data2.py) it creates but the Frame and the
pdf file (without including the “NameString”) at the same time. That is not
what I want, I want the pdf to open and include the “NameString” only after I
clicked/press the “Save To PDF” button.

Thanks in advance for your time.

**data1.py**

    
    
    class MyFrame1 ( wx.Frame ):
    
        def __init__( self, parent ):
            wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = wx.EmptyString, pos = wx.DefaultPosition, size = wx.Size( 250,150 ), style = wx.CAPTION|wx.CLOSE_BOX|wx.MINIMIZE_BOX|wx.SYSTEM_MENU|wx.TAB_TRAVERSAL )
    
            self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize )
    
            DataBox = wx.BoxSizer( wx.HORIZONTAL )
    
            gSizer2 = wx.GridSizer( 0, 2, 0, 0 )
    
            self.NameLabel = wx.StaticText( self, wx.ID_ANY, u"Name", wx.DefaultPosition, wx.DefaultSize, 0 )
            self.NameLabel.Wrap( -1 )
            self.NameLabel.SetFont( wx.Font( 13, 70, 90, 90, False, wx.EmptyString ) )
    
            gSizer2.Add( self.NameLabel, 0, wx.ALL, 5 )
    
            self.NameField = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
            gSizer2.Add( self.NameField, 1, wx.ALL, 5 )
    
            self.SaveToPDF = wx.Button( self, wx.ID_ANY, u"Save To PDF", wx.DefaultPosition, wx.DefaultSize, 0 )
            gSizer2.Add( self.SaveToPDF, 0, wx.ALL, 5 )
    
    
            DataBox.Add( gSizer2, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL, 5 )
    
    
            self.SetSizer( DataBox )
            self.Layout()
    
            self.Centre( wx.BOTH )
    
            # Connect Events
            self.SaveToPDF.Bind( wx.EVT_BUTTON, self.SaveToPDF_Function )
    
        def __del__( self ):
            pass
    
    
        # Virtual event handlers, overide them in your derived class
        def SaveToPDF_Function( self, event ):
            event.Skip()
    

**data2.py**

    
    
    #!/usr/bin/python
    # -*- coding: utf-8 -*- 
    
    
    import wx
    from data1 import MyFrame1
    
    from reportlab.pdfgen import canvas
    from reportlab.lib.pagesizes import A3
    from reportlab.lib.pagesizes import landscape
    
    import os
    import tempfile
    import threading
    
    
    
    class MyFrame2(MyFrame1):
        def __init__(self, parent):
            MyFrame1.__init__ (self, parent)
    
    
    
        def SaveToPDF_Function( self, event ):
    
            NameString = self.NameField.GetValue()
            print NameString
    
    
    
    
    file_not_fount = "Selected file doesn't exist!"
    
    
    class Launcher(threading.Thread):
        def __init__(self,path):
            threading.Thread.__init__(self)
            self.path = 'myFile.pdf'
        def run(self):
            self.open_file(self.path)
    
    
    
        def open_file(self,path):
    
            if os.path.exists(path):
                if os.name == 'posix':
                    subprocess.call(["xdg-open", path])
                    #os.popen("evince %s" % path)
                else:
                    os.startfile(path)
            else:
                wx.MessageBox(self.file_not_fount,
                              self.title,
                              wx.OK|wx.ICON_INFORMATION)
    
    # NameString = raw_input("Enter ur text: ")
    
    c = canvas.Canvas("myFile.pdf", pagesize=landscape(A3))
    
    c.drawCentredString(600, 800, 'I want this to open only after I clicked the “Save To PDF” button. Also, the text field value (NameString variable) should appear here =>'  + 'NameString' )
    
    c.save()
    
    Launcher('myFile.pdf').start()
    
    
    
    
    
    
    app = wx.App(0)
    MyFrame2(None).Show()
    app.MainLoop()
    


Answer: First of all, I would create the reportlab module so that you can actually
call it. As it is currently implemented, it will immediately create the PDF
whenever it is run. You need to put all the reportlab code into a function of
some sort. Since this is a really simple reportlab script, I just combined it
with the wxPython code and removed the threading portion so you can see one
easy way to do it:

    
    
    import wx
    
    from reportlab.pdfgen import canvas
    from reportlab.lib.pagesizes import A3
    from reportlab.lib.pagesizes import landscape
    
    class MyFrame1 ( wx.Frame ):
    
        def __init__( self ):
            wx.Frame.__init__ ( self, None, 
                                size = wx.Size( 250,150 ), 
                                style = wx.CAPTION|wx.CLOSE_BOX|wx.MINIMIZE_BOX|wx.SYSTEM_MENU|wx.TAB_TRAVERSAL )
    
            self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize )
    
            DataBox = wx.BoxSizer( wx.HORIZONTAL )
    
            gSizer2 = wx.GridSizer( 0, 2, 0, 0 )
    
            self.NameLabel = wx.StaticText( self, wx.ID_ANY, u"Name", wx.DefaultPosition, wx.DefaultSize, 0 )
            self.NameLabel.Wrap( -1 )
            self.NameLabel.SetFont( wx.Font( 13, 70, 90, 90, False, wx.EmptyString ) )
    
            gSizer2.Add( self.NameLabel, 0, wx.ALL, 5 )
    
            self.NameField = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
            gSizer2.Add( self.NameField, 1, wx.ALL, 5 )
    
            self.SaveToPDF = wx.Button( self, wx.ID_ANY, u"Save To PDF", wx.DefaultPosition, wx.DefaultSize, 0 )
            gSizer2.Add( self.SaveToPDF, 0, wx.ALL, 5 )
    
    
            DataBox.Add( gSizer2, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL, 5 )
    
    
            self.SetSizer( DataBox )
            self.Layout()
    
            self.Centre( wx.BOTH )
    
            # Connect Events
            self.SaveToPDF.Bind( wx.EVT_BUTTON, self.SaveToPDF_Function )
    
            self.Show()
    
        #----------------------------------------------------------------------
        def create_pdf(self):
            """"""
            nameString = str(self.NameField.GetValue())
            c = canvas.Canvas("myFile.pdf", pagesize=landscape(A3))
    
            txt = """'I want this to open only after I clicked the "Save To PDF" 
            button. Also, the text field value (NameString variable) should appear 
            here =>'  + %s """ % nameString
            c.drawCentredString(600, 800, txt)
    
            c.save()
    
    
        # Virtual event handlers, overide them in your derived class
        def SaveToPDF_Function( self, event ):
            self.create_pdf()
    
    if __name__ == "__main__":
        app = wx.App(False)
        frame = MyFrame1()
        app.MainLoop()
    

I recommend reading the following articles on threads and wxPython:

  * <http://www.blog.pythonlibrary.org/2010/05/22/wxpython-and-threads/>
  * <http://wiki.wxpython.org/LongRunningTasks>

To add threading back in, I would create a threading class like your Launcher.
However, I would put ALL the reportlab code inside that class and when you
instantiate the Thread class, you would pass in the **namedString** :

    
    
    class Launcher(threading.Thread):
        def __init__(self,path, namedString):
            threading.Thread.__init__(self)
            self.path = 'myFile.pdf'
            self.namedString = namedString
    

I hope this helps!

