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.

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

project website is

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)
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(, "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.
import sys
from PyQt4 import QtCore, QtGui

app = QtGui.QApplication(sys.argv)

mdiArea = QtGui.QMdiArea()

labels = []

for i in range(5):
    labels[i].setText("hello world")


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.

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.

Thursday 3 September 2009

OpenMolar - screencast

ok.. I finally got around to doing this. A 15 minute intro to openmolar.

as an aside... A note about

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)

Sunday 30 August 2009

Ubuntu mysql and ssl

4 flippin' hours I have been tearing my hair out trying to force an ssl connection with mysql.
I generated keys, wrote the code, but it didn't work.

finally got it going though.
this HOW TO is the bomb.

for ubuntu swap all references to /var/db/mysql to /etc/mysql

so now you can all sniff my packets, see if I care.

Thursday 27 August 2009


So I've started an application to help make and deploy python apps on Ubuntu.

Here's a screencast showing it in use (version 0.2)

project is at

I would love feedback as to whether this application is of interest to anyone.

Tuesday 25 August 2009


to get a break from programming openMolar... but to help cement what I had learned about ubuntu packaging, I started another project pyAppTemplate.

The goal of pyAppTemplate is to create a "debianisable" source tree from the get go of a project and... it works! The debs put the app onto the python path, and an executable in /usr/bin an icon into /usr/share/icons/hicolor/scalable and create a menu item.

pyAppTemplate is currently a command-line only app, (I had my first play with the getopt module to parse the command line args passed), but I intend to create a qt, gtk, wx and tkinter frontend for it... just for fun!

I quickly made 2 virgin packages... and deployed them locally.

pyAppTemplate is, of course, gplv3 code, and available at

I'll put a deb into My PPA soon, and create a demo of the tool in use.

Monday 24 August 2009

Silly python lyrics....

To the lyrics of "Always on your side" by Sheryl Crow.

My pyqt apps are all boxed up and neatly put away
The qt kit with python they did bind
But we were always waiting for the license to be changed
From the LGPL, it found a place to hide
Which is a shame but now we have PySide.

why is my head full of such garbage?

Sunday 23 August 2009

.bashrc for pythonistas

It's a great trick for creating a clean copy of a directory of python stuff, but I got sick of typing

~$ rsync -av --exclude="*~" --exclude="*.pyc" ~/tests/ ~/tested/

so I decided to add an alias to ~/.bashrc

alias rsync='rsync --exclude="*~" --exclude="*.pyc"'

works fine.

Saturday 22 August 2009

Ubuntu Karmic alpha4 - 1st impressions

2 months before the release of Ubuntu 9.10 "Karmic Koala" (due 29th October), I am trying out the alpha 64-bit live CD on my advent 9112 laptop.

I have to tell you I am MIGHTILY impressed.

First linux distro that has recognised my rf (wireless) switch.
And palimpsest gave me a warning about my failing hard drive.
Time to run captain spinrite methinks.

My keyboard was incorrectly recognised as us... but that gave me my first look at the keyboard settings app. A thing of beauty. Dunno if that's new to karmic?
Anyways I would stake my mortgage on the keyboard layout being correct if I choose to install this to disk......

.... and do you know what? I am going to, because this just feels RIGHT.

congrats and thanks to everyone involved!

Friday 14 August 2009

Pylint Recursively

Not sure why I am blogging this, but I've been having a bit of fun with pylint, which checks python code for "bad smells" (ie. code which doesn't match the PEP standards).
Pylint also finds unused imports, variables etc...

I have written a little script to get only errors and scores out of pylint.
It works recursively looking for *.py scripts within a directory passed to it on the command line, or (if that is missing) within the current working directory.

Posted here in case it is of any use to anyone.

#! /usr/bin/env python
this module runs pylint on all python scripts found in a directory tree

import os
import re
import sys

total = 0.0
count = 0

def check(module):
apply pylint to the file specified if it is a *.py file
global total, count

if module[-3:] == ".py":

print "CHECKING ", module
pout = os.popen('pylint %s'% module, 'r')
for line in pout:
if re.match("E....:.", line):
print line
if "Your code has been rated at" in line:
print line
score = re.findall("\d.\d\d", line)[0]
total += float(score)
count += 1

if __name__ == "__main__":
print sys.argv
BASE_DIRECTORY = sys.argv[1]
except IndexError:
print "no directory specified, defaulting to current working directory"
BASE_DIRECTORY = os.getcwd()

print "looking for *.py scripts in subdirectories of ", BASE_DIRECTORY
for root, dirs, files in os.walk(BASE_DIRECTORY):
for name in files:
filepath = os.path.join(root, name)

print "==" * 50
print "%d modules found"% count
print "AVERAGE SCORE = %.02f"% (total / count)

Thursday 13 August 2009


I went trawling through synaptic to find a little app to notify me when a new piece of email arrived in my googlemail account, and found "gmail-notifier". I installed it, and decided that this was perfect for me.


the project looks stale (last commit in 2007) and the project's mailing list is full of spam.

the app has IMHO two major shortcomings...
  • password is stored in plain text in a config file

  • I suspect the password is sent in the clear

I may be wrong about point number 2, I was going to ask via the mailing list, but I very much doubt anyone reads it unless they want a bigger willy or cheap pharmaceuticals.

So it looks like I will have to use the FF plug in like everybody else :(

Thursday 23 July 2009

Sansa Clip Firmware upgrade

Just sucessfully updated the firmware on my sansa clip player following
This thread

Did not need to resort to the windows method :)

Thursday 16 July 2009

Programming resource

Highly recommended reading, on object orientation and software design in general.

Saturday 11 July 2009

bash-history - a tip and some thoughts.

A great tip on the Ubuntu-uk podcast this week for keeping your .bash_history file a little leaner (AND MORE SECURE!).

what's the difference between these two lines?

~mysqldump -u user -ppassword mydatabase > backup.sql
~ mysqldump -u user -ppassword mydatabase > backup.sql

well both do the same thing (backup a mysql database to a flat file).
However, the first variant would go into your .bash_history, whereas the second one wouldn't thanks to the space at the line start.

why does this matter?
1. you probably don't want to put plain text passwords into the shell history. It may be convenient, but this file is one of the first places anyone malicious (or simply mischievous) would look to harvest such things if they got physical or virtual access to your machine.

2. perhaps, like me, your .bash_history get's full of stuff like

cd ~
cp -av /home/neil/bashpodder/2009-06-25/ .
mount /dev/sdb /media/player
sudo mount /dev/sdb /media/player
sudo -s
ssh -p 3298 -N -L 2948:
sudo umount /media/player
echo "" >> bashpodder/bp.conf
cd ~/windows_openmolar/openmolar/ && bzr pull

now of those commands, only one or two benefit from being available via arrow navigation. in future, I will be considering putting a space before the majority of my command line playing.

but d'you know what? I think I need to look into the behaviour of bash_history some more. Ideally I would like it to have everything from that terminal session directly available, spaces or no...(in case of typos, which I'll admit happen occasionally) but archive only those without.

I'll bet there's something in the man page... must check sometime.

Thursday 2 July 2009

tunneling mysql over ssh

Here's my setup for remote access to my mysql server at work.

My work has a dynamically assigned ip from the local provider. I use the free service from to make that ip available. So let's say the hostname I chose from dyndns was
I could ping that address using


I set up a machine at work (machine A) to listen for ssh connections on port 34567
so I can connect to that machine using.

~ssh -p 34567

oh hang on.. my home user "neil" is not allowed, silly me, so let's say I have a known user called "dentist"

~ssh -p 34567

that works!

Now A separate mysql server (machine B) resides on that network. It's LAN IP is
This machine does not allow any other type of connection. What if I want to lever that database remotely? The easiest way is to ssh into machine A (as above) and use the mysql-client command line tool on that machine to connect to machine B.

that command is
~mysql -h -u databaseUser -p

which will prompt for a password. or
~mysql -h -u databaseUser -pPASSWORD mydatabase

which will pass the password "PASSWORD" automatically, and start using database "mydatabase" automatically.

but what if I want to use an application - like openmolar - to connect to that database.

the answer here is to forward the mysql port from machine B through machine A, so that it appears as a service running on my local machine.
here's how.....

~ssh -p 34567 -N -L 45678:

which means...
-p 34567 use "port" 34567 (isn't port a silly term... surely this should be "channel")
-N from the ssh manual - "Do not execute a remote command" I don't fully understand this, but do know the connection is refused if I ommit this.

-L 45678: let's break this down. -L is the command to "bind an address"

45678 is a random port on mylocal machine. is the address of the mysql service on machine B. machine A has permission to connect to this. Also note - 3306 is mysql's standard port.

with that command running, I can now point the app to and I'm golden.

Saturday 20 June 2009

dell mini bios upgrade

I've just upgraded the bios on my dell mini-9.

Followed this howto.

the new bios makes Fn+Z and Fn+X as function keys, and apprarently improves battery life.

more importantly... fancy graphics on boot :)

Friday 12 June 2009

Floss Manuals

Fascinating podcasts have emerged from the libre planet event.

Many mention the work done (in sprints thereat) to improve the floss manuals.

Here they are. Highly recommended.

Monday 11 May 2009

3 tips for new pythonistas

1. learn regex.
2. learn regex
3. learn regex.

Saturday 9 May 2009

Promote linux campaign

If I were involved in the marketing of Ubuntu... here is the advertising campaign I would run.

Setting - a school IT classroom
Music - Pink Floyd. "we don't need no education"
Theme - zombie kids being taught to use a popular word-processing application by (no names).

cool kid in the back of the classroom has a different desktop... what is it?
he's spinning the cube switching between all the killer apps. he winks to camera.

fade out...

ubuntu logo...

"because kids don't need no thought control"

Thursday 30 April 2009

openmolar update and a plea for sanity

2 weeks ago I switched off our commercially provided uber expensive crapware for the last time, and moved 100% to using my own solution "openMolar".

openMolar is a multi-client/single server application. Presently I have 2 linux clients, 3 windows clients running my software and a linux mysql server.
(I also have the full front-end and backend application running on my dell-mini9, but that's another story...)

The development of this application has been quite a trip, but proves that even someone with my limited coding skills can knock up a professional quality app using only open source tools.

And the bandwagon keeps on rolling.
Bryan Harris from the US and Mehmet nur Olcay from Turkey have joined in on the launchpad site, and are working on packaging the app for ubuntu and (more interestingly) Pardus linux. I look forward to making the code accessible for language translation purposes.

Richard Querin has again exceeded all expectations by providing an alternative logo focusing on the freedom aspect of the software. I intend to get some embroidered t-shirts for the practice staff to wear when we attend the British Dental Association Conference in 4 weeks time.

But before folks get too carried away, a reminder. This bandwagon needs the brakes applying somewhat.

My focus has always been (and will for the medium term at least) to have this software working in the practice.
The interest that is being shown from the dental profession and open source movement alike is quite humbling but a little frightening. It goes to show that there is demand for open source solutions, however, please let nobody be under the impression that this product is finished.
I am wary that if others take my code and push it out at this stage it could get it a reputation from which it will never recover. I am also fully aware that there are many companies who would love to see this project fail. I beg the open source community not to play a part in making this happen.

Here is a page of screenshots and explanations for those who would like to know more.

The project can be found on launchpad.

Thursday 16 April 2009

logos, launchpad and bizarre experiences

Three things.
  1. I am overjoyed with Richard Querin's first draft of a logo for openMolar.

  2. My code is up on at
  3. The python versioning control system Bzr is working well. I hope. thanks to "thumper" in #launchpad on freenode for his guidance. I have made 3 pushes already. Awesome.

Wednesday 8 April 2009

openmolar - new tarball

another test tarball for interested folks to install and try.

Still immature software, but a fair few new features.

Many thanks to all who sucessfully installed the 1st version despite a few errors in my README file!
Hopefully those bugs are now ironed out.

please note, you will need to refresh to the new database if you installed 0.0.3 version.

BTW - hopefully I will get the hang of bzr / launchpad for future releases... meantime, the tarball can be found HERE .

Sunday 22 March 2009

two wee python lifesavers

filed under the "notes to self" category... if I forget these tips I'll googling for them... so let's put them on my random blog for now.
  • This should be enabled by default in the standard python shell.

  • 'enscript' is a must have for printing code... and from the man page, is this suggestion from the developer himself - flags for printing code: gaudy header, two columns, landscape, code highlighting, 2-up printing.
    ~$enscript -G2rE -U2

    if you have a long script, check it out... it's beautiful.

Thursday 19 March 2009

openMolar - it's not a comic book reader.

I was a guest on the linux link tech show last night, I was my usual incoherent babbling self, but I had a great time.

If anyone is interested in seeing just how badly I write code, please download,play with, and scrutinise the openMolar software.

tarball (inlcuding an example database for March and April 2009) and a checksum for the paranoid can be found at

Tuesday 17 February 2009

Review - sansa clip ogg/mp3 player

With Linux cranks and the bad apples podcasts going to ogg only, I decided to invest in a new player.

The sansa clip seemed to fit the bill perfectly, for the following reasons.
  • mounts as a drive, easy drag and drop of files

  • plays ogg

  • built in fm radio

  • small

  • cheap

  • sexy

but it turns out that this is the best damn player I've ever owned... and here's why.
  • It can delete tracks. Very useful. I need to do this, otherwise I forget what I've listened to.

  • it can play tracks at an increased speed

That last feature sounds like a gimmick (or a discourtesy to the podcaster) however, I am enjoying it.
This morning, for example - it compressed lottalinuxlinks 106 down to MY commuting time, not Daves. Lynn didn't sound so sexy.. but kajarii did ;)

Battery Life could maybe be better... I have been spoilt by Sony players, having said that, battery life is perhaps the only thing to recommend Sony players.

Monday 16 February 2009

dell mini ram trouble

Odd problem with my dell mini on Saturday. I needed to get a few packages up to ubuntu intrepid versions, and found that wasn't a trivial affair given the "lpia" archictecture that the pre-installed 'dellbuntu' insists upon.

I was installing dependencies, but they were still appearing to be missing. Very odd, so I decide to install the vanilla 32 bit Ubuntu8.10.

I partition the drive (ext2 with no swap) and stick in an 8.10 usb startup disk. Start the install and BOOM!! major problems at 27%. I get warnings that "source and destination files don't match".... "you probably have a dirty CD, or a damaged hard-drive".

Bummer - it has to be the hard-drive right? We all know these solid state drives are friable?

A quick google suggests several folks have had trouble with the dell mini drives, so I was 100% certain that was the problem.
Anyway... long story short, it turns out it wasn't that at all.

My RAM was hosed. memtest86 on the startup disk (which I tried on a whim whilst googling for SSD device checking software) found 40,000 errors at a very specific range of addresses.

So I popped in a different ram block, and voila! All is well. Ubuntu 8.10 was running flawlessly 20 minutes later.

On the off chance any ubuntu devs reading this.. it might be worth adding a line to the error dialog stating that the memory could be an issue as well as the more likely CD or hardrive cause.

Even more importantly, THANKYOU for having that memtest86 on the install media by default.. you saved me from ringing dell support (a fate worse than death methinks?)

Wednesday 4 February 2009

Using binary data in python

For those who don't know, I am writing a python gui-frontend to an existing mysql database.

The original program that wrote out the data was written by an old-school C guy. Some of the data has been put into the tables in a "blob" format. Example
mysql> describe esttable;
| Field | Type | Null | Key | Default | Extra |
| serialno | int(11) | NO | PRI | 0 | |
| data | blob | YES | | NULL | |

so when I read this table with the MySQLdb module (a third party module for python) I get a string like this.
import MySQLdb
Cursor.execute('select data from esttable where serialno=31599')

You will note that this is packed with characters which need to be handled with care (understatement!)

So how to make sense of it?
One way is to use python's build in Struct module, which allows conversion between python and C types of variable.

after a bit of decoding.... I noticed the above is in chunks of 8 bytes, with /x00 at every 2,6,7 position. I also realise (by experiment) that the data contains 2 distinct unsigned doubles so the following code works for me.
import struct
def blobToList(blob):
for i in range(0,len(blob),8):
pythonlist.append("%s %s $%d.%02d"%(number,item,cost/100,cost%100))
return pythonlist
if __name__=="__main__":
testdata ='\x01\x00o\x00\n\x0f\x00\x00\x04\x00\xc9\x00\x00\x00\x00\x00'
print blobToList(testdata)

which gives me the following output
['1 111 $38.50', '4 201 $0.00', '1 1001 $28.50', '2 1415 $80.00']

and that makes more sense to me (just).

Friday 16 January 2009

Wikipedia for young minds

Another random idea inspired by real life.

Assuming there isn't (see picture)... wouldn't it be nice if there was a version of wikipedia aimed at kids under 12.
Same as wikipedia (and linking to it), but less verbose, simplified language, and guaranteed free of adult material?

Heck that would be a great resource for junior schools, teachers and parents alike.

Possible Names - "WikiJunior", "Junior Wikipedia", "PaedoPedia" (ok... one of the 1st 2)

Monday 12 January 2009

Dell Mini-9 update DISASTER!

This post is about the dell-mini-9.
A netbook with pre-installed ubuntu 8.04. These machines use a separate repo for updates, (suffix http://dell-mini.archive.canonical,com) so would you expect the following to happen?

After weeks with no new packages for the standard dell-mini ubuntu repos, last night saw me pulling down 170MB.
(note - this is a vanilla ubuntu 8.04 dell-mini, first used in 'dec 08)
however, these upgrades have left me with several annoying issues.

1. an x-server update bust my uk keyboard layout. #~{}]["£ all moved around

2. New Firefox and/or WebFav packages wiped all my browser history/usernames/passwords AND removed all my add-ons. Furthermore I got an annoying "Yahoo" home page, default search and a task bar (the bloody screen is small enough, thanks)
Read again - I lost my homepage, all addons, history and bookmarks

3. the upgrading of lsb-base (3.2-4ubuntu1) to 3.2-4ubuntu1netbook1 meant that the mysql-server-5.0 wouldn't start, as I got an error about an unbound variable. Mysql is mission critical on this machine.

Anyway, I have smart friends on irc (thanks jlindsay and Peter64) and I am now fixed up again.
However, these machines are aimed at a broad spectrum of users who would be more inconvenienced than I.

For the sake of the ubuntu and gnu/linux community... please hold these upgrades back for further testing before unleashing them on new linux users.

Sunday 11 January 2009

Richard Querin - I love you!

Thanks to the aggregation, I have become a huge fan of Richard Querin's Work.
I am particulary grateful for his post about an easy way to CREATE YOUR OWN FONT.

Anyway, my 10 year old daughter followed this advice, and I noticed today that she has not only made such a font, but has tweaked it with fontforge (an apt-get away for us debian derivatives) and is now using it as her default application/Desktop/Window-Title and Document Font.

linux truly is flippin' awesome.

here is her desktop... (click image for a larger view)

Thursday 8 January 2009

Implementing a Custom Widget using pyqt4.

Pat from the linux link tech show (and others) have told me to stop whinging, and solve my own IT worries. I won't go over these again here, but scroll down for the full story.
So I dusted down all the programming manuals on the shelf and got to it, and I have to say, so far so good, I have spend many happy hours coding, and am REALLY enjoying the experience.

The tools - python and the qt4 toolkit, mysql, mysqldb, qt-designer and SPE (a wonderful python IDE)

Tomorrow, I have a meeting with a colleague, and I need to show him where I'm at. Thought I'd share here also. Here's a screenshot.

A thing of beauty IMHO!

Anyway, I was delighted how easy PyQt4 makes it to embed a custom widget. My little dental chart slotted into the tabPageWidget above is such a thing. The code is below for all to laugh at.
Note - if you have python and pyqt4 installed, this script will run as a top-level window, something ALL qt widgets are capable of doing. Very cool.

The chartWidget inherits from the QWidgetClass, but overwrites several methods, including the all-important paint method. So today I learned how to draw rectangles, rounded rectangles, lines and points again. QCanvas is like a big etch-a-sketch for nerds. Much Fun.

NOTE - this code borrows heavily from the "" example in chapter 11 of the pyqt4 book by Mark Summerfield.

#!/usr/bin/env python
from __future__ import division
from PyQt4 import QtGui,QtCore

class chartWidget(QtGui.QWidget):
'''a custom widget to show a standard UK dental chart - allows for user navigation with mouse and/or keyboard'''
def __init__(self, parent=None):
self.grid = [[18,17,16,15,14,13,12,11,21,22,23,24,25,26,27,28],[48,47,46,45,44,43,42,41,31,32,33,34,35,36,37,38]]
self.selected = [0, 0]
def sizeHint(self):
return QtCore.QSize(600, 150)
def minimumSizeHint(self):
return QtCore.QSize(240, 80)
def mousePressEvent(self, event):
xOffset = self.width() / 16
yOffset = self.height() / 2
x= event.x()//xOffset
if event.y() < yOffset:
y = 0
y = 1
self.selected = [x, y]

def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_Left:
self.selected[0] = 15 if self.selected[0] == 0 else self.selected[0] - 1
elif event.key() == QtCore.Qt.Key_Right:
self.selected[0] = 0 if self.selected[0] == 15 else self.selected[0] + 1
elif event.key() == QtCore.Qt.Key_Up:
self.selected[1] = 1 if self.selected[1] == 0 else self.selected[1] - 1
elif event.key() == QtCore.Qt.Key_Down:
self.selected[1] = 0 if self.selected[1] == 1 else self.selected[1] + 1

def paintEvent(self,event=None):
painter = QtGui.QPainter(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing, True)
xOffset = (self.width() - midline) / 16 #cell width
yOffset = (self.height() - midlineV) / 2 #cell height

for x in range(16):
if x>7:
for y in range(2):
cell = self.grid[y][x]
rect = QtCore.QRectF(x * xOffset + midx, y * yOffset+y*midlineV,xOffset, yOffset).adjusted(0.5, 0.5, -0.5, -0.5)
color = QtGui.QColor("#ddddff")
if str(cell)[1] in ("8","7","6","5","4"): #molars
if backTooth:
irw=outerRect.width()/4 #inner rectangle width
irh=outerRect.height()/4 #inner rectangle height

#draw a rectangle around the selected tooth, but don't overwrite the centre
if [x, y] == self.selected:
painter.setPen(QtGui.QPen(, 3))

if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
form = chartWidget()