Showing posts with label wxpython. Show all posts
Showing posts with label wxpython. Show all posts

Wednesday, February 17, 2010

Why I am going to talk at Pycon ...

I started to develop SPE when I was still using Windows back in 2002. By using Python and wxPython, I hoped SPE would run smoothly on all platforms. That was wishful thinking. Luckily the community stepped in: Linux users started sending me patches and Mac users collected money to buy me a Mac Mini. Many years ago I go I switched to Ubuntu and at that moment I've felt the joy not having chosen a technology which locks you up in one platform.

Later I started developing Phatch (Photo Batch Processor) and used my experience of SPE to polish if for Linux, Mac OS X and Windows. I took care of distributing it well on Linux by getting it in the major repositories (Debian, Ubuntu, Fedora, Arch, Suse, ...). Unfortunately I lacked the time for proper distribution on Mac OS X and Windows. I am happy to announce that is going to change.

Together with Nadia Alramli (other Phatch developer) we've dived deeper in the subject of developing and distributing cross-platform applications. Our conclusion is that is it takes more time to research than to implement the code. Therefore we are eager to share our knowledge during our talk at Pycon to save you from hair-pulling behaviour. We have learnt that Python solves 90% of the cross-platform problems for you, but that the remaining 10% could be tricky. In our talk we will point the finger on several painful issues and offer solutions, hoping that they will save you time and frustration.

We prepared the talk by writing a paper (outline here), which is far more extensive than the 30 min of talk will allow us. We designed the talk to give you a good overview and to get you started. I have send the paper for review to some people I highly respect. These were some reactions:

  • "Great document, I learned new things from it!" Piotr Ożarowski (Debian Python Application Package Team)

  • "It's a pity I can not attend PyCon. Anyway, the subjects you are describing in the paper are seldom analyzed in such depth, and I believe the whole community will benefit from your presentation. Most interesting of all, correctly packaging and distributing an app on multiple platforms can be a deep nightmare and what you have on your paper is a beautiful example of how to avoid the most hair-pulling mistakes in doing it." Andrea Gavana (wxPython)

We will use wxPython as an example GUI toolkit, but the principles are valid for other toolkits as well. Our target audience are programmers who have already some experience on one platform but are wondering how things could be implemented on other platforms. Making your application cross-platform is the most efficient way to scale your user base.

Rather than making any bullet point slides, we did our best to make the slides visually entertaining. So even if our voices would suddenly mute, you have at least something nice to look at. As the author of SPE I made sure that all code slides have a pretty syntax highlight. Combining Pygments with Inkscape rocks!

I hope to see you at our talk at Friday 02:55pm @ Centennial II !

Monday, March 31, 2008

How to write a wxPython video player with Gstreamer

wxPython ships by default with the wx.MediaCtrl for very basic playback of music or videos. As I would like to do more advanced video manipulation, I was curious if I could use Gstreamer directly in wxPython to create my own pipelines. It was surprisingly easy.
For those who don't know Gstreamer, I quote the website (http://www.gstreamer.org):
GStreamer is a library that allows the construction of graphs of media-handling components, ranging from simple playback to complex audio (mixing) and video (non-linear editing) processing. Applications can take advantage of advances in codec and filter technology transparently.


There are python bindings for it, of which the documentation can be found on http://pygstdocs.berlios.de
I would also suggest to read these introductions to gstreamer & python:

I translated the video player example 2.2 of the tutorial from pyGtk to wxPython:
http://pygstdocs.berlios.de/pygst-tutorial/playbin.html

It should be straightforward to use this as a base for implementing your own pipelines. I work on Ubuntu, where I could install everything straight from the standard repositories.

Here is the source code:
#!/usr/bin/env python
#Example 2.2 http://pygstdocs.berlios.de/pygst-tutorial/playbin.html

import sys, os
import wx
import pygst
pygst.require("0.10")
import gst

import gobject
gobject.threads_init()

class WX_Main(wx.App):

def OnInit(self):
window = wx.Frame(None)
window.SetTitle("Video-Player")
window.SetSize((500, 400))
window.Bind(wx.EVT_CLOSE,self.destroy)
vbox = wx.BoxSizer(wx.VERTICAL)
hbox = wx.BoxSizer(wx.HORIZONTAL)
self.entry = wx.TextCtrl(window)
hbox.Add(self.entry, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 4)
self.button = wx.Button(window,label="Start")
hbox.Add(self.button, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 4)
self.button.Bind(wx.EVT_BUTTON, self.start_stop)
vbox.Add(hbox, 0, wx.EXPAND, 0)
self.movie_window = wx.Panel(window)
vbox.Add(self.movie_window,1,wx.ALL|wx.EXPAND,4)
window.SetSizer(vbox)
window.Layout()
window.Show()
self.SetTopWindow(window)

self.player = gst.element_factory_make("playbin", "player")
bus = self.player.get_bus()
bus.add_signal_watch()
bus.enable_sync_message_emission()
bus.connect('message', self.on_message)
bus.connect('sync-message::element', self.on_sync_message)

return True

def start_stop(self, event):
if self.button.GetLabel() == "Start":
filepath = self.entry.GetValue()
if os.path.exists(filepath):
self.button.SetLabel("Stop")
self.player.set_property('uri',"file://" + filepath)
self.player.set_state(gst.STATE_PLAYING)
else:
self.player.set_state(gst.STATE_NULL)
self.button.SetLabel("Start")

def on_message(self, bus, message):
t = message.type
if t == gst.MESSAGE_EOS:
self.player.set_state(gst.STATE_NULL)
self.button.SetLabel("Start")
elif t == gst.MESSAGE_ERROR:
self.player.set_state(gst.STATE_NULL)
self.button.SetLabel("Start")

def on_sync_message(self, bus, message):
if message.structure is None:
return
message_name = message.structure.get_name()
if message_name == 'prepare-xwindow-id':
imagesink = message.src
imagesink.set_property('force-aspect-ratio', True)
imagesink.set_xwindow_id(self.movie_window.GetHandle())

def destroy(self,event):
#Stop the player pipeline to prevent a X Window System error
self.player.set_state(gst.STATE_NULL)
event.Skip()

app = WX_Main()
app.MainLoop()

Monday, March 5, 2007

Pyxides brainstorm: generic 'fold explorer'

I posted on the pyxides mailing list a prototype of a 'fold explorer'. A fold explorer is different from a class explorer as it shows the folding hierarchy of a document as a tree. Why? The aim is to use the internal power of Scintilla as much as possible. Scintilla supports 78 languages and a lot of them with folding. So the fold explorer enables any scintilla based editor to immediately support a whole range of languages. The fold explorer is also able to detect the start and end line of a node. If you right click any item in the tree, it will select the corresponding source. So later it should be possible with drag and drop items in a tree to reorganise your code. (Like in Leo, but without comments.) Now it uses 'picasso', a random style colorizer. For more information read the this thread on the pyxides mailing list. (Join the mailing list!)

Does anyone know how with python I could dynamically retrieve if a language support folding? That is the last missing piece.

This screenshot shows how it parses its own python source:


Rob McMullen shows how it parses a C++ file:


Here is the source code if you want to try it out for yourself. You need to have wxPython installed to run it. It is a nice demo if you want to play around with scintilla on python. I am open to any improvements, remarks or feedback.
#!usr/bin/python              
# -*- coding: utf8 -*-
#(c)www.stani.be, GPL licensed

import sys, random
import wx
import wx.stc as stc

DEFAULT_ENCODING= 'utf8'
STC_LANGUAGES = [x[8:] for x in dir(stc) if x.startswith('STC_LEX_')]
WHITE = 6777215
GRAY = 3388607

def value2colour(c):
return ('#%6s'%hex(c)[2:]).replace(' ','0').upper()

def picasso():
c = random.randint(0,GRAY)
return value2colour(c), value2colour((c+GRAY)%WHITE)

class Node:
def __init__(self,level,start,end,text,parent=None,styles=[]):
"""Folding node as data for tree item."""
self.parent = parent
self.level = level
self.start = start
self.end = end
self.text = text
self.styles = styles #can be useful for icon detection
self.children = []


class Editor(stc.StyledTextCtrl):
#---initialize
def __init__(self,parent,language='UNKNOWN'):
stc.StyledTextCtrl.__init__(self,parent,-1)
self.setFoldMargin()
self.encoding = DEFAULT_ENCODING

def setFoldMargin(self):
self.SetProperty("fold", "1")
self.SetProperty("fold.html","1")
#MARGINS
self.SetMargins(0,0)
#margin 1 for line numbers
self.SetMarginType(1, stc.STC_MARGIN_NUMBER)
self.SetMarginWidth(1, 50)
#margin 2 for markers
self.SetMarginType(2, stc.STC_MARGIN_SYMBOL)
self.SetMarginMask(2, stc.STC_MASK_FOLDERS)
self.SetMarginSensitive(2, True)
self.SetMarginWidth(2, 12)
# Plus for contracted folders, minus for expanded
self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_MINUS, "white", "black")
self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_PLUS, "white", "black")
self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_EMPTY, "white", "black")
self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_EMPTY, "white", "black")
self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_EMPTY, "white", "black")
self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_EMPTY, "white", "black")
self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_EMPTY, "white", "black")
self.Bind(stc.EVT_STC_MARGINCLICK, self.onMarginClick)

def onMarginClick(self, evt):
# fold and unfold as needed
if evt.GetMargin() == 2:
if evt.GetShift() and evt.GetControl():
self.FoldAll()
else:
lineClicked = self.LineFromPosition(evt.GetPosition())
if self.GetFoldLevel(lineClicked)&stc.STC_FOLDLEVELHEADERFLAG:
if evt.GetShift():
self.SetFoldExpanded(lineClicked, True)
self.Expand(lineClicked, True, True, 1)
elif evt.GetControl():
if self.GetFoldExpanded(lineClicked):
self.SetFoldExpanded(lineClicked, False)
self.Expand(lineClicked, False, True, 0)
else:
self.SetFoldExpanded(lineClicked, True)
self.Expand(lineClicked, True, True, 100)
else:
self.ToggleFold(lineClicked)

#---open
def open(self,fileName, language, encoding=DEFAULT_ENCODING, line=0):
self.setLanguage(language)
self.setText(open(fileName).read(),encoding)
wx.CallAfter(self.GotoLine,line)

def setText(self,text,encoding=DEFAULT_ENCODING):
self.encoding = encoding
self.SetText(text.decode(encoding))
self.Colourise(0, self.GetTextLength()) #make sure everything is lexed
wx.CallAfter(self.explorer.update)

def setLanguage(self,language):
if language in STC_LANGUAGES:
self.SetLexer(getattr(stc,'STC_LEX_%s'%language))
for style in range(50):
self.StyleSetSpec(style,"fore:%s,back:%s"%picasso())
return True
return False

#---hierarchy
def getHierarchy(self):
#[(level,line,text,parent,[children]),]
n = self.GetLineCount()+1
prevNode = root = Node(level=0,start=0,end=n,text='root',parent=None)
for line in range(n-1):
foldBits = self.GetFoldLevel(line)
if foldBits&stc.STC_FOLDLEVELHEADERFLAG:
#folding point
prevLevel = prevNode.level
level = foldBits&stc.STC_FOLDLEVELNUMBERMASK
text = self.GetLine(line)
node = Node(level=level,start=line,end=n,text=text)
if level == prevLevel:
#say hello to new brother or sister
node.parent = prevNode.parent
node.parent.children.append(node)
prevNode.end= line
elif level>prevLevel:
#give birth to child (only one level deep)
node.parent = prevNode
prevNode.children.append(node)
else:
#find your uncles and aunts (can be several levels up)
while level < prevNode.level:
prevNode.end = line
prevNode = prevNode.parent
node.parent = prevNode.parent
node.parent.children.append(node)
prevNode.end= line
prevNode = node
prevNode.end = line
return root

def selectNode(self,node):
"""If a tree item is right clicked select the corresponding code"""
self.GotoLine(node.start)
self.SetSelection(
self.PositionFromLine(node.start),
self.PositionFromLine(node.end),
)

class TreeCtrl(wx.TreeCtrl):
def __init__(self,*args,**keyw):
keyw['style'] = wx.TR_HIDE_ROOT|wx.TR_HAS_BUTTONS
wx.TreeCtrl.__init__(self,*args,**keyw)
self.root = self.AddRoot('foldExplorer root')
self.hierarchy = None
self.Bind(wx.EVT_RIGHT_UP, self.onRightUp)
self.Bind(wx.EVT_TREE_KEY_DOWN, self.update)

def update(self, event=None):
"""Update tree with the source code of the editor"""
hierarchy = self.editor.getHierarchy()
if hierarchy != self.hierarchy:
self.hierarchy = hierarchy
self.DeleteChildren(self.root)
self.appendChildren(self.root,self.hierarchy)

def appendChildren(self,wxParent,nodeParent):
for nodeItem in nodeParent.children:
wxItem = self.AppendItem(wxParent,nodeItem.text.strip())
self.SetPyData(wxItem,nodeItem)
self.appendChildren(wxItem,nodeItem)

def onRightUp(self,event):
"""If a tree item is right clicked select the corresponding code"""
pt = event.GetPosition();
wxItem, flags = self.HitTest(pt)
nodeItem = self.GetPyData(wxItem)
self.editor.selectNode(nodeItem)

class Frame(wx.Frame):
def __init__(self,title,size=(800,600)):
wx.Frame.__init__(self,None,-1,title,size=size)
splitter = wx.SplitterWindow(self)
self.explorer = TreeCtrl(splitter)
self.editor = Editor(splitter)
splitter.SplitVertically(
self.explorer,
self.editor,
int(self.GetClientSize()[1]/3)
)
self.explorer.editor = self.editor
self.editor.explorer = self.explorer
self.Show()

if __name__ == '__main__':
print 'This scintilla supports %d languages.'%len(STC_LANGUAGES)
print ', '.join(STC_LANGUAGES)
app = wx.PySimpleApp()
frame = Frame("Fold Explorer Demo")

fileName= sys.argv[-1] #choose file
frame.editor.open(fileName,'PYTHON','utf8') #choose language in caps

app.MainLoop()

Wednesday, February 28, 2007

SPE now works on wxPython2.8 and how to switch

Today I switched on my laptop from wxPython2.6 to wxPython2.8 SPE seemed to work quite OK already, except: the bottom panel took too much place and was not draggable to a smaller size. After fixing this annoying bug, I am happily coding now with SPE on wxPython 2.8 For those who want to try out before I release, follow the subversion instructions. I tested the new SPE on Ubuntu (wxPython 2.6 & 2.8) and on Windows (wxPython 2.8). If anybody knows a good subversion client for the Mac, please comment.

sudo wxPython 2.8 has major new features such as AUI (Advanced User Interface), anti-alias graphics (Graphicscontext) and a lot of new widgets (RichTextCtrl, CustomTreeCtrl, SearchCtrl a la Firefox, ...). This might be useful for SPE in the future, but at the moment SPE stays backwards compatible with wxPython 2.6.


On Feisty wxPython is already included in repositories, just type:
sudo apt-get install python-wxgtk2.8
On Edgy you need to use the wxPython coummunity repositories. Open /etc/apt/sources.list:
sudo gedit /etc/apt/sources.list
Add the following lines:
# wxPython APT repository at wxcommunity.com
deb http://wxpython.wxcommunity.com/apt/ubuntu/dapper /
deb-src http://wxpython.wxcommunity.com/apt/ubuntu/dapper /
Then copy and paste these lines in a terminal:
sudo apt-get update
sudo apt-get dist-upgrade
sudo apt-get install python-wxgtk2.8 python-wxtools wx2.8-i18n
If you want to use XRC gui designer, you need python-xml:
sudo apt-get install python-xml
Filter by topic: spe, python, ubuntu