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
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
Friday, 7 May 2010
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.
Monday, 1 March 2010
The DreamPie Python Shell
Just trying out the DreamPie Python Shell.
Lovin' it so far, this solves a lot of problems I have with other interative shells.
I grabbed it from The dreampie PPA
Highly recommended.
Lovin' it so far, this solves a lot of problems I have with other interative shells.
I grabbed it from The dreampie PPA
Highly recommended.
Thursday, 25 February 2010
Graphical database application with 67 lines of python
Following some discussion in the #pyqt chatroom on freenode.net, I decided to play with the QtSql module of pyqt.
Here's the results, the table allows direct editing of the db.
note - you may need to install some dependencies
sudo apt-get install python-qt4 python-qt4-sql libqt4-sql-sqlite
Here's the results, the table allows direct editing of the db.
note - you may need to install some dependencies
sudo apt-get install python-qt4 python-qt4-sql libqt4-sql-sqlite
#! /usr/bin/env python
'''
###########################################################
## A Demo Application showing the use of sqlite3 ##
## and the QSqlTableModel Class ##
## written by rowinggolfer 24th Feb 2010 ##
## version 0.1 and NOT YET WORKING!! ##
## this work is in the public domain, ##
## do with it as you please ##
###########################################################
'''
import os, sys
from PyQt4 import QtCore, QtGui, QtSql
def makeDB():
import sqlite3
db = sqlite3.connect("test.db")
db.execute("create table if not exists table1 (value text, data text)")
query = "insert into table1 (value, data) values (?, ?)"
valueSet = (("day","today"),("time","noon"),("food","cheese"))
for values in valueSet:
db.execute(query, values)
db.commit()
class TestApp(QtGui.QDialog):
def __init__(self, model, parent = None):
super(TestApp, self).__init__(parent)
self.model = model
table = QtGui.QTableView()
table.setModel(self.model)
button = QtGui.QPushButton("Add a row")
layout = QtGui.QVBoxLayout(self)
layout.addWidget(table)
layout.addWidget(button)
self.connect(button, QtCore.SIGNAL("clicked()"), self.addRow)
def addRow(self):
self.model.insertRows(self.model.rowCount(), 1)
class myModel(QtSql.QSqlTableModel):
def __init__(self, parent = None):
super(myModel, self).__init__(parent)
self.setEditStrategy(QtSql.QSqlTableModel.OnFieldChange)
self.setTable("table1")
self.select()
if __name__ == "__main__":
if not os.path.exists("test.db"):
makeDB()
myDb = QtSql.QSqlDatabase.addDatabase("QSQLITE")
myDb.setDatabaseName("test.db")
if not myDb.open():
print "Unable to create connection!"
print "have you installed the sqlite driver?"
print "sudo apt-get install libqt4-sql-sqlite"
sys.exit(1)
model = myModel()
app = QtGui.QApplication(sys.argv)
dl = TestApp(model)
dl.exec_()
Wednesday, 24 February 2010
Ubuntu Update manager - feature request
Ubuntu handles updates really well, no question. The user is prompted to update, but without annoying pop ups that disrupt a workflow (cf M$ windows reboot in 5 minutes - ARGHH!)
However, there's one thing I would like to see altered.
Once one has clicked "install", the top level dialog box prevents access to all the wonderful information about the updates being installed. Granted, one should check before accepting these.. but....
I would prefer if I could still read details about what is being installed... as it happens.
As a hobby coder, I realise this is extra work, but if the scrollArea and Description widgets were still acessible.. I would be delighted.
update - I've filed a bug
https://bugs.launchpad.net/update-manager/+bug/526937
watch this space
However, there's one thing I would like to see altered.
Once one has clicked "install", the top level dialog box prevents access to all the wonderful information about the updates being installed. Granted, one should check before accepting these.. but....
I would prefer if I could still read details about what is being installed... as it happens.
As a hobby coder, I realise this is extra work, but if the scrollArea and Description widgets were still acessible.. I would be delighted.
update - I've filed a bug
https://bugs.launchpad.net/update-manager/+bug/526937
watch this space
Wednesday, 10 February 2010
pitivi
There's been a lot of buzz about pitivi perhaps being in ubuntu lucid by default.
So I thought I would try the latest version.
I was surprised to learn that there isn't a ppa version available, so I set one up, and made a deb from the latest git version of pitivi. It works very well indeed, and is very intuitive.
My ppa for pitivi "unstable" is here, and can be added using the new add-apt-repository command, which saves a lot of key hassle.
sudo add-apt-repository ppa:rowinggolfer/pitivi-unstable
add the g-streamer ppa while you are at it (pitivi uses gstreamer for the heavy lifting)
sudo add-apt-repository ppa:gstreamer-developers/ppa
Here's my first attempt with the new pitivi.. a title page tacked onto the front of a wee video.
How did I make the title page? Gimp. But that's another issue altogether....
So I thought I would try the latest version.
I was surprised to learn that there isn't a ppa version available, so I set one up, and made a deb from the latest git version of pitivi. It works very well indeed, and is very intuitive.
My ppa for pitivi "unstable" is here, and can be added using the new add-apt-repository command, which saves a lot of key hassle.
sudo add-apt-repository ppa:rowinggolfer/pitivi-unstable
add the g-streamer ppa while you are at it (pitivi uses gstreamer for the heavy lifting)
sudo add-apt-repository ppa:gstreamer-developers/ppa
Here's my first attempt with the new pitivi.. a title page tacked onto the front of a wee video.
How did I make the title page? Gimp. But that's another issue altogether....
Wednesday, 9 December 2009
SCALE 2010
I have just submitted a talk for the beginner track at next years Southern California Linux Expo.
I have no idea whether it will be accepted. However, here's the submission.
Short submission.
I started writing an application called "openMolar" in November of 2008. openMolar is an application used in my dental practice.
To do this I had to learn Python, Qt4, the bazaar version control system, mysql, GNU gettext, and debian packaging.
I also learnt to use the Launchpad facilities for code hosting, bug tracking, translation and a PPA repository for ubuntu .
In this talk, I hope to give you a basic synopsis of why I chose these particular tools (because, let's face it, there are some fine alternatives to each).
I do not claim to be anything other than an enthusiastic hobbyist in any of these areas, but I have successfully used them to get my application to a stage which is working well in a demanding real-life situation.
So if you are not developing applications yet, or are doing so using different tools, please come along and hear what I hope is an interesting story of "an application from scratch".
p.s. if you are not writing code yet... I will endeavour to change this. If I (a middle-aged dentist) can write working code.. anyone can.
Long Submission.
I want to discuss the following items during the talk.
1. Having A problem to solve - a demonstration of my application in use.
2. Choosing a license.
3. Why Python?
4. Why PyQt?
5. Collaborating with others, using launchpad for code hosting and bug tracking.
6. Packaging the app so that you get feedback. Debian packaging and the use of a PPA.
7. Translating into other languages - the GNU gettext tools. Porquoi?
8. the future for the application - can we make money from this?
Message to Reviewers.
No presentation to upload at present, but I do have a video online at
http://tinyvid.tv/show/1174zh4v3sldz
project website is https://launchpad.net/openmolar
I have no idea whether it will be accepted. However, here's the submission.
| Title | Get Developing - it's easy. |
| Categories | General |
| Audiences | Beginner, Intermediate |
| Description | We all know that Linux has some wonderful tools for developing applications. I learnt these tools to become the sole IT support for my business ( a sucessful dental office). I'll tell you what I did, and hopefully inspire you to do the same. |
Short submission.
I started writing an application called "openMolar" in November of 2008. openMolar is an application used in my dental practice.
To do this I had to learn Python, Qt4, the bazaar version control system, mysql, GNU gettext, and debian packaging.
I also learnt to use the Launchpad facilities for code hosting, bug tracking, translation and a PPA repository for ubuntu .
In this talk, I hope to give you a basic synopsis of why I chose these particular tools (because, let's face it, there are some fine alternatives to each).
I do not claim to be anything other than an enthusiastic hobbyist in any of these areas, but I have successfully used them to get my application to a stage which is working well in a demanding real-life situation.
So if you are not developing applications yet, or are doing so using different tools, please come along and hear what I hope is an interesting story of "an application from scratch".
p.s. if you are not writing code yet... I will endeavour to change this. If I (a middle-aged dentist) can write working code.. anyone can.
Long Submission.
I want to discuss the following items during the talk.
1. Having A problem to solve - a demonstration of my application in use.
2. Choosing a license.
3. Why Python?
4. Why PyQt?
5. Collaborating with others, using launchpad for code hosting and bug tracking.
6. Packaging the app so that you get feedback. Debian packaging and the use of a PPA.
7. Translating into other languages - the GNU gettext tools. Porquoi?
8. the future for the application - can we make money from this?
Message to Reviewers.
No presentation to upload at present, but I do have a video online at
http://tinyvid.tv/show/1174zh4v3sldz
project website is https://launchpad.net/openmolar
Thursday, 19 November 2009
regex saves the pyqt day
I'm developing openmolar on Ubuntu Karmic, and that creates some backwards compatibility issues for hardy, intrepid and jaunty with code generated by pyuic4.
so I have to make some substitutions, and that's where regex comes in.
the python regex module is "re", and I am using a few of it's features here. Anyways, I'll let the code speak for itself.
the problem is to turn generated code like this (on pyqt 4.6 where native python integers are acceptable)
my script for compiling any qt-designer generated ui files into python code can be found here the above code is in lines 38 - 48.
so I have to make some substitutions, and that's where regex comes in.
the python regex module is "re", and I am using a few of it's features here. Anyways, I'll let the code speak for itself.
the problem is to turn generated code like this (on pyqt 4.6 where native python integers are acceptable)
spinBox.setProperty("value", 8)
progressBar.setProperty("value", 10)
randomWidget.setProperty("value", 260)
into this....spinBox.setProperty("value", QtCore.QVariant(8))
progressBar.setProperty("value", QtCore.QVariant(10))
randomWidget.setProperty("value", QtCore.QVariant(260)Here's one way to do it...import re
matches = re.finditer('setProperty\("value", (\d+)\)', data)
for m in matches:
data = data.replace(m.group(), "QtCore.QVariant(%s)"% m.groups()[0])my script for compiling any qt-designer generated ui files into python code can be found here the above code is in lines 38 - 48.
Sunday, 1 November 2009
Using an mdiArea in PyQt
I am adding a notifications area to openmolar, and experimenting with an mdiArea for this.
I couldn't find a nice python (PyQt) example on the web, so offer this for google's sake.
I couldn't find a nice python (PyQt) example on the web, so offer this for google's sake.
import sys
from PyQt4 import QtCore, QtGui
app = QtGui.QApplication(sys.argv)
mdiArea = QtGui.QMdiArea()
mdiArea.show()
labels = []
for i in range(5):
labels.append(QtGui.QLabel())
labels[i].setText("hello world")
mdiArea.addSubWindow(labels[i])
labels[i].show()
mdiArea.cascadeSubWindows()
sys.exit(app.exec_())
Monday, 5 October 2009
Internationalising Openmolar
I was approached by 2 fine gents who offered to translate OpenMolar.
Firstly Ariel Cornejo offered a Spanish translation, closely followed by Philippe Le Toquin who offered a French Version.
Anyways, the experience has been 100% positive. Lots of fun, and valuable learning again. The Gnu Gettext stuff, and the Launchpad translation facilities are very well thought out. Ditto Python's own implementation of such tools.
The screenshots to the right show the application
switching into French Mode.
Tres bon, n' est ce pas??
I'll do a technical write up on what I learned in doing this over on the openmolar wiki.
Firstly Ariel Cornejo offered a Spanish translation, closely followed by Philippe Le Toquin who offered a French Version.
Anyways, the experience has been 100% positive. Lots of fun, and valuable learning again. The Gnu Gettext stuff, and the Launchpad translation facilities are very well thought out. Ditto Python's own implementation of such tools.
The screenshots to the right show the application
switching into French Mode.
Tres bon, n' est ce pas??
I'll do a technical write up on what I learned in doing this over on the openmolar wiki.
Sunday, 20 September 2009
OpenMolar on the Ubuntu-uk podcast
This week I was interviewed by the ubuntu loco team for the ubuntu-uk podcast.
I was totally overwhelmed (as I always am when in the company of REAL nerds/geeks), but they were kind to me, and their editing skills have produced some audio of which I am proud to be a part of.
The ubuntu-uk podcast is a professional and highly regarded podcast within the linux community and beyond. What an honour! I have already had lots of interesting feedback.
If you haven't heard the podcast, choose on of these formats.
Many thanks to Alan, Daviey, Ciemon, Daviey, Tony and Laura.
I was totally overwhelmed (as I always am when in the company of REAL nerds/geeks), but they were kind to me, and their editing skills have produced some audio of which I am proud to be a part of.
The ubuntu-uk podcast is a professional and highly regarded podcast within the linux community and beyond. What an honour! I have already had lots of interesting feedback.
If you haven't heard the podcast, choose on of these formats.
Many thanks to Alan, Daviey, Ciemon, Daviey, Tony and Laura.
Thursday, 3 September 2009
OpenMolar - screencast
ok.. I finally got around to doing this. A 15 minute intro to openmolar.
http://tinyvid.tv/show/1174zh4v3sldz
as an aside... A note about tinyvid.tv.
I didn't use youtube because they have a 10 minute restriction on length.
Trawling the web, I came across tinyvid via floss manuals... there is some seriously good community content on there (Stallman vids etc..)... and all the content is ogg.
Putting content onto Tinyvid is a wonderful experience, from start to finish.
It accepted my launchpad page as openID. Uploaded the video without fuss, and even transcodes non-ogg content into ogg "in the cloud".
I hope they are around for a long time to come. I tried to leave a donation, but couldn't see how to do that.
Hope you enjoy the video. Comments, as always, very welcome.
(screencast done on dell-mini9 using gtk-recordmydesktop and logitech USB headset)
http://tinyvid.tv/show/1174zh4v3sldz
as an aside... A note about tinyvid.tv.
I didn't use youtube because they have a 10 minute restriction on length.
Trawling the web, I came across tinyvid via floss manuals... there is some seriously good community content on there (Stallman vids etc..)... and all the content is ogg.
Putting content onto Tinyvid is a wonderful experience, from start to finish.
It accepted my launchpad page as openID. Uploaded the video without fuss, and even transcodes non-ogg content into ogg "in the cloud".
I hope they are around for a long time to come. I tried to leave a donation, but couldn't see how to do that.
Hope you enjoy the video. Comments, as always, very welcome.
(screencast done on dell-mini9 using gtk-recordmydesktop and logitech USB headset)
Subscribe to:
Posts (Atom)







