#! /usr/bin/env python
################################################################################
## Python Object Orientated example by rowinggolfer ##
## the same object... but extra functionality added in layers ##
## ##
## A noteable "Gotcha" for newbs is the __init__ function. ##
## the base class in this example sets some important attributes in this ##
## function. Therefore classes which inherit from it, but be careful if ##
## re-implementing this function ##
## NOTE - the teenager class does NOT overwrite this function... hence when ##
## a teenager is initiated, python looks up the class hierarchy and executes ##
## the first __init__ method it finds.. in this case the Toddler's __init__ ##
## ##
## ##
## Run this script, and study the output. ##
## ##
## Pay special attention to the attribute "description, and see how it is ##
## altered mid method in the teenage years. ##
## ##
################################################################################
from datetime import date
from base64 import b64decode
class TimritBaby(object):
'''
a baby has a date of birth, a name, and limited functionality
'''
def __init__(self, date_of_birth):
print "TimritBaby initiated"
self.name = 'Scott Murtz'
self.dob = date_of_birth
self.description = "(a baby)"
def poop(self):
print " %s %s can Poop!"% (self.name, self.description)
class TimritToddler(TimritBaby):
'''
a toddler has a new method
'''
def __init__(self, dob):
# call the init method of the base class..
# otherwise the name attribute won't work!!
print "\nTimritToddler initiated"
TimritBaby.__init__(self, dob)
self.description = "(a toddler)"
def walk(self):
print " %s %s can Walk!"% (self.name, self.description)
class TimritTeenager(TimritToddler):
'''
a teenager with a secret habit
'''
def private_time(self):
self.description = "(now a teeneager)"
message = b64decode('aXMgbWFzdHVyYmF0aW5nIGFnYWlu')
print " %s %s %s"% (self.name, self.description, message)
if __name__ == "__main__":
baby = TimritBaby(date(1968,1,1))
baby.poop()
try:
baby.walk()
except AttributeError as e:
print "ERROR - WHOOPS!", e
teenager = TimritTeenager(date(1968,1,1))
teenager.poop()
teenager.walk()
teenager.private_time()
teenager.walk()
Random musings on GNU+Linux. open source software, and Python. The main purpose of this blog is to archive (for myself) what I am learning about these wonderful technologies/communities
Tuesday, 28 September 2010
Object Orientation Example
I knocked this example up today for a friend on IRC. Thought I would post it here.
Sunday, 12 September 2010
fuzzymatching in postgres on ubuntu
I had a battle getting the soundex function installed into my database on postgres.
but here's how I succeeded.
One - Install the postgres-contrib package
Two - change user to postgres (a database superuser created on install of postgresql
Three - pipe the supplied script into the database (my database is called "openmolar_demo")
Four - test it!
but here's how I succeeded.
One - Install the postgres-contrib package
sudo apt-get install postgresql-contrib
Two - change user to postgres (a database superuser created on install of postgresql
neil@slim-maroon:~$ sudo su - postgres postgres@slim-maroon:~$
Three - pipe the supplied script into the database (my database is called "openmolar_demo")
postgres@slim-maroon:~$ psql -d openmolar_demo -f /usr/share/postgresql/8.4/contrib/fuzzystrmatch.sql SET CREATE FUNCTION CREATE FUNCTION CREATE FUNCTION CREATE FUNCTION CREATE FUNCTION CREATE FUNCTION CREATE FUNCTION CREATE FUNCTION postgres@slim-maroon:~$
Four - test it!
openmolar_demo=> SELECT soundex('Neil');
soundex
---------
N400
(1 row)
Tuesday, 7 September 2010
Postgres for openmolar2
I've been playing with postgres all morning, and have decided that the next version of openmolar will use it in preference to MySQL.Main Reasons.
- it's a clean break with the past
- GNUmed use postgres
- active development
- standards compliance
- not owned by oracle
Thursday, 2 September 2010
Pynotify style notifications
The notifcation system for openmolar2 is nearly complete.
Why not use pynotify? Cross-platform is a goal here.
Why not use pynotify? Cross-platform is a goal here.
Wednesday, 4 August 2010
tllts co-incidence makes me feel awful.
so today, I am playing golf and listening to last week's tllts.
for the 1st time in my life, I get half way through the show (well, maybe 30 minutes in)... and i think "not enjoying this!" and delete it!
imagine what a douche i feel then, when I get home.... to find an air mail package containing a tllts T-shirt and hackey sack.
it's as if the cock just crowed 3 times, and i realise my denial.
Allan, Pat, Linc and Dann, please forgive me, it won't happen again.
for the 1st time in my life, I get half way through the show (well, maybe 30 minutes in)... and i think "not enjoying this!" and delete it!
imagine what a douche i feel then, when I get home.... to find an air mail package containing a tllts T-shirt and hackey sack.
it's as if the cock just crowed 3 times, and i realise my denial.
Allan, Pat, Linc and Dann, please forgive me, it won't happen again.
openmolar2
last night I began work on a new version of openmolar.
this one will not be done in a hurry (ie. in 4 months because of a proprietary license termination), nor will I be bound to a schema which I dare not change. I have come a long way on my programming journey, and feel ready to take the plunge into what I hope will be the most customisable, pluggable, and downright froody piece of dental management software known to mankind.
openmolar up to this point, is software for my practice alone.
openmolar2 will be of use to other practices (I hope).
more later.
Neil.
this one will not be done in a hurry (ie. in 4 months because of a proprietary license termination), nor will I be bound to a schema which I dare not change. I have come a long way on my programming journey, and feel ready to take the plunge into what I hope will be the most customisable, pluggable, and downright froody piece of dental management software known to mankind.
openmolar up to this point, is software for my practice alone.
openmolar2 will be of use to other practices (I hope).
more later.
Neil.
Tuesday, 3 August 2010
QTextEdit with autocompletion using pyqt
Problem - I wanted a text-edit which would help enter long words.
Solution - I have converted the C++ example code into python, and post it here to for all to see.
NB - The code assumes your dictionary is located at /usr/share/dict/words.
Solution - I have converted the C++ example code into python, and post it here to for all to see.
NB - The code assumes your dictionary is located at /usr/share/dict/words.
from PyQt4 import QtGui, QtCore
STARTTEXT = ('This TextEdit provides autocompletions for words that have ' +
'more than 3 characters.\nYou can trigger autocompletion using %s\n\n'''% (
QtGui.QKeySequence("Ctrl+E").toString(QtGui.QKeySequence.NativeText)))
class DictionaryCompleter(QtGui.QCompleter):
def __init__(self, parent=None):
words = []
try:
f = open("/usr/share/dict/words","r")
for word in f:
words.append(word.strip())
f.close()
except IOError:
print "dictionary not in anticipated location"
QtGui.QCompleter.__init__(self, words, parent)
class CompletionTextEdit(QtGui.QTextEdit):
def __init__(self, parent=None):
super(CompletionTextEdit, self).__init__(parent)
self.setMinimumWidth(400)
self.setPlainText(STARTTEXT)
self.completer = None
self.moveCursor(QtGui.QTextCursor.End)
def setCompleter(self, completer):
if self.completer:
self.disconnect(self.completer, 0, self, 0)
if not completer:
return
completer.setWidget(self)
completer.setCompletionMode(QtGui.QCompleter.PopupCompletion)
completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
self.completer = completer
self.connect(self.completer,
QtCore.SIGNAL("activated(const QString&)"), self.insertCompletion)
def insertCompletion(self, completion):
tc = self.textCursor()
extra = (completion.length() -
self.completer.completionPrefix().length())
tc.movePosition(QtGui.QTextCursor.Left)
tc.movePosition(QtGui.QTextCursor.EndOfWord)
tc.insertText(completion.right(extra))
self.setTextCursor(tc)
def textUnderCursor(self):
tc = self.textCursor()
tc.select(QtGui.QTextCursor.WordUnderCursor)
return tc.selectedText()
def focusInEvent(self, event):
if self.completer:
self.completer.setWidget(self);
QtGui.QTextEdit.focusInEvent(self, event)
def keyPressEvent(self, event):
if self.completer and self.completer.popup().isVisible():
if event.key() in (
QtCore.Qt.Key_Enter,
QtCore.Qt.Key_Return,
QtCore.Qt.Key_Escape,
QtCore.Qt.Key_Tab,
QtCore.Qt.Key_Backtab):
event.ignore()
return
## has ctrl-E been pressed??
isShortcut = (event.modifiers() == QtCore.Qt.ControlModifier and
event.key() == QtCore.Qt.Key_E)
if (not self.completer or not isShortcut):
QtGui.QTextEdit.keyPressEvent(self, event)
## ctrl or shift key on it's own??
ctrlOrShift = event.modifiers() in (QtCore.Qt.ControlModifier ,
QtCore.Qt.ShiftModifier)
if ctrlOrShift and event.text().isEmpty():
# ctrl or shift key on it's own
return
eow = QtCore.QString("~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-=") #end of word
hasModifier = ((event.modifiers() != QtCore.Qt.NoModifier) and
not ctrlOrShift)
completionPrefix = self.textUnderCursor()
if (not isShortcut and (hasModifier or event.text().isEmpty() or
completionPrefix.length() < 3 or
eow.contains(event.text().right(1)))):
self.completer.popup().hide()
return
if (completionPrefix != self.completer.completionPrefix()):
self.completer.setCompletionPrefix(completionPrefix)
popup = self.completer.popup()
popup.setCurrentIndex(
self.completer.completionModel().index(0,0))
cr = self.cursorRect()
cr.setWidth(self.completer.popup().sizeHintForColumn(0)
+ self.completer.popup().verticalScrollBar().sizeHint().width())
self.completer.complete(cr) ## popup it up!
if __name__ == "__main__":
app = QtGui.QApplication([])
completer = DictionaryCompleter()
te = CompletionTextEdit()
te.setCompleter(completer)
te.show()
app.exec_()
Wednesday, 30 June 2010
Session Managers and PyQt Applications
X11 window managers commonly have an option to "remember" applications which are running when a session ends.
Here's the gnome-checkbox (System -> Preferences -> Startup Applications)
but for this to work, the applications themselves need to comply. Qt does this with the QSessionManager class.
The class is not used directly, but is passed as an argument into functions of qApp, namely the saveState and commitData functions.
qApp therefore needs to be subclassed, and these two functions overwritten.
here's a working example. Note the simplicity of the functions!
Here's the gnome-checkbox (System -> Preferences -> Startup Applications)
but for this to work, the applications themselves need to comply. Qt does this with the QSessionManager class.
The class is not used directly, but is passed as an argument into functions of qApp, namely the saveState and commitData functions.
qApp therefore needs to be subclassed, and these two functions overwritten.
here's a working example. Note the simplicity of the functions!
#! /usr/bin/env python
from PyQt4 import QtGui, QtCore
import sys
class RestorableApplication(QtGui.QApplication):
def __init__(self):
super(RestorableApplication, self).__init__(sys.argv)
def commitData(self, session):
'''re-implement this method, called on quit'''
pass
def saveState(self, session):
'''re-implement this method, called on run'''
pass
app = RestorableApplication()
mw = QtGui.QMainWindow()
mw.setMinimumSize(300,300)
mw.setWindowTitle("I live on after a logout!")
label = QtGui.QLabel(
'''Leave me running and log off
I will survive! (on X11 systems)
Window size and position will be restored
... cool eh?''', mw)
label.setAlignment(QtCore.Qt.AlignCenter)
mw.setCentralWidget(label)
mw.show()
app.exec_()
Tuesday, 22 June 2010
qt-o-fax
Making the pluggable pyqt app has been more fun than I thought, and I believe the application may actually turn out to be half decent contacts manager.
it accepts plugins now (a zipped folder, with a config file within) and I am still finialising the plugin API, but I am certain I am on the right lines. The "zipimport" module did all that I hoped for and more.
It's running great on linux and windows. Plugins can simply be dropped into the applications plugins folder which reside here.
So what plugins could be created?? I think the sky is the limit. Add custom fields to the database, send SMS messages, create word processor documents with addresses embedded, christmas card lists.... contact sharing..
I've stuck it up on launchpad https://launchpad.net/qt-o-fax.
Screenshots


it accepts plugins now (a zipped folder, with a config file within) and I am still finialising the plugin API, but I am certain I am on the right lines. The "zipimport" module did all that I hoped for and more.
It's running great on linux and windows. Plugins can simply be dropped into the applications plugins folder which reside here.
linux = ~/.qt-o-fax/plugins windows = C:\Documents and Settings\USER\.qt-o-fax\Config file is simple format, heavily influenced by the gedit method of plugin config
[qt-o-fax Plugin] # module is where the entry point of the plugin is # it should contain a class Plugin, with a method run() # for this example, it is main.py # THIS IS THE ONLY FIELD WHICH IS ABSOLUTELY REQUIRED Module=main restartneeded=False Name=Hello World Name[fr]=bonjour toulemande Description=The simplest possible plugin Description[fr]=Un plugin moins sofisticate Version=0.1 Authors=Neil WallaceCopyright=Copyright © 2010 Neil Wallace Licence=LGPLv3 Website=https://launchpad.net/qt-o-fax/ LongDescription= Hello World Plugin
This plugin simply displays a hello world message when activated.
It connects to no signals, nor alters the database.
So what plugins could be created?? I think the sky is the limit. Add custom fields to the database, send SMS messages, create word processor documents with addresses embedded, christmas card lists.... contact sharing..
I've stuck it up on launchpad https://launchpad.net/qt-o-fax.
Screenshots


Wednesday, 16 June 2010
Making a pyqt4 application which accepts "plugins".
So I decided that openmolar needed to be broken up into bits. I did this last week, Client, Server and language packs are now seperate packages, meaning that I can update one part of it independently of the other bits. All well and good.
However, I REALLY want "plugins" (similar to firefox functionality) to provide some of the functionality that folks are going to want (partly because it will make it possible for them to code such things themselves)
So, as ever, 1st stage is to make a trivial app, and see if I can get some form of plugin system going.
Here's a video of that application (In making it, I learned a lot about QToolbar class... something I haven't used much before).
http://tinyvid.tv/show/2q6yruijgemsd
(if you have an HTML5 compatible browser, that should appear by magic below.. simply hit play)
If you want to see the code for this little application, I stuck it on launchpad - get it this way.
the vid was taken with the files at revision 1.
Now for the difficult bit.. designing and loading the plugins.
My idea is that plugins should be zipped folders. Signed by the author ideally, and containing a config file which describes it, gives version numbers, and installation instructions which the parent app can use.
Python can import modules from zipped folders easily thanks to the zipimport module.
I think I am on the right lines for this... I'll let you know (via this blog) how I get on
However, I REALLY want "plugins" (similar to firefox functionality) to provide some of the functionality that folks are going to want (partly because it will make it possible for them to code such things themselves)
So, as ever, 1st stage is to make a trivial app, and see if I can get some form of plugin system going.
Here's a video of that application (In making it, I learned a lot about QToolbar class... something I haven't used much before).
http://tinyvid.tv/show/2q6yruijgemsd
(if you have an HTML5 compatible browser, that should appear by magic below.. simply hit play)
If you want to see the code for this little application, I stuck it on launchpad - get it this way.
~$ bzr branch lp:~rowinggolfer/+junk/pluggable-pyqt-app
the vid was taken with the files at revision 1.
Now for the difficult bit.. designing and loading the plugins.
My idea is that plugins should be zipped folders. Signed by the author ideally, and containing a config file which describes it, gives version numbers, and installation instructions which the parent app can use.
Python can import modules from zipped folders easily thanks to the zipimport module.
I think I am on the right lines for this... I'll let you know (via this blog) how I get on
Friday, 7 May 2010
cloud required??
I have 2 issues to solve today.
1. backup of x-ray images (currently that is approx 3GB of data per year)
2. being able to share these images with colleagues when appropriate
now... forgive me if I am wrong.. but is the solution not "dropbox" or similar??
big opportunity for a lucrative market.
follow-up---
http://www.ironmountain.co.uk/solutions/industry/healthcare/imaging.asp
1. backup of x-ray images (currently that is approx 3GB of data per year)
2. being able to share these images with colleagues when appropriate
now... forgive me if I am wrong.. but is the solution not "dropbox" or similar??
big opportunity for a lucrative market.
follow-up---
http://www.ironmountain.co.uk/solutions/industry/healthcare/imaging.asp
Wednesday, 5 May 2010
QTreeView and QAbractItemModel example
This little example sets up a pythonic model, attaches a treeview to it, and allows user interaction in 2 ways.
1. clicking on the treeview
2. making selections using buttons.
Why is this important? Well, "models" are not aware of what is selected by the various views attached to them. With multiple views of the same data (or related data) it is up to the coder to keep this reference. the button click stuff is used in this example to explore such a scenario.
1. clicking on the treeview
2. making selections using buttons.
Why is this important? Well, "models" are not aware of what is selected by the various views attached to them. With multiple views of the same data (or related data) it is up to the coder to keep this reference. the button click stuff is used in this example to explore such a scenario.
from PyQt4 import QtGui, QtCore
HORIZONTAL_HEADERS = ("Surname", "Given Name")
class person_class(object):
'''
a trivial custom data object
'''
def __init__(self, sname, fname, isMale):
self.sname = sname
self.fname = fname
self.isMale = isMale
def __repr__(self):
return "PERSON - %s %s"% (self.fname, self.sname)
class TreeItem(object):
'''
a python object used to return row/column data, and keep note of
it's parents and/or children
'''
def __init__(self, person, header, parentItem):
self.person = person
self.parentItem = parentItem
self.header = header
self.childItems = []
def appendChild(self, item):
self.childItems.append(item)
def child(self, row):
return self.childItems[row]
def childCount(self):
return len(self.childItems)
def columnCount(self):
return 2
def data(self, column):
if self.person == None:
if column == 0:
return QtCore.QVariant(self.header)
if column == 1:
return QtCore.QVariant("")
else:
if column == 0:
return QtCore.QVariant(self.person.sname)
if column == 1:
return QtCore.QVariant(self.person.fname)
return QtCore.QVariant()
def parent(self):
return self.parentItem
def row(self):
if self.parentItem:
return self.parentItem.childItems.index(self)
return 0
class treeModel(QtCore.QAbstractItemModel):
'''
a model to display a few names, ordered by sex
'''
def __init__(self, parent=None):
super(treeModel, self).__init__(parent)
self.people = []
for fname, sname, isMale in (("John","Brown", 1),
("Fred", "Bloggs", 1), ("Sue", "Smith", 0)):
person = person_class(sname, fname, isMale)
self.people.append(person)
self.rootItem = TreeItem(None, "ALL", None)
self.parents = {0 : self.rootItem}
self.setupModelData()
def columnCount(self, parent=None):
if parent and parent.isValid():
return parent.internalPointer().columnCount()
else:
return len(HORIZONTAL_HEADERS)
def data(self, index, role):
if not index.isValid():
return QtCore.QVariant()
item = index.internalPointer()
if role == QtCore.Qt.DisplayRole:
return item.data(index.column())
if role == QtCore.Qt.UserRole:
if item:
return item.person
return QtCore.QVariant()
def headerData(self, column, orientation, role):
if (orientation == QtCore.Qt.Horizontal and
role == QtCore.Qt.DisplayRole):
try:
return QtCore.QVariant(HORIZONTAL_HEADERS[column])
except IndexError:
pass
return QtCore.QVariant()
def index(self, row, column, parent):
if not self.hasIndex(row, column, parent):
return QtCore.QModelIndex()
if not parent.isValid():
parentItem = self.rootItem
else:
parentItem = parent.internalPointer()
childItem = parentItem.child(row)
if childItem:
return self.createIndex(row, column, childItem)
else:
return QtCore.QModelIndex()
def parent(self, index):
if not index.isValid():
return QtCore.QModelIndex()
childItem = index.internalPointer()
if not childItem:
return QtCore.QModelIndex()
parentItem = childItem.parent()
if parentItem == self.rootItem:
return QtCore.QModelIndex()
return self.createIndex(parentItem.row(), 0, parentItem)
def rowCount(self, parent=QtCore.QModelIndex()):
if parent.column() > 0:
return 0
if not parent.isValid():
p_Item = self.rootItem
else:
p_Item = parent.internalPointer()
return p_Item.childCount()
def setupModelData(self):
for person in self.people:
if person.isMale:
sex = "MALES"
else:
sex = "FEMALES"
if not self.parents.has_key(sex):
newparent = TreeItem(None, sex, self.rootItem)
self.rootItem.appendChild(newparent)
self.parents[sex] = newparent
parentItem = self.parents[sex]
newItem = TreeItem(person, "", parentItem)
parentItem.appendChild(newItem)
def searchModel(self, person):
'''
get the modelIndex for a given appointment
'''
def searchNode(node):
'''
a function called recursively, looking at all nodes beneath node
'''
for child in node.childItems:
if person == child.person:
index = self.createIndex(child.row(), 0, child)
return index
if child.childCount() > 0:
result = searchNode(child)
if result:
return result
retarg = searchNode(self.parents[0])
print retarg
return retarg
def find_GivenName(self, fname):
app = None
for person in self.people:
if person.fname == fname:
app = person
break
if app != None:
index = self.searchModel(app)
return (True, index)
return (False, None)
if __name__ == "__main__":
def row_clicked(index):
'''
when a row is clicked... show the name
'''
print tv.model().data(index, QtCore.Qt.UserRole)
def but_clicked():
'''
when a name button is clicked, I iterate over the model,
find the person with this name, and set the treeviews current item
'''
name = dialog.sender().text()
print "BUTTON CLICKED:", name
result, index = model.find_GivenName(name)
if result:
if index:
tv.setCurrentIndex(index)
return
tv.clearSelection()
app = QtGui.QApplication([])
model = treeModel()
dialog = QtGui.QDialog()
dialog.setMinimumSize(300,150)
layout = QtGui.QVBoxLayout(dialog)
tv = QtGui.QTreeView(dialog)
tv.setModel(model)
tv.setAlternatingRowColors(True)
layout.addWidget(tv)
label = QtGui.QLabel("Search for the following person")
layout.addWidget(label)
buts = []
frame = QtGui.QFrame(dialog)
layout2 = QtGui.QHBoxLayout(frame)
for person in model.people:
but = QtGui.QPushButton(person.fname, frame)
buts.append(but)
layout2.addWidget(but)
QtCore.QObject.connect(but, QtCore.SIGNAL("clicked()"), but_clicked)
layout.addWidget(frame)
but = QtGui.QPushButton("Clear Selection")
layout.addWidget(but)
QtCore.QObject.connect(but, QtCore.SIGNAL("clicked()"), tv.clearSelection)
QtCore.QObject.connect(tv, QtCore.SIGNAL("clicked (QModelIndex)"),
row_clicked)
dialog.exec_()
app.closeAllWindows()
Saturday, 17 April 2010
openmolar - preparing for roll out
i am thinking of breaking openmolar into bits
ie. separate packages namely openmolar-server, openmolar-client and openmolar-help multiple reasons for this.
A slimmer version of the existing package will become the client, and this will be the cross platform bit (ie. I will make a windows executable)
openmolar-server will be the package which sets up and configures the mysql server and allows for practice customisation. This will only run on unix-like OSs.
openmolar-help will simply put a set of html docs and videos into a pre-destined location.
if anyone has any thoughts, please let me know.
ie. separate packages namely openmolar-server, openmolar-client and openmolar-help multiple reasons for this.
A slimmer version of the existing package will become the client, and this will be the cross platform bit (ie. I will make a windows executable)
openmolar-server will be the package which sets up and configures the mysql server and allows for practice customisation. This will only run on unix-like OSs.
openmolar-help will simply put a set of html docs and videos into a pre-destined location.
if anyone has any thoughts, please let me know.
Monday, 12 April 2010
PyQt4 model/view drag & drop example
In PyQt4, drag and drop with QListWidget works really, really well.
However, for real life application, I believe it's best to leave the convenience widgets (like QListWidget) behind, and head for the model/view pyqt4 classes. This gives a lot more flexibility than the "one-size fits all" model which comes with QListwidget et al.
This enables easy referencing of the true python objects that underlie the model, using Qt.UserRole to refer to the object, and pickle (or cPickle) to convert to a bytestream which the drag/drop can handle.
here is a little example using a few tricks to get drag and drop of native python objects in PyQt4.
However, for real life application, I believe it's best to leave the convenience widgets (like QListWidget) behind, and head for the model/view pyqt4 classes. This gives a lot more flexibility than the "one-size fits all" model which comes with QListwidget et al.
This enables easy referencing of the true python objects that underlie the model, using Qt.UserRole to refer to the object, and pickle (or cPickle) to convert to a bytestream which the drag/drop can handle.
here is a little example using a few tricks to get drag and drop of native python objects in PyQt4.
import datetime
import cPickle
import pickle
import sys
from PyQt4 import QtGui, QtCore
class person(object):
'''
a custom data structure, for example purposes
'''
def __init__(self, name, dob, house_no):
self.name = name
self.dob = dob
self.addr = "%d Rue de la Soleil"% house_no
def __repr__(self):
return "%s\n%s\n%s"% (self.name, self.dob, self.addr)
class simple_model(QtCore.QAbstractListModel):
def __init__(self, parent=None):
super(simple_model, self).__init__(parent)
self.list = []
for name, dob, house_no in (
("Neil", datetime.date(1969,12,9), 23),
("John", datetime.date(1952,5,3), 2543),
("Ilona", datetime.date(1975,4,6), 1)):
self.list.append(person(name, dob, house_no))
self.setSupportedDragActions(QtCore.Qt.MoveAction)
def rowCount(self, parent=QtCore.QModelIndex()):
return len(self.list)
def data(self, index, role):
if role == QtCore.Qt.DisplayRole: #show just the name
person = self.list[index.row()]
return QtCore.QVariant(person.name)
elif role == QtCore.Qt.UserRole: #return the whole python object
person = self.list[index.row()]
return person
return QtCore.QVariant()
def removeRow(self, position):
self.list = self.list[:position] + self.list[position+1:]
self.reset()
class dropZone(QtGui.QLabel):
def __init__(self, parent=None):
super(dropZone, self).__init__(parent)
self.setMinimumSize(200,200)
self.set_bg()
self.setText("Drop Here")
self.setAlignment(QtCore.Qt.AlignCenter)
self.setAcceptDrops(True)
def dragEnterEvent(self, event):
if event.mimeData().hasFormat("application/x-person"):
self.set_bg(True)
event.accept()
else:
event.ignore()
def dragMoveEvent(self, event):
if event.mimeData().hasFormat("application/x-person"):
event.setDropAction(QtCore.Qt.MoveAction)
event.accept()
else:
event.ignore()
def dragLeaveEvent(self, event):
self.set_bg()
def dropEvent(self, event):
data = event.mimeData()
bstream = data.retrieveData("application/x-person",
QtCore.QVariant.ByteArray)
selected = pickle.loads(bstream.toByteArray())
self.setText(str(selected))
self.set_bg()
event.accept()
def set_bg(self, active=False):
if active:
val = "background:yellow;"
else:
val = "background:green;"
self.setStyleSheet(val)
class draggableList(QtGui.QListView):
'''
a listView whose items can be moved
'''
def ___init__(self, parent=None):
super(draggableList, self).__init__(parent)
self.setDragEnabled(True)
def dragEnterEvent(self, event):
if event.mimeData().hasFormat("application/x-person"):
event.setDropAction(QtCore.Qt.QMoveAction)
event.accept()
else:
event.ignore()
def startDrag(self, event):
index = self.indexAt(event.pos())
if not index.isValid():
return
## selected is the relevant person object
selected = self.model().data(index,QtCore.Qt.UserRole)
## convert to a bytestream
bstream = cPickle.dumps(selected)
mimeData = QtCore.QMimeData()
mimeData.setData("application/x-person", bstream)
drag = QtGui.QDrag(self)
drag.setMimeData(mimeData)
# example 1 - the object itself
pixmap = QtGui.QPixmap()
pixmap = pixmap.grabWidget(self, self.rectForIndex(index))
# example 2 - a plain pixmap
#pixmap = QtGui.QPixmap(100, self.height()/2)
#pixmap.fill(QtGui.QColor("orange"))
drag.setPixmap(pixmap)
drag.setHotSpot(QtCore.QPoint(pixmap.width()/2, pixmap.height()/2))
drag.setPixmap(pixmap)
result = drag.start(QtCore.Qt.MoveAction)
if result: # == QtCore.Qt.MoveAction:
self.model().removeRow(index.row())
def mouseMoveEvent(self, event):
self.startDrag(event)
class testDialog(QtGui.QDialog):
def __init__(self, parent=None):
super(testDialog, self).__init__(parent)
self.setWindowTitle("Drag Drop Test")
layout = QtGui.QGridLayout(self)
label = QtGui.QLabel("Drag Name From This List")
self.model = simple_model()
self.listView = draggableList()
self.listView.setModel(self.model)
self.dz = dropZone()
layout.addWidget(label,0,0)
layout.addWidget(self.listView,1,0)
layout.addWidget(self.dz,0,1,2,2)
if __name__ == "__main__":
'''
the try catch here is to ensure that the app exits cleanly no matter what
makes life better for SPE
'''
try:
app = QtGui.QApplication([])
dl = testDialog()
dl.exec_()
except Exception, e: #could use as e for python 2.6...
print e
sys.exit(app.closeAllWindows())
Thursday, 25 March 2010
putting video onto lg ks360 mobile phone
Tonight the problem I had to solve was putting video onto my daughter's mobile phone, which is an inexpensive lg ks360.
First I grabbed the latest, uncrippled ffmpeg, following this howto http://ubuntuforums.org/showthread.php?t=786095
now after some experimenting I settled on the following params to convert to a format the phone supported.
First I grabbed the latest, uncrippled ffmpeg, following this howto http://ubuntuforums.org/showthread.php?t=786095
now after some experimenting I settled on the following params to convert to a format the phone supported.
ffmpeg -i input_file -acodec libfaac -ar 22000 -ab 32k -ac 2 -vtag mp4v -r 15 -s 320x240 output_file.mp4then I drop the output_file.mp4 onto the phone's miniSD card, in the folder "Videos", and I'm done.
Subscribe to:
Posts (Atom)




