Peter's Blog

Redefining the Impossible

Items filed under wxpython


A short reminder to self on how to get a file open dialog running under python on Windows:

>> import win32ui
>> o = win32ui.CreateFileDialog( 1, ".txt", "default.txt", 0,
"Text Files (*.txt)|*.txt|All Files (*.*)|*.*|")
>> o.DoModal()
>> o.GetPathName()
c:\\blah.txt

This uses the python windows stuff that is pretty much essential for python on windows anyway. I'm trying to avoid using wxPython from now on as it's essentially an MFC/Win32api clone and has inherited verbose messy api that changes with each release.

Notes:

  • the 0 is a flag field and can have values from win32con module.
  • The filter term needs an extra | character at the end to stop garbage in the combo.
  • Works interactively from console.
  • ActiveState has some online documentation for the python win32 stuff here. It's a cursory but better than nothing.

Filed under: python wxpython

2 Comments

I've been trying Wing IDE a development environment for Python. I've tried some in the past and always fell back to using VIM and print statements:

IDLE
cannot debug GUI applications (IDLEfork might, haven't tried it)
PythonWin
cannot debug GUI applications, doesn't appear to have evolved in the last few years
Boa Constructor
haven't tried it for a while, back then it was flaky and didn't work with the contemporary wxPython (which is a moving target anyway). Recent reports say the debugger is still flaky.
pdb
python's own debugger, command line based sad

I was interested in Wing IDE (WingIDE?) as it would be nice to have a decent debugger. Simple things like:

  • double click exception stack dump to take me to point of exception in source
  • breakpoints (!)
  • single step

I tried the cheap 'personal' edition and here's a potted review:

  • on pentium 2 450M with 256M ram on windows XP it is very sluggish. It uses 40M of memory so it is probably swapping. On 1.5G Centrino with 512M it is much more useable. Single steps still take a second or so each (like walking through mud). When it runs the target the target runs at full speed. The help file makes mention of it being slow when it is loading symbols on startup but the slowness I see effects it all the time.
  • editor is ok in a Visual Studio kind of way. It's not VIM but I can still use VIM in the same way that I still use it with Visual Studio. Write code in VIM, correct it in IDE.
  • editor has auto-completion (fills in the rest of an identifier for you) but it's kinda slow to come up and VIM has a way to do this that is simplistic but useful (CTRL-N for the uninitiated).
  • first project I ran generated an exception in the startup code, something mysterious in Swig generated code. Wing is supposed to intelligently tell the difference between exceptions that you trap yourself in your code and ones that you don't trap: presumably it only drops into the debugger on an unhandled exception. This particular exception was in the Swig stuff so it may have been under some C code, in which case the exception always stops in the debugger (in Wing terminology this is a 'false positive'). However, Wing has a useful option to learn which exceptions you want to always ignore so I set this up and was not bothered by it again. The details of the exception were presumably stored in the project file.
  • IDE and debugger works as you would expect from standards set by Turbo C 15 years ago.
  • in the personal edition, when the debugger comes in you cannot evaluate expressions or examine variables, you can only look at the values of variables in the stack trace. There is a python shell but it is independent from the script you are debugging so it is only useful for experiments.
  • from visual studio I am used to hovering the cursor over a variable name and seeing it's value in a tool tip, or being able to right click and see it's value. Wing does not do this, it seem you have to look through the stack trace to find the value of variable.
  • coolest feature is remote debugging. You can edit a script to add a line to import a Wing module and from then on, if the script raises an exception it will find the Wing IDE and you can start debugging. This is useful if, for example, you are debugging CGI scripts that are launched from Apache: the scripts can find Wing IDE and you can debug them, they don't have to be launched from Wing IDE itself.
  • it works fine debugging wxPython and has been solid so far.
  • I left it running a program for an hour. The program generated lots of print output which was caught and displayed in the debugger. After an hour the debugger was using over 100M of ram: it doesn't seem to want to limit the size of the debug buffer. Maybe there is an option to stop this but it's odd that it should do this by default.

Conclusion: I can live with it's lethargy just to have a useable debugger. I'm still on the free trial, time will tell if I want to shell out $35 for the personal edition (less than £20, not bad).

There are other open source alternatives I haven't looked at:

spe: looks like work in progress

Eclipse: written in java, mainly for java


3 Comments

Uploading images to this blog can be kinda fiddly. Say I have an image in the windows clipboard (e.g. a screen clip from OneNote and I want it on the blog, I have to fire up a graphics program, paste it in, save it to a file, fire up filezilla, upload it, delete the image file.

This little script does all this for me. It asks for a file name, writes the image in the clipboard to the file, uploads it and deletes the image. Job Done.

The uploading is done using pscp, a version of scp from the putty camp. It uses my putty private key so I don't have to give a password for access to the server (I didn't use the cygwin version of ssh as it didn't like having 'c:\' in file names). The script uses the Python Imaging Library to do the grabbing and wxPython to ask for the file name.

   1  #
   2  # Upload image in clipboard to server
   3  #
   4  import wx
   5  import ImageGrab
   6  import Image
   7  import os
   8  import tempfile
   9  
  10  class MyApp(wx.App):
  11      def OnInit(self):
  12          oImage = ImageGrab.grabclipboard()
  13  
  14          if not isinstance( oImage, Image.Image):
  15              #
  16              # In no way is wxPython an easy to use library.
  17              #
  18              dlg = wx.MessageDialog( None, 'No image in clipboard', 'Error', wx.OK)
  19              dlg.ShowModal()
  20              dlg.Destroy()
  21              return True
  22  
  23          dlg = wx.TextEntryDialog( None, 'Enter a file name', 'Upload Clipboard', '')
  24          if dlg.ShowModal() != wx.ID_OK:
  25              dlg.Destroy()
  26              return True
  27  
  28          strName = dlg.GetValue()
  29          dlg.Destroy()
  30  
  31          strTempFile = tempfile.mktemp( strName)
  32          oImage.save( open( strTempFile, 'wb'))
  33  
  34          strCmd = 'pscp -q -i "c:\my documents\puttykey.ppk" %s me@myserver.com:www/images/%s' % (strTempFile, strName)
  35  
  36          os.system( strCmd)
  37  
  38          os.unlink( strTempFile)
  39  
  40          return True
  41  
  42  app = MyApp(0)
  43  app.MainLoop()
Toggle Line Numbers

This is the result of alt-print screen and running this script:

images/grab.jpg

The script itself inside VIM

ToDo:

  • handle file names in clipboard
  • detect upload errors
  • allow resizing images

4 Comments

Been playing with Pythoncard. This is a wrapper library for wxpython that simplifies using it somewhat. The best feature of it is probably the resource manager that provides a visual GUI editor for dropping controls and assigning properties, vb style. It is clean and stable.

I've written a utility that uses:

and it all works together sweetly. The application generates an html report and uses the HtmlWindow control to display it an also print it, complete with graphics.

Problems encountered:

  • The BitmapCanvas component is supposed to support the display of PIL images directly but it did not work for me. I had to hack bitmapcanvas.py as follows:
       1      def drawBitmapScaled(self, aBitmap, xy, size, transparency=1):
       2          if isinstance(aBitmap, graphic.Bitmap):
       3              img = wx.ImageFromBitmap(aBitmap.getBits())
       4          elif isinstance(aBitmap, wx.Bitmap):
       5              img = wx.ImageFromBitmap(aBitmap)
       6          elif isinstance(aBitmap, wx.Image):
       7              img = aBitmap
       8          # pcw elif PIL_FOUND and isinstance(aBitmap, Image.Image):
       9          elif PIL_FOUND:
      10              img = graphic.PILToImage(aBitmap)
      11          elif NUMERIC_FOUND and isinstance(aBitmap, ArrayType):
      12              img = graphic.numericArrayToImage(aBitmap)
      13          else:
      14              return
    
    Toggle Line Numbers
    The isinstance call was returning PIL.Image.Image so the above test was failing. I put a similar change in DrawBitmap
  • The HtmlWindow control does not seem to support coloured fonts. The following:
    <span style="color:#ff0000;">Bin It</span>.
    
    comes out black instead of red. Maybe it doesn't support .CSS.

Filed under: cheetah python wxpython


I love Python and VIM and I always write the former using the latter. All the scripts I write are GUI-less tools, I don't seem to need GUIs. Something that helps me in this, and todays tip, is the runscript plugin. It does this for me:

  • Maps F11 to mark the python script in the current window as one to execute.
  • Maps F9 to open an 'output' window.
  • Maps F12 to run the script chosen by F11 and dump the output from stdout and stderr in the output window.

So the working cycle is:

  • Open or type out python script
  • Press F11 to mark script
  • Press F9 to open output window
  • Press F12 to run script and see results in output window
  • Edit script
  • Press F12
  • Edit script
  • Press F12
  • etc

I have been using this for a few years. Working like this I don't need GUI's, the output window is all I need to see the script results. I can scroll up and down it or copy text from it.

I've tried PythonWin, IDLE, Boa Constructor, Notepad, Scintilla etc and nothing compares. Because the script is not launched from a GUI environment the script is free to launch it's own GUI in wxPython for example.

The main downside of using VIM as an IDE is that I can't use a debugger: I cannot put breakpoints on lines. I do all my debugging with print statements. I could use the python command line debugger but I've never bothered to learn how.

UPDATE: after enthusing about this script I find that it does not work in text mode Vim on Gentoo linux and possibly others so I've fixed it. The problems were:

  • The path to the executable was hard-wired for windows
  • The "Output window" appears to be called "Scratch" in text mode linux version.
" Vim global plugin for running Python scripts
" Version: 1.1
" Maintainer: Frederick Young <lordfyb@hotmail.com>
" Last change: 2001 Oct 30 
"
"*******************************************************************************
"
" --Pressing the "F12" key will run the Python script in the current buffer
"  
" --Pressing the "F11" key will designate the Python script in the current
"   buffer as the script to run when pressing "F12"
"   
" --Pressing "Shift-F11" will remove the designation performed by pressing "F11"
"   and will allow pressing "F12" to run the script in the current buffer
"   
" --Typing the Ex command "Rs" will also allow you to run a Python script but
"   with the benefit of allowing you to pass up to 20 parameters to the script.
"   
"   Sample syntax...
"
"       :rs -xy 800 600
"
"   The above will run the script in the current buffer or the one designated
"   to run when pressing "F11" and pass to it the parameters "-xy 800 600"
"
" --Pressing "F9" will toggle the display of a buffer containing the output
"   produced when running the Ex command "Rs" or pressing "F12".
"
"   With some minor modifications, it should be possible to run other scripts
"   like perl, vbscript, or even executables compiled with the Ex command
"   "make".
"
"*******************************************************************************


" Change this variable to reflect the path of the Python executable on your
" system.  If your system knows how to find Python then setting this variable
" to 'python' should be enough.  Otherwise, type the complete path to the
" executable.
let s:PathToExecutable = 'python'

" Used to designate the Python script to run when pressing "F12" or typing the
" Ex command "Rs"
let s:mainfile = ""

let s:flag = 0
let @a = ""

" Map keys to function calls
if !hasmapto('<Plug>RunScript')
  nmap <unique> <silent> <F12> <Plug>ExecuteScript
endif
if !hasmapto('<Plug>SetMainScript')
  nmap <unique> <silent> <F11> <Plug>SetMainScript
endif
if !hasmapto('<Plug>ClearMainScript')
  nmap <unique> <silent> <S-F11> <Plug>ClearMainScript
endif
if !hasmapto('<Plug>ToggleOutputWindow')
  nmap <unique> <silent> <F9> <Plug>ToggleOutputWindow
endif

" The main plug-in mapping.
nmap <script> <silent> <Plug>ExecuteScript :call <SID>ExecuteScript()<CR>
nmap <script> <silent> <Plug>SetMainScript :call <SID>SetMainScript()<CR>
nmap <script> <silent> <Plug>ClearMainScript :call <SID>ClearMainScript()<CR>
nmap <script> <silent> <Plug>ToggleOutputWindow :call <SID>ToggleOutputWindow()<CR>

function s:SetMainScript()
    let s:mainfile = bufname('%')
    echo s:mainfile . ' set as the starting program.'
endfunction

function s:ClearMainScript()
    echo s:mainfile . ' is no longer the starting program.'
    let s:mainfile = ""
endfunction

function s:ExecuteScript()
    " Execute script
    if strlen(s:mainfile) > 0
        let @a = system(s:PathToExecutable . ' ' . s:mainfile)
    else
        let @a = system(s:PathToExecutable . ' ' . bufname('%'))
    endif
    "bdelete Scratch
    call s:ShowOutputInBuffer()
endfunction

function s:ToggleOutputWindow()
    if s:flag == 0
        let s:flag = 1
        " pcw
        " silent rightbelow new Scratch
        silent rightbelow new Scratch
        resize 7
        set buftype=nofile
        silent normal "aP
    elseif s:flag == 1
        let s:flag = 0
        if bufexists(Scratch) > 0
            silent! bwipeout Scratch
        endif
        let @a = ""
    endif
endfunction

" Ex command which take 0 or more ( up to 20 ) parameters
command -nargs=* Rs call s:CLExecuteScript(<f-args>)

function s:CLExecuteScript(...)
    let index = 1
    let params = ""
    " Assemble the paramters to pass to the Python script
    while index <= a:0
        execute 'let params = params . " " . a:' . index
        let index = index + 1
    endwhile
    " Execute script (with parameters if provided)
    if strlen(s:mainfile) > 0
        let @a = system(s:PathToExecutable . ' ' . s:mainfile . params)
    else
        let @a = system(s:PathToExecutable . ' ' . bufname('%') . params)
    endif
    "bdelete Scratch
    call s:ShowOutputInBuffer()
endfunction

" Display a buffer containing the contents of register "a"
function s:ShowOutputInBuffer()
    if s:flag == 1
        if bufexists("Scratch") > 0
            silent! bwipeout Scratch
        endif
        silent botright new Scratch
        resize 7
        set buftype=nofile
        silent normal "aP
    endif
endfunction

12 Comments

Using Bit Torrent to download the lastest Knoppix CD. It's coming down at about 60KB/s which is as high as I ever see. It's uploading at 8k/s so I'm doing someone a favour.

It is intersting in that it is a python application. It normally uses wxPython for it's gui and it installs only the following files:

  • bittorrent.ico
  • btdownloadgui.exe
  • datetime.pyd
  • library.zip
  • LICENSE.txt
  • python23.dll
  • redirdonate.html
  • select.pyd
  • uninstall.exe
  • w9xpopen.exe
  • wxc.pyd
  • wxmsw24h.dll
  • zlib.pyd
  • _socket.pyd
  • _sre.pyd
  • _ssl.pyd
  • _winreg.pyd

Interesting, just the dll's that are necessary. It does make it big though, this lot adds up to 10M, mostly unused library code.

The source includes a headless version that runs a treat on the Gentoo box so I have no qualms about leaving it running to upload to my peers. There is a Gentoo ebuild for bitTorrent but that has a whole lot of dependencies, including gtk and wxPython, which it wanted to build from source.

Interesting, just the dll's that are necessary. It does make it big though, this lot adds up to 10M, mostly unused library code.

The source includes a headless version that runs a treat on the Gentoo box so I have no qualms about leaving it running to upload to my peers. There is a Gentoo ebuild for bitTorrent but that has a whole lot of dependencies, including gtk and wxPython, which it wanted to build from source.


2 Comments

I knocked up a little Rest preview tool using wxPython, a text control and a IE control. It has two tabs, one to enter text and a second that shows the DocUtils output. I hope it will take some of the guesswork out of composing ReST 1 documents. It might also inspire me 2

Apart from the tab character being about 3 inches across, the other problem is how to enter tab characters? Tab is taking it's 'goto next control' role.

[1]

ReStructured Text http://docutils.sourceforge.net.

[2]

To use features like footnotes.


Filed under: untagged wxpython

2 Comments