dialogs/FBDBlockDialog.py
changeset 409 34c9f624c2fe
child 534 d506a353b3d3
equal deleted inserted replaced
408:0e389fa5b160 409:34c9f624c2fe
       
     1 #!/usr/bin/env python
       
     2 # -*- coding: utf-8 -*-
       
     3 
       
     4 #This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
       
     5 #based on the plcopen standard. 
       
     6 #
       
     7 #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
       
     8 #
       
     9 #See COPYING file for copyrights details.
       
    10 #
       
    11 #This library is free software; you can redistribute it and/or
       
    12 #modify it under the terms of the GNU General Public
       
    13 #License as published by the Free Software Foundation; either
       
    14 #version 2.1 of the License, or (at your option) any later version.
       
    15 #
       
    16 #This library is distributed in the hope that it will be useful,
       
    17 #but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    18 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    19 #General Public License for more details.
       
    20 #
       
    21 #You should have received a copy of the GNU General Public
       
    22 #License along with this library; if not, write to the Free Software
       
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
       
    24 
       
    25 import wx
       
    26 
       
    27 from graphics import *
       
    28 
       
    29 #-------------------------------------------------------------------------------
       
    30 #                          Create New Block Dialog
       
    31 #-------------------------------------------------------------------------------
       
    32 
       
    33 [ID_FBDBLOCKDIALOG, ID_FBDBLOCKDIALOGNAME, 
       
    34  ID_FBDBLOCKDIALOGTYPETREE, ID_FBDBLOCKDIALOGTYPEDESC, 
       
    35  ID_FBDBLOCKDIALOGINPUTS, ID_FBDBLOCKDIALOGPREVIEW, 
       
    36  ID_FBDBLOCKDIALOGEXECUTIONORDER, ID_FBDBLOCKDIALOGEXECUTIONCONTROL, 
       
    37  ID_FBDBLOCKDIALOGSTATICTEXT1, ID_FBDBLOCKDIALOGSTATICTEXT2, 
       
    38  ID_FBDBLOCKDIALOGSTATICTEXT3, ID_FBDBLOCKDIALOGSTATICTEXT4, 
       
    39  ID_FBDBLOCKDIALOGSTATICTEXT5, ID_FBDBLOCKDIALOGSTATICTEXT6, 
       
    40 ] = [wx.NewId() for _init_ctrls in range(14)]
       
    41 
       
    42 [CATEGORY, BLOCK] = range(2)
       
    43 
       
    44 class FBDBlockDialog(wx.Dialog):
       
    45     
       
    46     if wx.VERSION < (2, 6, 0):
       
    47         def Bind(self, event, function, id = None):
       
    48             if id is not None:
       
    49                 event(self, id, function)
       
    50             else:
       
    51                 event(self, function)
       
    52     
       
    53     def _init_coll_flexGridSizer1_Items(self, parent):
       
    54         parent.AddSizer(self.MainSizer, 0, border=20, flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT)
       
    55         parent.AddSizer(self.ButtonSizer, 0, border=20, flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT)
       
    56 
       
    57     def _init_coll_flexGridSizer1_Growables(self, parent):
       
    58         parent.AddGrowableCol(0)
       
    59         parent.AddGrowableRow(0)
       
    60 
       
    61     def _init_coll_MainSizer_Items(self, parent):
       
    62         parent.AddSizer(self.LeftBoxSizer, 1, border=5, flag=wx.GROW|wx.RIGHT)
       
    63         parent.AddSizer(self.RightGridSizer, 1, border=5, flag=wx.GROW|wx.LEFT)
       
    64 
       
    65     def _init_coll_LeftBoxSizer_Items(self, parent):
       
    66         parent.AddWindow(self.TypeTree, 3, border=5, flag=wx.GROW|wx.BOTTOM)
       
    67         parent.AddWindow(self.TypeDesc, 1, border=0, flag=wx.GROW)
       
    68 
       
    69     def _init_coll_RightGridSizer_Items(self, parent):
       
    70         parent.AddSizer(self.RightUpGridSizer, 0, border=0, flag=wx.GROW)
       
    71         parent.AddWindow(self.staticText6, 0, border=0, flag=wx.GROW)
       
    72         parent.AddWindow(self.Preview, 0, border=0, flag=wx.GROW)
       
    73 
       
    74     def _init_coll_RightGridSizer_Growables(self, parent):
       
    75         parent.AddGrowableCol(0)
       
    76         parent.AddGrowableRow(2)
       
    77 
       
    78     def _init_coll_RightUpGridSizer_Items(self, parent):
       
    79         parent.AddWindow(self.staticText2, 0, border=4, flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP)
       
    80         parent.AddWindow(self.BlockName, 0, border=0, flag=wx.GROW)
       
    81         parent.AddWindow(self.staticText3, 0, border=4, flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP)
       
    82         parent.AddWindow(self.Inputs, 0, border=0, flag=wx.GROW)
       
    83         parent.AddWindow(self.staticText4, 0, border=4, flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP)
       
    84         parent.AddWindow(self.ExecutionOrder, 0, border=0, flag=wx.GROW)
       
    85         parent.AddWindow(self.staticText5, 0, border=4, flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP)
       
    86         parent.AddWindow(self.ExecutionControl, 0, border=0, flag=wx.GROW)
       
    87     
       
    88     def _init_coll_RightUpGridSizer_Growables(self, parent):
       
    89         parent.AddGrowableCol(1)
       
    90     
       
    91     def _init_sizers(self):
       
    92         self.flexGridSizer1 = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=10)
       
    93         self.MainSizer = wx.BoxSizer(wx.HORIZONTAL)
       
    94         self.LeftBoxSizer = wx.StaticBoxSizer(self.staticbox1, wx.VERTICAL)
       
    95         self.RightGridSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=5)
       
    96         self.RightUpGridSizer = wx.FlexGridSizer(cols=2, hgap=5, rows=4, vgap=5)
       
    97         
       
    98         self._init_coll_flexGridSizer1_Items(self.flexGridSizer1)
       
    99         self._init_coll_flexGridSizer1_Growables(self.flexGridSizer1)
       
   100         self._init_coll_MainSizer_Items(self.MainSizer)
       
   101         self._init_coll_LeftBoxSizer_Items(self.LeftBoxSizer)
       
   102         self._init_coll_RightGridSizer_Items(self.RightGridSizer)
       
   103         self._init_coll_RightGridSizer_Growables(self.RightGridSizer)
       
   104         self._init_coll_RightUpGridSizer_Items(self.RightUpGridSizer)
       
   105         self._init_coll_RightUpGridSizer_Growables(self.RightUpGridSizer)
       
   106         
       
   107         self.SetSizer(self.flexGridSizer1)
       
   108 
       
   109     def _init_ctrls(self, prnt):
       
   110         wx.Dialog.__init__(self, id=ID_FBDBLOCKDIALOG,
       
   111               name='FBDBlockDialog', parent=prnt, pos=wx.Point(376, 223),
       
   112               size=wx.Size(600, 400), style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER,
       
   113               title=_('Block Properties'))
       
   114         self.SetClientSize(wx.Size(600, 400))
       
   115 
       
   116         self.staticbox1 = wx.StaticBox(id=ID_FBDBLOCKDIALOGSTATICTEXT1,
       
   117               label=_('Type:'), name='staticBox1', parent=self,
       
   118               pos=wx.Point(0, 0), size=wx.Size(0, 0), style=0)
       
   119 
       
   120         self.staticText2 = wx.StaticText(id=ID_FBDBLOCKDIALOGSTATICTEXT2,
       
   121               label=_('Name:'), name='staticText2', parent=self,
       
   122               pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
       
   123 
       
   124         self.staticText3 = wx.StaticText(id=ID_FBDBLOCKDIALOGSTATICTEXT2,
       
   125               label=_('Inputs:'), name='staticText4', parent=self,
       
   126               pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
       
   127 
       
   128         self.staticText4 = wx.StaticText(id=ID_FBDBLOCKDIALOGSTATICTEXT4,
       
   129               label=_('Execution Order:'), name='staticText4', parent=self,
       
   130               pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
       
   131 
       
   132         self.staticText5 = wx.StaticText(id=ID_FBDBLOCKDIALOGSTATICTEXT5,
       
   133               label=_('Execution Control:'), name='staticText5', parent=self,
       
   134               pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
       
   135 
       
   136         self.staticText6 = wx.StaticText(id=ID_FBDBLOCKDIALOGSTATICTEXT6,
       
   137               label=_('Preview:'), name='staticText6', parent=self,
       
   138               pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
       
   139 
       
   140         if wx.Platform == '__WXMSW__':
       
   141             treestyle = wx.TR_HAS_BUTTONS|wx.TR_SINGLE|wx.SUNKEN_BORDER
       
   142         else:
       
   143             treestyle = wx.TR_HAS_BUTTONS|wx.TR_HIDE_ROOT|wx.TR_SINGLE|wx.SUNKEN_BORDER
       
   144         self.TypeTree = wx.TreeCtrl(id=ID_FBDBLOCKDIALOGTYPETREE,
       
   145               name='TypeTree', parent=self, pos=wx.Point(0, 0),
       
   146               size=wx.Size(0, 0), style=treestyle)
       
   147         self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnTypeTreeItemSelected,
       
   148               id=ID_FBDBLOCKDIALOGTYPETREE)
       
   149 
       
   150         self.TypeDesc = wx.TextCtrl(id=ID_FBDBLOCKDIALOGTYPEDESC,
       
   151               name='TypeDesc', parent=self, pos=wx.Point(0, 0),
       
   152               size=wx.Size(0, 0), style=wx.TE_READONLY|wx.TE_MULTILINE)
       
   153 
       
   154         self.BlockName = wx.TextCtrl(id=ID_FBDBLOCKDIALOGNAME, value='',
       
   155               name='BlockName', parent=self, pos=wx.Point(0, 0),
       
   156               size=wx.Size(0, 24), style=0)
       
   157         self.Bind(wx.EVT_TEXT, self.OnNameChanged, id=ID_FBDBLOCKDIALOGNAME)
       
   158 
       
   159         self.Inputs = wx.SpinCtrl(id=ID_FBDBLOCKDIALOGINPUTS,
       
   160               name='Inputs', parent=self, pos=wx.Point(0, 0),
       
   161               size=wx.Size(0, 24), style=wx.SP_ARROW_KEYS, min=2, max=20)
       
   162         self.Bind(wx.EVT_SPINCTRL, self.OnInputsChanged, id=ID_FBDBLOCKDIALOGINPUTS)
       
   163 
       
   164         self.ExecutionOrder = wx.SpinCtrl(id=ID_FBDBLOCKDIALOGEXECUTIONORDER,
       
   165               name='ExecutionOrder', parent=self, pos=wx.Point(0, 0),
       
   166               size=wx.Size(0, 24), style=wx.SP_ARROW_KEYS, min=0)
       
   167         self.Bind(wx.EVT_SPINCTRL, self.OnExecutionOrderChanged, id=ID_FBDBLOCKDIALOGEXECUTIONORDER)
       
   168 
       
   169         self.ExecutionControl = wx.CheckBox(id=ID_FBDBLOCKDIALOGEXECUTIONCONTROL,
       
   170               name='ExecutionControl', parent=self, pos=wx.Point(0, 0),
       
   171               size=wx.Size(0, 24), style=0)
       
   172         self.Bind(wx.EVT_CHECKBOX, self.OnExecutionOrderChanged, id=ID_FBDBLOCKDIALOGEXECUTIONCONTROL)
       
   173 
       
   174         self.Preview = wx.Panel(id=ID_FBDBLOCKDIALOGPREVIEW,
       
   175               name='Preview', parent=self, pos=wx.Point(0, 0),
       
   176               size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL|wx.SIMPLE_BORDER)
       
   177         self.Preview.SetBackgroundColour(wx.Colour(255,255,255))
       
   178         setattr(self.Preview, "GetDrawingMode", lambda:FREEDRAWING_MODE)
       
   179         setattr(self.Preview, "GetScaling", lambda:None)
       
   180         setattr(self.Preview, "GetBlockType", self.Controler.GetBlockType)
       
   181         setattr(self.Preview, "IsOfType", self.Controler.IsOfType)
       
   182 
       
   183         self.ButtonSizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE)
       
   184         if wx.VERSION >= (2, 5, 0):
       
   185             self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.ButtonSizer.GetAffirmativeButton().GetId())
       
   186             self.Preview.Bind(wx.EVT_PAINT, self.OnPaint)
       
   187         else:
       
   188             self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.ButtonSizer.GetChildren()[0].GetSizer().GetChildren()[0].GetWindow().GetId())
       
   189             wx.EVT_PAINT(self.Preview, self.OnPaint)
       
   190         
       
   191         self._init_sizers()
       
   192 
       
   193     def __init__(self, parent, controler):
       
   194         self.Controler = controler
       
   195         self._init_ctrls(parent)
       
   196         self.BlockName.SetValue("")
       
   197         self.BlockName.Enable(False)
       
   198         self.Inputs.Enable(False)
       
   199         self.Block = None
       
   200         self.MinBlockSize = None
       
   201         self.First = True
       
   202         
       
   203         self.PouNames = []
       
   204         self.PouElementNames = []
       
   205     
       
   206     def SetPreviewFont(self, font):
       
   207         self.Preview.SetFont(font)
       
   208     
       
   209     def FindTreeItem(self, root, name, inputs = None):
       
   210         if root.IsOk():
       
   211             pydata = self.TypeTree.GetPyData(root)
       
   212             type_inputs = pydata.get("inputs", None)
       
   213             type_extension = pydata.get("extension", None)
       
   214             if inputs is not None and type_inputs is not None:
       
   215                 if type_extension is not None:
       
   216                     same_inputs = type_inputs == inputs[:type_extension]
       
   217                 else:
       
   218                     same_inputs = type_inputs == inputs
       
   219             else:
       
   220                 same_inputs = True
       
   221             if self.TypeTree.GetItemText(root) == name and same_inputs:
       
   222                 return root
       
   223             else:
       
   224                 if wx.VERSION < (2, 6, 0):
       
   225                     item, root_cookie = self.TypeTree.GetFirstChild(root, 0)
       
   226                 else:
       
   227                     item, root_cookie = self.TypeTree.GetFirstChild(root)
       
   228                 while item.IsOk():
       
   229                     result = self.FindTreeItem(item, name, inputs)
       
   230                     if result:
       
   231                         return result
       
   232                     item, root_cookie = self.TypeTree.GetNextChild(root, root_cookie)
       
   233         return None
       
   234     
       
   235     def OnOK(self, event):
       
   236         selected = self.TypeTree.GetSelection()
       
   237         block_name = self.BlockName.GetValue()
       
   238         name_enabled = self.BlockName.IsEnabled()
       
   239         if not selected.IsOk() or self.TypeTree.GetItemParent(selected) == self.TypeTree.GetRootItem() or selected == self.TypeTree.GetRootItem():
       
   240             message = wx.MessageDialog(self, _("Form isn't complete. Valid block type must be selected!"), _("Error"), wx.OK|wx.ICON_ERROR)
       
   241             message.ShowModal()
       
   242             message.Destroy()
       
   243         elif name_enabled and block_name == "":
       
   244             message = wx.MessageDialog(self, _("Form isn't complete. Name must be filled!"), _("Error"), wx.OK|wx.ICON_ERROR)
       
   245             message.ShowModal()
       
   246             message.Destroy()
       
   247         elif name_enabled and not TestIdentifier(block_name):
       
   248             message = wx.MessageDialog(self, _("\"%s\" is not a valid identifier!")%block_name, _("Error"), wx.OK|wx.ICON_ERROR)
       
   249             message.ShowModal()
       
   250             message.Destroy()
       
   251         elif name_enabled and block_name.upper() in IEC_KEYWORDS:
       
   252             message = wx.MessageDialog(self, _("\"%s\" is a keyword. It can't be used!")%block_name, _("Error"), wx.OK|wx.ICON_ERROR)
       
   253             message.ShowModal()
       
   254             message.Destroy()
       
   255         elif name_enabled and block_name.upper() in self.PouNames:
       
   256             message = wx.MessageDialog(self, _("\"%s\" pou already exists!")%block_name, _("Error"), wx.OK|wx.ICON_ERROR)
       
   257             message.ShowModal()
       
   258             message.Destroy()
       
   259         elif name_enabled and block_name.upper() in self.PouElementNames:
       
   260             message = wx.MessageDialog(self, _("\"%s\" element for this pou already exists!")%block_name, _("Error"), wx.OK|wx.ICON_ERROR)
       
   261             message.ShowModal()
       
   262             message.Destroy()
       
   263         else:
       
   264             self.EndModal(wx.ID_OK)
       
   265 
       
   266     def SetBlockList(self, blocktypes):
       
   267         if wx.Platform == '__WXMSW__':
       
   268             root = self.TypeTree.AddRoot(_("Block Types"))
       
   269         else:
       
   270             root = self.TypeTree.AddRoot("")
       
   271         self.TypeTree.SetPyData(root, {"type" : CATEGORY})
       
   272         for category in blocktypes:
       
   273             category_name = category["name"]
       
   274             category_item = self.TypeTree.AppendItem(root, _(category_name))
       
   275             self.TypeTree.SetPyData(category_item, {"type" : CATEGORY})
       
   276             for blocktype in category["list"]:
       
   277                 blocktype_item = self.TypeTree.AppendItem(category_item, blocktype["name"])
       
   278                 block_data = {"type" : BLOCK, 
       
   279                               "inputs" : tuple([type for name, type, modifier in blocktype["inputs"]]),
       
   280                               "extension" : None}
       
   281                 if blocktype["extensible"]:
       
   282                     block_data["extension"] = len(blocktype["inputs"])
       
   283                 self.TypeTree.SetPyData(blocktype_item, block_data)
       
   284         if wx.Platform == '__WXMSW__':
       
   285             self.TypeTree.Expand(root)
       
   286 
       
   287     def SetMinBlockSize(self, size):
       
   288         self.MinBlockSize = size
       
   289 
       
   290     def SetPouNames(self, pou_names):
       
   291         self.PouNames = [pou_name.upper() for pou_name in pou_names]
       
   292         
       
   293     def SetPouElementNames(self, element_names):
       
   294         self.PouElementNames = [element_name.upper() for element_name in element_names]
       
   295         
       
   296     def SetValues(self, values):
       
   297         blocktype = values.get("type", None)
       
   298         if blocktype is not None:
       
   299             inputs = values.get("inputs", None)
       
   300             item = self.FindTreeItem(self.TypeTree.GetRootItem(), blocktype, inputs)
       
   301             if item:
       
   302                 self.TypeTree.SelectItem(item)
       
   303         for name, value in values.items():
       
   304             if name == "name":
       
   305                 self.BlockName.SetValue(value)
       
   306             elif name == "extension":
       
   307                 self.Inputs.SetValue(value)
       
   308             elif name == "executionOrder":
       
   309                 self.ExecutionOrder.SetValue(value)
       
   310             elif name == "executionControl":
       
   311                    self.ExecutionControl.SetValue(value)
       
   312         self.RefreshPreview()
       
   313 
       
   314     def GetValues(self):
       
   315         values = {}
       
   316         item = self.TypeTree.GetSelection()
       
   317         values["type"] = self.TypeTree.GetItemText(item)
       
   318         values["inputs"] = self.TypeTree.GetPyData(item)["inputs"]
       
   319         if self.BlockName.GetValue() != "":
       
   320             values["name"] = self.BlockName.GetValue()
       
   321         values["width"], values["height"] = self.Block.GetSize()
       
   322         values["extension"] = self.Inputs.GetValue()
       
   323         values["executionOrder"] = self.ExecutionOrder.GetValue()
       
   324         values["executionControl"] = self.ExecutionControl.GetValue()
       
   325         return values
       
   326 
       
   327     def OnTypeTreeItemSelected(self, event):
       
   328         self.BlockName.SetValue("")
       
   329         selected = event.GetItem()
       
   330         pydata = self.TypeTree.GetPyData(selected)
       
   331         if pydata["type"] != CATEGORY:
       
   332             blocktype = self.Controler.GetBlockType(self.TypeTree.GetItemText(selected), pydata["inputs"])
       
   333             if blocktype:
       
   334                 self.Inputs.SetValue(len(blocktype["inputs"]))
       
   335                 self.Inputs.Enable(blocktype["extensible"])
       
   336                 self.BlockName.Enable(blocktype["type"] != "function")
       
   337                 comment = blocktype["comment"]
       
   338                 self.TypeDesc.SetValue(_(comment) + blocktype.get("usage", ""))
       
   339                 wx.CallAfter(self.RefreshPreview)
       
   340             else:
       
   341                 self.BlockName.Enable(False)
       
   342                 self.Inputs.Enable(False)
       
   343                 self.Inputs.SetValue(2)
       
   344                 self.TypeDesc.SetValue("")
       
   345                 wx.CallAfter(self.ErasePreview)
       
   346         else:
       
   347             self.BlockName.Enable(False)
       
   348             self.Inputs.Enable(False)
       
   349             self.Inputs.SetValue(2)
       
   350             self.TypeDesc.SetValue("")
       
   351             wx.CallAfter(self.ErasePreview)
       
   352         event.Skip()
       
   353 
       
   354     def OnNameChanged(self, event):
       
   355         if self.BlockName.IsEnabled():
       
   356             self.RefreshPreview()
       
   357         event.Skip()
       
   358     
       
   359     def OnInputsChanged(self, event):
       
   360         if self.Inputs.IsEnabled():
       
   361             self.RefreshPreview()
       
   362         event.Skip()
       
   363     
       
   364     def OnExecutionOrderChanged(self, event):
       
   365         self.RefreshPreview()
       
   366         event.Skip()
       
   367     
       
   368     def OnExecutionControlChanged(self, event):
       
   369         self.RefreshPreview()
       
   370         event.Skip()
       
   371     
       
   372     def ErasePreview(self):
       
   373         dc = wx.ClientDC(self.Preview)
       
   374         dc.Clear()
       
   375         self.Block = None
       
   376         
       
   377     def RefreshPreview(self):
       
   378         dc = wx.ClientDC(self.Preview)
       
   379         dc.SetFont(self.Preview.GetFont())
       
   380         dc.Clear()
       
   381         item = self.TypeTree.GetSelection()
       
   382         if item.IsOk():
       
   383             pydata = self.TypeTree.GetPyData(item)
       
   384             if pydata["type"] == CATEGORY:
       
   385                 self.Block = None
       
   386             else:
       
   387                 blocktype = self.TypeTree.GetItemText(item)
       
   388                 if blocktype:
       
   389                     self.Block = FBD_Block(self.Preview, blocktype, 
       
   390                             self.BlockName.GetValue(), 
       
   391                             extension = self.Inputs.GetValue(), 
       
   392                             inputs = pydata["inputs"], 
       
   393                             executionControl = self.ExecutionControl.GetValue(), 
       
   394                             executionOrder = self.ExecutionOrder.GetValue())
       
   395                     width, height = self.MinBlockSize
       
   396                     min_width, min_height = self.Block.GetMinSize()
       
   397                     width, height = max(min_width, width), max(min_height, height)
       
   398                     self.Block.SetSize(width, height)
       
   399                     clientsize = self.Preview.GetClientSize()
       
   400                     x = (clientsize.width - width) / 2
       
   401                     y = (clientsize.height - height) / 2
       
   402                     self.Block.SetPosition(x, y)
       
   403                     self.Block.Draw(dc)
       
   404                 else:
       
   405                     self.Block = None
       
   406 
       
   407     def OnPaint(self, event):
       
   408         if self.Block is not None:
       
   409             self.RefreshPreview()
       
   410         event.Skip()