A real print preview

#~ DlgPageA4Zoom2 (tmp name) is a dialog window, that displays a real
#~ print preview of one printer page. I call it real because the
#~ preview dialog displays the physical page and the printable area,
#~ which is correctly positionned on the physical page.
#~ It uses the Pythonwin extensions, so it works only on a win32
#~ platform.
#~ Plotting is achieved in the usual way, through the dc (see
#~ fct DoDraw2).

#~ My problem:
#~ I did not succeed to set the focus on the ScrolledWindow. So,
#~ I cann't move the scrollbars with the arrow keys. A similar
# dialog without the close button and the combo box works.

#~ Attempts without succes:
#~ - no sizers
#~ - wx.Frame instead of wx.Dialog
#~ - two panels
#~ - page and area classes outside the Dialog class

#~ Question:
#~ In this print preview, a zoom of 100% represents a wx.window
#~ having a size equal to the printer dc size (in pixel). What
#~ is a zoom of 100% supposed to represent on the screen?

#~ Any idea is wellcome.

#~ Jean-Michel Fauth, Switzerland

# -*- coding: iso-8859-1 -*-

···

#--------------------------------------------------------------------
# Name: PageA4Zoom4.py
# Purpose:
# Author: Jean-Michel Fauth, Switzerland
# Copyright: (c) 2005 Jean-Michel Fauth
# Licence: GPL
#--------------------------------------------------------------------
# os dev: windows 98
# py dev: Python 2.4.1
# wx dev: wxPython 2.6.1
# Revision: 7 June 2005
#--------------------------------------------------------------------

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

import wx
import win32print
import win32ui

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

#Constants for GetDeviceCaps
HORZSIZE = 4 # Horizontal size in millimeters
VERTSIZE = 6 # Vertical size in millimeters
HORZRES = 8 # Horizontal width in pixels == dc.Width() in wx when
printing
VERTRES = 10 # Vertical width in pixels == dc.Height() in wx when
printing
PHYSICALWIDTH = 110 # Physical Width in device units
PHYSICALHEIGHT = 111 # Physical Height in device units
PHYSICALOFFSETX = 112 # Physical Printable Area x margin
PHYSICALOFFSETY = 113 # Physical Printable Area y margin

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

def DoDraw2(dc):
    dc.BeginDrawing()

    dcwi, dche = dc.GetSize()
    p1x, p1y = 0, 0
    p2x, p2y = dcwi, dche
    mx, my = dcwi / 2, dche / 2

    #plot area
    dc.SetPen(wx.Pen(wx.NamedColour('RED')))
    dc.DrawRectangle(0, 0, dcwi, dche)

    #a square proportional to the dc size, centered on the dc
    dc.SetPen(wx.Pen(wx.NamedColour('BLUE')))
    side = dcwi * 0.2
    dc.DrawRectangle(mx - side / 2, my - side / 2, side, side)

    dc.EndDrawing()

#-------------------------------------------------------------------
#- zoom and sizeable -----------------------------------------------
#-------------------------------------------------------------------

class DlgPageA4Zoom2(wx.Dialog):

    def __init__(self, parent, id, pos, size):
        s = 'Preview'
        sty = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER
        wx.Dialog.__init__(self, parent, id, s, pos, size, sty)

        #get printer name and create a dc
        PrinterName = win32print.GetDefaultPrinter()
        #~ print 'printer_name:', printer_name
        hDC = win32ui.CreateDC()
        hDC.CreatePrinterDC(PrinterName)

        #get relevant infos
        self.PhysicalWidth = hDC.GetDeviceCaps(PHYSICALWIDTH)
        self.PhysicalHeight = hDC.GetDeviceCaps(PHYSICALHEIGHT)
        self.HorzRes = hDC.GetDeviceCaps(HORZRES)
        self.VertRes = hDC.GetDeviceCaps(VERTRES)
        self.PhysicalOffsetX = hDC.GetDeviceCaps(PHYSICALOFFSETX)
        self.PhysicalOffsetY = hDC.GetDeviceCaps(PHYSICALOFFSETY)

        del hDC

        self.SetTitle('Preview - ' + PrinterName)

        #used widgets
        but1 = wx.Button(self, wx.NewId(), 'Close', (-1, -1), (-1, -1))
        but1.Bind(wx.EVT_BUTTON, self.OnClick1)

        combolist = []
        for i in xrange(5, 105, 5):
            s = ('%i%%') % (i)
            combolist.append(s)
        sty = wx.CB_READONLY
        self.combo = wx.ComboBox(self, wx.NewId(), '50%', (-1, -1),
(-1, -1), combolist, sty)
        self.combo.Bind(wx.EVT_COMBOBOX, self.OnComboBox)
        self.combo.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)

        #default value at ShowModal
        self.combo.SetValue('20%')
        #real zoom factor
        self.zoom = float(self.combo.GetValue().rstrip('%')) / 100.0

        #the scrolled window, position and size from vsizer
        self.swin = wx.ScrolledWindow(self, wx.NewId(), (-1, -1), (-1, -1),
wx.SUNKEN_BORDER)
        c = 120
        self.swin.SetBackgroundColour(wx.Colour(c, c, c))

        #the page
        #position of the page in the scrolled window
        self.posinswin = 20
        tpos = (self.posinswin, self.posinswin)
        tsize = (self.PhysicalWidth * self.zoom, self.PhysicalHeight *
self.zoom)
        self.page = wx.Window(self.swin, wx.NewId(), tpos, tsize,
wx.NO_BORDER)
        c = 240
        self.page.SetBackgroundColour(wx.Colour(c, c, c))

        #the printable area
        tpos = (self.PhysicalOffsetX * self.zoom, self.PhysicalOffsetY *
self.zoom)
        tsize = (self.HorzRes * self.zoom, self.VertRes * self.zoom)
        self.area = wx.Window(self.page, wx.NewId(), tpos, tsize,
wx.NO_BORDER)
        self.area.SetBackgroundColour(wx.NamedColour('white'))

        #set the virtual size and reset the scrollbars
        tvsize = (self.PhysicalWidth * self.zoom + 2 * self.posinswin,
self.PhysicalHeight * self.zoom + 2 * self.posinswin)
        self.swin.Scroll(0, 0)
        self.swin.SetVirtualSize(tvsize)
        self.swin.SetScrollRate(1, 1)

        #~ self.swin.SetDimensions(20, 20, 300, 300)
        #the dlg is sizable, create sizers and place the ctrls
        b = 5
        hsizer1 = wx.BoxSizer(wx.HORIZONTAL)
        hsizer1.Add(but1, 0, wx.ALL, b)
        hsizer1.Add(self.combo, 0, wx.ALL, b)

        b = 0
        vsizer1 = wx.BoxSizer(wx.VERTICAL)
        vsizer1.Add(hsizer1, 0, wx.ALIGN_LEFT | wx.ALL, b)
        vsizer1.Add(self.swin, 1, wx.EXPAND | wx.ALL, b)
        self.SetSizer(vsizer1)

        self.swin.SetFocus()

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

    def OnPaint(self, event):
        dc = wx.PaintDC(self.area)
        self.PrepareDC(dc)
        DoDraw2(dc)

    def OnClick1(self, event):
        self.EndModal(wx.ID_CANCEL)

    def OnComboBox(self, event):
        self.zoom = float(self.combo.GetValue().rstrip('%')) / 100.0

        self.swin.Scroll(0, 0)

        self.posinswin = 50
        tpos = (self.posinswin, self.posinswin)
        tsize = (self.PhysicalWidth * self.zoom, self.PhysicalHeight *
self.zoom)
        self.page.SetPosition(tpos)
        self.page.SetSize(tsize)

        tpos = (self.PhysicalOffsetX * self.zoom, self.PhysicalOffsetY *
self.zoom)
        tsize = (self.HorzRes * self.zoom, self.VertRes * self.zoom)
        self.area.SetPosition(tpos)
        self.area.SetSize(tsize)

        tvsize = (self.PhysicalWidth * self.zoom + 2 * self.posinswin,
self.PhysicalHeight * self.zoom + 2 * self.posinswin)
        self.swin.SetVirtualSize(tvsize)

        self.area.Refresh()

        self.swin.SetFocus()

    #force a selection with the mouse, tmp hack
    def OnKeyDown(self, event):
        pass

#-------------------------------------------------------------------
# main app ---------------------------------------------------------
#-------------------------------------------------------------------

class MyPanel(wx.Panel):

    def __init__(self, parent, id):
        wx.Panel.__init__(self, parent, id, wx.DefaultPosition,
wx.DefaultSize)

        b1 = wx.Button(self, wx.NewId(), 'my preview', (20, 20), (-1, -1))
        b1.Bind(wx.EVT_BUTTON, self.OnClick1)

    def OnClick1(self, event):
        dlg = DlgPageA4Zoom2(self, wx.NewId(), (0, 0), (800, 600))
        dlg.ShowModal()

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

class MyFrame(wx.Frame):

    def __init__(self, parent, id):
        s = 'PageA4Zoom'
        sty = wx.DEFAULT_FRAME_STYLE
        wx.Frame.__init__(self, parent, id, s, (20, 20), (300, 200), sty)

        pa = MyPanel(self, -1)

        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)

    def OnCloseWindow(self, event):
        self.Destroy()

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

class MyApp(wx.App):

    def OnInit(self):
        frame = MyFrame(None, -1)
        frame.Show(True)
        self.SetTopWindow(frame)
        return True

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

def main():
    app = MyApp(0)
    app.MainLoop()

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

if __name__ == '__main__':
    main()

#eof-------------------------------------------------------------------

Jean-Michel Fauth wrote:

#~ DlgPageA4Zoom2 (tmp name) is a dialog window, that displays a real
#~ print preview of one printer page. I call it real because the
#~ preview dialog displays the physical page and the printable area,
#~ which is correctly positionned on the physical page.
#~ It uses the Pythonwin extensions, so it works only on a win32
#~ platform.
#~ Plotting is achieved in the usual way, through the dc (see
#~ fct DoDraw2).

#~ My problem:
#~ I did not succeed to set the focus on the ScrolledWindow. So,
#~ I cann't move the scrollbars with the arrow keys. A similar
# dialog without the close button and the combo box works.

I think it is because of the child windows used in the scrolled window. When it gets the focus it tries to set it to its first child that accepts the focus. But since its child is a wx.Window which normally doesn't accept the focus it keeps searching and ends up back at the button or combobox. I think that instead of using subwindows to represent the page and drawable area if you just draw them on the scrolled window (and clip and offset the dc to just be drawable within center area) then it will work better for you.

#~ Attempts without succes:
#~ - no sizers
#~ - wx.Frame instead of wx.Dialog
#~ - two panels
#~ - page and area classes outside the Dialog class

#~ Question:
#~ In this print preview, a zoom of 100% represents a wx.window
#~ having a size equal to the printer dc size (in pixel). What
#~ is a zoom of 100% supposed to represent on the screen?

Normally I think it is supposed to mean that 1 unit (inch, centimeter, etc.) on the screen is equal to 1 unit on the paper. So that means adjusting things by the DPI of the screen vs the DPI of the printer, but since most display drivers don't use an accurate DPI setting then your preview can only be approximate or a best guess.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!