PyShell that controls separate process

Hey,
This week I announced the release of my wxPython project PythonTurtle. There was something I forgot to add, and that might be useful to some of you: During development of PythonTurtle I had to develop a shell, based on PyShell, that controls a separate Python process, created by the multiprocessing package. If you think this shell might be useful for your project. you are welcome to download it from the source code of PythonTurtle. It is an independent package named “shelltoprocess” in the folder “src”. It includes documentation.

Ram.

Hi!

I’m using the PrintFramework.py demo example to print some line charts but I have some problems:

  1. If I want to see a preview I get this error:

Traceback (most recent call last):

File “D:\Eigene Datein\Python\Test\src\test.py”, line 102, in OnPrintPage

maxX = self.canvas.getWidth()

AttributeError: ‘MyCanvas’ object has no attribute ‘getWidth’

If I set the self.canvas.getWidth() and self.canvas.getHeight() myself, I see a preview but the 2 plots are splitted on two pages. How I can put them one under the other?

Thanks for any suggestions!

Stefanie

Here’s the code:

···

import wx

BUFFERED = 1

class MyCanvas(wx.ScrolledWindow):
def init(self, parent, id = -1, size = wx.DefaultSize):
wx.ScrolledWindow.init(self, parent, id, (0, 0), size=size, style=wx.SUNKEN_BORDER)

self.lines = []
self.maxWidth = 1000
self.maxHeight = 1000
self.x = self.y = 0
self.curLine = []
self.drawing = False

self.SetBackgroundColour(“WHITE”)

self.SetVirtualSize((self.maxWidth, self.maxHeight))
self.SetScrollRate(20,20)

if BUFFERED:
self.buffer = wx.EmptyBitmap(self.maxWidth, self.maxHeight)
dc = wx.BufferedDC(None, self.buffer)
dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
dc.Clear()
y = 100
for i in range(2):
self.DoDrawing(dc, y = y)
y = y + 220

    self.Bind(wx.EVT_PAINT, self.OnPaint)

def OnPaint(self, event):
    if BUFFERED:
        dc = wx.BufferedPaintDC(self, self.buffer, wx.BUFFER_VIRTUAL_AREA)
    else:
        dc = wx.PaintDC(self)
        self.PrepareDC(dc)
        self.DoDrawing(dc, y = 100)

def DoDrawing(self, dc, y, printing=False):
    dc.BeginDrawing()
    dc.SetPen(wx.Pen('BLACK'))
    x = 100     # move plot left and right
    #y = 100     # move plot up and down
    length = 900
    hight = 200
    dc.DrawRectangle(x, y, length, hight+1)
           
    lst = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), (8, 9), (9, 10), (10, 11), (11, 12), (12, 13), (13, 14), (14, 15), (15, 16), (16, 17), (17, 18)]
    final_lst = []
    for i in lst:
        coord1 = i[0]*length/len(lst)
        coord2 = -i[1]*150/21
        final_lst.append((coord1, coord2))
    dc.SetPen(wx.Pen('RED', 2))
    dc.DrawLines(final_lst, x+1, y+200)

    dc.EndDrawing()

class MyPrintout(wx.Printout):
def init(self, canvas):
wx.Printout.init(self)
self.canvas = canvas

def OnBeginDocument(self, start, end):
return super(MyPrintout, self).OnBeginDocument(start, end)

def OnEndDocument(self):
super(MyPrintout, self).OnEndDocument()

def OnBeginPrinting(self):
super(MyPrintout, self).OnBeginPrinting()

def OnEndPrinting(self):
super(MyPrintout, self).OnEndPrinting()

def OnPreparePrinting(self):
super(MyPrintout, self).OnPreparePrinting()

def HasPage(self, page):
if page <= 2:
return True
else:
return False

def GetPageInfo(self):
return (1, 2, 1, 2)

def OnPrintPage(self, page):
dc = self.GetDC()

#-------------------------------------------
# One possible method of setting scaling factors…

maxX = self.canvas.getWidth()
maxY = self.canvas.getHeight()

Let’s have at least 50 device units margin

    marginX = 50
    marginY = 50

Add the margin to the graphic size

    maxX = maxX + (2 * marginX)
    maxY = maxY + (2 * marginY)

Get the size of the DC in pixels

    (w, h) = dc.GetSizeTuple()

Calculate a suitable scaling factor

    scaleX = float(w) / maxX
    scaleY = float(h) / maxY

Use x or y scaling factor, whichever fits on the DC

    actualScale = min(scaleX, scaleY)

Calculate the position on the DC for centering the graphic

    posX = (w - (self.canvas.getWidth() * actualScale)) / 2.0
    posY = (h - (self.canvas.getHeight() * actualScale)) / 2.0

Set the scale and origin

    dc.SetUserScale(actualScale, actualScale)
    dc.SetDeviceOrigin(int(posX), int(posY))

#-------------------------------------------

self.canvas.DoDrawing(dc, True)
dc.DrawText(“Page: %d” % page, marginX/2, maxY-marginY)

return True

class MyHelpFrame(wx.Frame):
def init(self):
wx.Frame.init(self, None, -1, “Help”, size=(1100, 800))

    self.printData = wx.PrintData()
    self.printData.SetPaperId(wx.PAPER_LETTER)
    self.printData.SetPrintMode(wx.PRINT_MODE_PRINTER)
    self.printData.SetOrientation(wx.LANDSCAPE)
    self.box = wx.BoxSizer(wx.VERTICAL)
    self.canvas = MyCanvas(self)
    self.box.Add(self.canvas, 1, wx.GROW)
    self.SetAutoLayout(True)
    self.SetSizer(self.box)
    self.build_menu()

def build_menu(self):
    # Create Menu Bar
    menuBar = wx.MenuBar()
    # Create Menu1 from left
    menu1 = wx.Menu()
    menu1.Append(101, "Preview")
             
    # Append all Menus
    menuBar.Append(menu1, "Preview")
 
   
    # # Bind all  Menus
    self.Bind(wx.EVT_MENU, self.OnPrintPreview, id=101)
   
    self.SetMenuBar(menuBar)
   
def OnPrintPreview(self, event):
    data = wx.PrintDialogData(self.printData)
    printout = MyPrintout(self.canvas)
    printout2 = MyPrintout(self.canvas)
    self.preview = wx.PrintPreview(printout, printout2, data)

if not self.preview.Ok():
return

pfrm = wx.PreviewFrame(self.preview, self, “This is a print preview”)

pfrm.Initialize()
pfrm.SetPosition(self.GetPosition())
pfrm.SetSize(self.GetSize())
pfrm.Show(True)

if name == ‘main’:
app = wx.PySimpleApp()
frame = MyHelpFrame()
frame.Show(True)
app.MainLoop()


import wx
import test

class MyPrintout(wx.Printout):
def init(self, canvas):
wx.Printout.init(self)
self.canvas = canvas

def OnBeginDocument(self, start, end):
return super(MyPrintout, self).OnBeginDocument(start, end)

def OnEndDocument(self):
super(MyPrintout, self).OnEndDocument()

def OnBeginPrinting(self):
super(MyPrintout, self).OnBeginPrinting()

def OnEndPrinting(self):
super(MyPrintout, self).OnEndPrinting()

def OnPreparePrinting(self):
super(MyPrintout, self).OnPreparePrinting()

def HasPage(self, page):
if page <= 2:
return True
else:
return False

def GetPageInfo(self):
return (1, 2, 1, 2)

def OnPrintPage(self, page):
dc = self.GetDC()

#-------------------------------------------
# One possible method of setting scaling factors…

maxX = self.canvas.getWidth()
maxY = self.canvas.getHeight()

Let’s have at least 50 device units margin

    marginX = 50
    marginY = 50

Add the margin to the graphic size

    maxX = maxX + (2 * marginX)
    maxY = maxY + (2 * marginY)

Get the size of the DC in pixels

    (w, h) = dc.GetSizeTuple()

Calculate a suitable scaling factor

    scaleX = float(w) / maxX
    scaleY = float(h) / maxY

Use x or y scaling factor, whichever fits on the DC

    actualScale = min(scaleX, scaleY)

Calculate the position on the DC for centering the graphic

    posX = (w - (self.canvas.getWidth() * actualScale)) / 2.0
    posY = (h - (self.canvas.getHeight() * actualScale)) / 2.0

Set the scale and origin

    dc.SetUserScale(actualScale, actualScale)
    dc.SetDeviceOrigin(int(posX), int(posY))

#-------------------------------------------

self.canvas.DoDrawing(dc, True)
dc.DrawText(“Page: %d” % page, marginX/2, maxY-marginY)

return True

#----------------------------------------------------------------------

class TestPrintPanel(wx.Panel):
def init(self, parent, frame):
wx.Panel.init(self, parent, -1)
self.frame = frame

self.printData = wx.PrintData()
self.printData.SetPaperId(wx.PAPER_LETTER)
self.printData.SetPrintMode(wx.PRINT_MODE_PRINTER)

    self.box = wx.BoxSizer(wx.VERTICAL)
    self.canvas = ScrolledWindow.MyCanvas(self)
    self.box.Add(self.canvas, 1, wx.GROW)

subbox = wx.BoxSizer(wx.HORIZONTAL)
btn = wx.Button(self, -1, “Page Setup”)
self.Bind(wx.EVT_BUTTON, self.OnPageSetup, btn)
subbox.Add(btn, 1, wx.GROW | wx.ALL, 2)

btn = wx.Button(self, -1, “Print Preview”)
self.Bind(wx.EVT_BUTTON, self.OnPrintPreview, btn)
subbox.Add(btn, 1, wx.GROW | wx.ALL, 2)

btn = wx.Button(self, -1, “Print”)
self.Bind(wx.EVT_BUTTON, self.OnDoPrint, btn)
subbox.Add(btn, 1, wx.GROW | wx.ALL, 2)

self.box.Add(subbox, 0, wx.GROW)

self.SetAutoLayout(True)
self.SetSizer(self.box)

def OnPageSetup(self, evt):
psdd = wx.PageSetupDialogData(self.printData)
psdd.CalculatePaperSizeFromId()
dlg = wx.PageSetupDialog(self, psdd)
dlg.ShowModal()

this makes a copy of the wx.PrintData instead of just saving

    # a reference to the one inside the PrintDialogData that will
    # be destroyed when the dialog is destroyed
    self.printData = wx.PrintData( dlg.GetPageSetupData().GetPrintData() )

dlg.Destroy()

def OnPrintPreview(self, event):
data = wx.PrintDialogData(self.printData)
printout = MyPrintout(self.canvas)
printout2 = MyPrintout(self.canvas)
self.preview = wx.PrintPreview(printout, printout2, data)

if not self.preview.Ok():
self.log.WriteText(“Houston, we have a problem…\n”)
return

pfrm = wx.PreviewFrame(self.preview, self.frame, “This is a print preview”)

pfrm.Initialize()
pfrm.SetPosition(self.frame.GetPosition())
pfrm.SetSize(self.frame.GetSize())
pfrm.Show(True)

def OnDoPrint(self, event):
pdd = wx.PrintDialogData(self.printData)
pdd.SetToPage(2)
printer = wx.Printer(pdd)
printout = MyPrintout(self.canvas)

if not printer.Print(self.frame, printout, True):
wx.MessageBox(“There was a problem printing.\nPerhaps your current printer is not set correctly?”, “Printing”, wx.OK)
else:
self.printData = wx.PrintData( printer.GetPrintDialogData().GetPrintData() )
printout.Destroy()

class MyHelpFrame(wx.Frame):
def init(self):
wx.Frame.init(self, None, -1, “Help”, size=(1100, 800))
self.canvas = TestPrintPanel(self)

if name == ‘main’:
app = wx.PySimpleApp()
frame = MyHelpFrame()
frame.Show(True)
app.MainLoop()

Stefanie Lück wrote:

Hi!
I'm using the PrintFramework.py demo example to print some line charts but I have some problems:
1) If I want to see a preview I get this error:
Traceback (most recent call last):
_File "D:\Eigene Datein\Python\Test\src\test.py", line 102, in OnPrintPage_
__maxX = self.canvas.getWidth()
AttributeError: 'MyCanvas' object has no attribute 'getWidth'

Well, this should be obvious. The MyCanvas class used in the demo has getWidth and getHeight methods, but your MyCanvas class does not.

If I set the self.canvas.getWidth() and self.canvas.getHeight() myself, I see a preview but the 2 plots are splitted on two pages. How I can put them one under the other?

Draw them both in the OnPrintPage method for the first page. You are telling the system in GetPageInfo and HasPage how many pages there are. If you only want to print one page then just say 1 instead of 2. Then your OnPrintPage will be called for each page and it is up to you what will be drawn on each.

···

--
Robin Dunn
Software Craftsman