Tuesday, 23 February 2010

PySide tutorial: custom widget painting

A few people have commented on how heavy the past two tutorials have been, and frankly, I agree - modelview is quite a difficult subject to wrap your head around (questions or comments are still welcome!), so I thought it might be an idea to step it down a notch and look at a different topic that I've also been asked a few questions about in the past few days: widget painting.

Qt provides an extensive collection of widgets by default, from push buttons and check boxes to sliders, labels, combo boxes, pretty much everything you see in a standard application of the past 10 or so years. But frequently, you'll want to do something a bit different: whether that is just customising the appearance of an existing widget to creating one from scratch, you don't want to be limited to standard UI controls.

Qt, of course, doesn't limit you - at all. ;)

In this tutorial, you'll discover the basics of painting, and learn to create your own simple (yes, ugly, I didn't bother to make it look pretty. ;)) widget which doesn't do a great deal.

As always, feedback is welcome.

# -*- coding: utf-8 -*-
# Written by Robin Burchell
# No licence specified or required, but please give credit where it's due,
# and please let me know if this helped you. Feel free to contact with corrections or suggestions.
#
# We're using PySide, Nokia's official LGPL bindings.
# You can however easily use PyQt (Riverside Computing's GPL bindings) by
# commenting these and fixing the appropriate imports.
from PySide.QtCore import *
from PySide.QtGui import *
#from PyQt4 import *
#from PyQt4.QtCore import *
#from PyQt4.QtGui import *
import sys

# As always, Qt allows us to subclass objects to override behaviour and generally monkey around
# with them how we want. This is the exact way that custom widget painting operates:
# you subclass the widget that you want as your base, and override paintEvent() to do your own painting.
class CustomWidget(QWidget):
 # We're taking a number and displaying it twice in different formats. Nothing complicated.
 def __init__(self, parent, anumber):
  QWidget.__init__(self, parent)

  # store the passed number
  self._number = anumber

 # This is where our custom painting happens. Qt calls paintEvent() whenever this widget needs
 # to repaint itself.
 #
 # If you ever need to force a widget to repaint, which you will if you e.g. change any data the widget
 # displays, you shouldn't invoke paintEvent manually: instead, you should use QWidget::update().
 # For more information on invoking a repaint, see:
 # http://doc.trolltech.com/4.6/qwidget.html#update
 #
 # For more information on paintEvent, see:
 # http://doc.trolltech.com/4.6/qwidget.html#paintEvent
 def paintEvent(self, ev):
  # We first need to create a painter. Think of the painter as the brains of the rendering operation:
  # they control the pen, and the brush, in order to do the actual drawing operations. More on them later.
  #
  # We pass 'self' to QPainter so it knows what it is painting on.
  # You can also use QPainter::begin() and QPainter::end(), but this method is easier since it will
  # automatically end() when it deallocates.
  # See also:
  # http://doc.trolltech.com/4.6/qpainter.html
  p = QPainter(self)

  # Simple operation here. Let's paint the background spotted red, so we can see that painting
  # is actually occuring. This will look hideous, but feel free to experiment.
  #
  # This demonstrates the first use of QBrush - QBrush is used for any fill operations performed
  # by QPainter. In terms of painting a picture, think of it like this: a painter will pick up a brush
  # to paint broad strokes of the background etc.
  #
  # QBrush is very versatile, you can do all sorts of things with it like drawing textures.
  # For more information on QBrush, see:
  # http://doc.trolltech.com/4.6/qbrush.html
  #
  # This also demonstrates another aspect of the painting system: that of rects. Painting in Qt is done
  # around the concept of small areas where painting occurs, we're using a convenience method here (QWidget::rect())
  # to get the entire area of the widget, but you can define and adjust rects yourself.
  # We'll go into more detail on that another time.
  # For more information on QRect, see:
  # http://doc.trolltech.com/4.6/qrect.html
  p.fillRect(self.rect(), QBrush(Qt.red, Qt.Dense2Pattern))

  # Now, we're going to draw the number we were passed. This won't be using the brush, but the
  # pen: more on that later. Let's just draw some text in the middle left of the widget for now.
  # Thankfully, Qt provides a way to do this for us, so we don't need to do any work on adjusting the rect.
  # For more information on drawing text, see:
  # http://doc.trolltech.com/4.6/qpainter.html#drawText
  p.drawText(self.rect(), Qt.AlignLeft | Qt.AlignVCenter, str(self._number))

  # Now, let's draw a different number (just for demonstration purposes) with a different colour.
  # To do this, we first need to set a new pen - but now you're probably wondering what that is.
  #
  # A pen is, like it's real world counterpart, used to draw fine detail, like text, lines, and outlines.
  # It is the emphasis, where the brush is the background.
  # For more information on QPen, see:
  # http://doc.trolltech.com/4.6/qpen.html
  #
  # QColor is fairly simplistic here, so I won't go into it. We're just using the RGB constructor to create
  # a grey color.
  # For more information, see:
  # http://doc.trolltech.com/4.6/qcolor.html#QColor-2
  p.setPen(QColor(220, 220, 220))

  # Now we've set our grey pen, let's take the value we have, multiply it by two, and draw it on the right hand
  # side of the widget.
  p.drawText(self.rect(), Qt.AlignRight | Qt.AlignVCenter, str(self._number * 2))

class MyMainWindow(QMainWindow):
 def __init__(self, parent):
  QMainWindow.__init__(self, parent)

  # Add content
  central = CustomWidget(self, 666)
  self.setCentralWidget(central)

if __name__ == '__main__':
 app = QApplication(sys.argv)
 sw = MyMainWindow(None)
 sw.show()
 app.exec_()
 sys.exit()

Labels: , , , , ,

Monday, 22 February 2010

PySide tutorial: model view programming, part two

In the previous tutorial (http://blog.rburchell.com/2010/02/pyside-tutorial-model-view-programming.html), we covered the basics of how to create a read-only model, with multiple views presenting the data.

In this tutorial, you will build on the knowledge of what you learned previously, to add the ability to edit, delete, and add data in the model, from any of the views.

As always, feedback is very welcome.

# -*- coding: utf-8 -*-
# Written by Robin Burchell
# No licence specified or required, but please give credit where it's due,
# and please let me know if this helped you. Feel free to contact with corrections or suggestions.
#
# We're using PySide, Nokia's official LGPL bindings.
# You can however easily use PyQt (Riverside Computing's GPL bindings) by
# commenting these and fixing the appropriate imports.
from PySide.QtCore import *
from PySide.QtGui import *
#from PyQt4 import *
#from PyQt4.QtCore import *
#from PyQt4.QtGui import *
import sys

# This is our model. It will maintain, modify, and present data to our view(s).
# For more information on list models, take a look at:
# http://doc.trolltech.com/4.6/qabstractitemmodel.html
class SimpleListModel(QAbstractListModel):
 def __init__(self, mlist):
  QAbstractListModel.__init__(self)

  # Store the passed data list as a class member.
  self._items = mlist

 # We need to tell the view how many rows we have present in our data. see tutorial #3
 def rowCount(self, parent = QModelIndex()):
  return len(self._items)

 # view is asking us about some of our data.
 # see tutorial #3 for more information on this.
 def data(self, index, role = Qt.DisplayRole):
  if role == Qt.DisplayRole:
   return QVariant(self._items[index.row()])
  elif role == Qt.EditRole:
   # The view is asking for the editable form of the data. i.e. unformatted.
   # See the comment in setData().
   return QVariant(self._items[index.row()])
  else:
   return QVariant()

 # the view is asking us to *change* some aspect of our data.
 # as in the above, it can be any aspect of the data, not *just* the information contained in the model.
 # remember to return true if you handle a data change, and false otherwise, always!
 # for more information, see:
 # http://doc.trolltech.com/4.6/qabstractitemmodel.html#setData
 def setData(self, index, value, role = Qt.EditRole):
  # You might be expecting Qt.DisplayRole here, but no.
  # Qt.DisplayRole is the *displayed* value of an item, like, a formatted currency value: "$44.00"
  # Qt.EditRole is the raw data of an item, e.g. "4400" (as in cents).
  if role == Qt.EditRole:
   # set the data.
   # the str() cast here is mostly for peace of mind, you can't perform some operations
   # in python with Qt types, like pickling.
   self._items[index.row()] = str(value.toString().toUtf8())

   # *always* emit the dataChanged() signal after changing any data inside the model.
   # this is so e.g. the different views know they need to do things with it.
   #
   # don't be lazy and pass a huge range of values to this, because it is processing-heavy.
   #
   # because we are a simple list, we only have one index to worry about for topleft/bottom right,
   # so just reuse the index we are passed.
   QObject.emit(self, SIGNAL("dataChanged(const QModelIndex&, const QModelIndex &)"), index, index)
   return True
  # unhandled change.
  return False

 # if you e.g. don't want to make an item selectable, or draggable, here's the place to do it.
 # by default, items are enabled, and selectable, but we want to make them editable too, so we need to
 # reimplement this. of course, this means you can make only specific items selectable, for example,
 # by using the 'index' parameter.
 # For more information, see:
 # http://doc.trolltech.com/4.6/qabstractitemmodel.html#flags
 def flags(self, index):
  return Qt.ItemIsSelectable | Qt.ItemIsEditable | Qt.ItemIsEnabled

 # remove rows from our model.
 # 'row' is the row number to be removed, 'count' are the total number of rows to remove.
 # 'parent' is the 'parent' of the initial row: this is pretty much only relevant for tree models etc.
 # For more information, see:
 # http://doc.trolltech.com/4.6/qabstractitemmodel.html#removeRows
 def removeRows(self, row, count, parent = QModelIndex()):
  # make sure the index is valid, to avoid IndexErrors ;)
  if row < 0 or row > len(self._items):
   return

  # let the model know we're changing things.
  # we may have to remove multiple rows, if not, this could be handled simpler.
  self.beginRemoveRows(parent, row, row + count - 1)

  # actually remove the items from our internal data representation
  while count != 0:
   del self._items[row]
   count -= 1

  # let the model know we're done
  self.endRemoveRows()

 # while we could use QAbstractItemModel::insertRows(), we'd have to shoehorn around the API
 # to get things done: we'd need to call setData() etc.
 # The easier way, in this case, is to use our own method to do the heavy lifting.
 def addItem(self, item):
  # The str() cast is because we don't want to be storing a Qt type in here.
  self.beginInsertRows(QModelIndex(), len(self._items), len(self._items))
  self._items.append(str(item))
  self.endInsertRows()
    

# This widget is our view of the readonly list.
# For more information, see:
# http://doc.trolltech.com/4.6/qlistview.html
class SimpleListView(QListView):
 def __init__(self, parent = None):
  QListView.__init__(self, parent)

  # unlike the previous tutorial, we'll do background colours 'properly'. ;)
  self.setAlternatingRowColors(True)
  
  # we want our listview to have a context menu taken from the actions on this widget
  # those actions will be to delete an item :)
  self.setContextMenuPolicy(Qt.ActionsContextMenu)

  # create a menu item for our context menu that will delete the selected item.
  a = QAction("Delete Selected", self)

  # hook up the triggered() signal on the menu item to the slot below.
  QObject.connect(a, SIGNAL("triggered()"), self, SLOT("onTriggered()"))
  self.addAction(a)

 # this is a slot! we covered signals and slots in tutorial #2,
 # but this is the first time we've created one ourselves.
 @pyqtSlot()
 def onTriggered(self):
  # tell our model to remove the selected row.
  self.model().removeRows(self.currentIndex().row(), 1)

# Our main application window.
# You should be used to this from previous tutorials.
class MyMainWindow(QWidget):
 def __init__(self):
  QWidget.__init__(self, None)

  # main section of the window
  vbox = QVBoxLayout()

  # create a data source:
  self._model = SimpleListModel(["test", "tes1t", "t3est", "t5est", "t3est"])

  # let's add two views of the same data source we just created:
  v = SimpleListView()
  v.setModel(self._model)
  vbox.addWidget(v)

  # second view..
  v = SimpleListView()
  v.setModel(self._model)
  vbox.addWidget(v)

  # bottom section of the window:
  # let's have a text input and a pushbutton that add an item to our model.
  hbox = QHBoxLayout()
  self._itemedit = QLineEdit()

  # create the button, and hook it up to the slot below.
  b = QPushButton("Add Item")
  QObject.connect(b, SIGNAL("clicked()"), self, SLOT("doAddItem()"))

  hbox.addWidget(self._itemedit)
  hbox.addWidget(b)

  # add bottom to main window layout
  vbox.addLayout(hbox)

  # set layout on the window
  self.setLayout(vbox)

 @pyqtSlot()
 def doAddItem(self):
  # instruct the model to add an item
  self._model.addItem(self._itemedit.text())

  # blank the text input.
  self._itemedit.setText("")

# set things up, and run it. :)
if __name__ == '__main__':
 app = QApplication(sys.argv)
 w = MyMainWindow()
 w.show()
 app.exec_()
 sys.exit()

Labels: , , , ,

Saturday, 20 February 2010

PySide tutorial: model view programming, part one

The third PySide tutorial here (part two: signals and slots available at http://blog.rburchell.com/2010/02/simple-pyside-tutorial-2-signals-and.html and part one: basic introduction/hello world: http://blog.rburchell.com/2010/01/simple-pyside-pyqt-tutorial-aimed-at.html), we're going to delve into something a little more involved and unique than the last two tutorials.

You've already seen by now that Qt has some aspects that are very similar to other programming libraries/toolkits, well, here's an idea which may not be so familiar to you, but, harnessed correctly, is very powerful.

If you've programmed much with GUI stuff before in C# or other languages, you'll be very familiar with widgets like QListWidget in Qt 4. Qt does, however, provide a more powerful alternative to standard widgets where you have to manually manage your data.

For those of you who have done much computer science studies, the pattern in this tutorial will be familiar to you: it's known as 'model view controller', but Qt doesn't actually have a controller component: most of that is handled by the view. So we have two main components: the model (the data) and the view (the presentation and logic of that data).

To get our feet wet, we're going to implement a simple, read-only list of data, with the data maintained separately from the actual widget.

On to the actual code!

# Written by Robin Burchell 
# No licence specified or required, but please give credit where it's due, and please let me know if this helped you.
# Feel free to contact with corrections or suggestions.
#
# We're using PySide, Nokia's official LGPL bindings.
# You can however easily use PyQt (Riverside Computing's GPL bindings) by commenting these and fixing the appropriate imports.
from PySide.QtCore import *
from PySide.QtGui import *
#from PyQt4 import *
#from PyQt4.QtCore import *
#from PyQt4.QtGui import *
import sys

# This is our model. It will maintain, modify, and present data to our view(s).
# As this is read-only, it's pretty straightforward, but it can get pretty complex.
# This is something that Qt Development Frameworks/Nokia are aware of and working on, in terms of
# better documentation, as well as a better implementation of all this, but both of those aren't
# really within the scope of this tutorial. ;)
#
# For more information on list models, take a look at:
# http://doc.trolltech.com/4.6/qabstractitemmodel.html
# but do bear in mind there are other models (like tables) available, depending on your data needs.
# Again, beyond the scope of this tutorial for now. :)
class SimpleListModel(QAbstractListModel):
 def __init__(self, mlist):
  QAbstractListModel.__init__(self)

  # Cache the passed data list as a class member.
  self._items = mlist

 # We need to tell the view how many rows we have present in our data.
 # For us, at least, it's fairly straightforward, as we have a python list of data,
 # so we can just return the length of that list.
 def rowCount(self, parent = QModelIndex()):
  return len(self._items)

 # Here, it's a little more complex.
 # data() is where the view asks us for all sorts of information about our data:
 # this can be purely informational (the data itself), as well as all sorts of 'extras'
 # such as how the data should be presented.
 #
 # For the sake of keeping it simple, I'm only going to show you the data, and one presentational
 # aspect.
 #
 # For more information on what kind of data the views can ask for, take a look at:
 # http://doc.trolltech.com/4.6/qabstractitemmodel.html#data
 #
 # Oh, and just  to clarify: when it says 'invalid QVariant', it means a null QVariant.
 # i.e. QVariant().
 #
 # 'index' is of type QModelIndex, which actually has a whole host of stuff, but we
 # only really care about the row number for the sake of this tutorial.
 # For more information, see:
 # http://doc.trolltech.com/4.6/qmodelindex.html
 def data(self, index, role = Qt.DisplayRole):
  if role == Qt.DisplayRole:
   # The view is asking for the actual data, so, just return the item it's asking for.
   return QVariant(self._items[index.row()])
  elif role == Qt.BackgroundRole:
   # Here, it's asking for some background decoration.
   # Let's mix it up a bit: mod the row number to get even or odd, and return different
   # colours depending.
   # (you can, and should, more easily do this using this:
   # http://doc.trolltech.com/4.6/qabstractitemview.html#alternatingRowColors-prop
   # but I deliberately chose to show that you can put your own logic/processing here.)
   #
   # Exercise for the reader: make it print different colours for each row.
   # Implementation is up to you.
   if index.row() % 2 == 0:
    return QVariant(QColor(Qt.gray))
   else:
    return QVariant(QColor(Qt.lightGray))
  else:
   # We don't care about anything else, so make sure to return an empty QVariant.
   return QVariant()

# This widget is our view of the readonly list.
# Obviously, in a real application, this will be more complex, with signals/etc usage, but
# for the scope of this tutorial, let's keep it simple, as always.
#
# For more information, see:
# http://doc.trolltech.com/4.6/qlistview.html
class SimpleListView(QListView):
 def __init__(self, parent = None):
  QListView.__init__(self, parent)

# Our main application window.
# You should be used to this from previous tutorials.
class MyMainWindow(QWidget):
 def __init__(self):
  QWidget.__init__(self, None)

  # main section of the window
  vbox = QVBoxLayout()

  # create a data source:
  m = SimpleListModel(["test", "tes1t", "t3est", "t5est", "t3est"])

  # let's add two views of the same data source we just created:
  v = SimpleListView()
  v.setModel(m)
  vbox.addWidget(v)

  # second view..
  v = SimpleListView()
  v.setModel(m)
  vbox.addWidget(v)

  # bottom section of the window
  hbox = QHBoxLayout()

  # add bottom to main window layout
  vbox.addLayout(hbox)

  # set layout on the window
  self.setLayout(vbox)

# set things up, and run it. :)
if __name__ == '__main__':
 app = QApplication(sys.argv)
 w = MyMainWindow()
 w.show()
 app.exec_()
 sys.exit()

Labels: , , , ,

Friday, 19 February 2010

We now resume your normal service...

Just a quick note to say that we're pretty much back to normal. I've finished restoring all the posts that didn't import properly (the comments I've left for another day, as they're not so straightforward), and fixed the various planet URLs I'm syndicated to.

It's been a pain in the ass to have to migrate everything off a self-hosted, old, crappy desktop+server to a real server and then to blogger, but thank god I'll not have to do this again.

Labels: ,

KDE vs the world

There was some brief discussion today in #kde-devel about a very interesting topic that got me thinking: that of commercial interest.

The exact discussion was about MeeGo, and similar projects, which according to some "reinvent" parts of the KDE stack (user experience level, plasma+kwin vs hildon-desktop in current Maemo, and of course much much more), which in their view is a bad thing due to duplicating effort, and lowering focus on KDE due to the employment of KDE hackers on unrelated projects.

I suppose this isn't as hard to understand as it might seem, at least on face value, but when you think that the goal is much wider than *just* KDE, then perhaps it's not as bad as it seems. After all, getting employed to hack on Qt related technologies, regardless of what they are, is surely going to help bring more attention to Qt (and as a heavily interrelated breed, KDE), right? It's also got to be a good thing that projects like these expand the interest, investment, and activity around the whole stack - because KDE and other projects can only gain from more eyes on the whole system. A kick-ass development stack is no good to anyone if it's not being used.

Above all, it should be remembered that diversity is strength, and that's something that shouldn't be sneezed at.

Labels: ,

Intelligent use of identi.ca and twitter

So since way back when, I had been heavily resistant to the idea of twitter/identi.ca/just about every other fad out there - though I've been gradually going back on this as I have been increasingly realising that they *can* actually be useful. So I started using one, then the other, then neither, then back to one, etcetera. It just felt really horrible.

Today, though, I think I've finally found a workflow which actually *works* for me, and means I can get on with things without distracting myself, and it's blindingly simple.

Those of you who use identi.ca will know that it has an XMPP (gtalk, for the uninitiated) feature, so you can send messages from your IM client, and receive notifications/whatnot there. Sounds like a brilliant idea, right? Well, it is. So my first step was to enable this, but this left me with a problem: I really don't want to have to duplicate everything through two seperate applications.

My first step to try fix this was to set up identi.ca to connect to twitter, and send all my notifications there, and while that part of things worked, it's severely lacking in other ways -- specifically, I can't pull my timeline and stuff into identi.ca, so I miss out on everything going on inside the twitterverse, which is to be honest, quite a lot - not optimal, so I undid this.

I decided I wasn't going to stick with using two applications, though, and kept reading - and finally found tweet.im, which means I can now use XMPP with twitter too. It means I have to copy and paste my updates, it means that I don't have a unified timeline, but hey. It works.

I hope this has been useful to someone.

[for those of you who use twitter, I'd strongly recommend identi.ca. they're a lot better, if not quite as popular/well known.]

Labels: , ,

Wednesday, 17 February 2010

Application compatibility and MeeGo

So, as I mentioned in my previous entry (http://blog.rburchell.com/2010/02/future-of-n900-what-meego-means.html), there isn't a huge need to worry about MeeGo.

After doing some more reading around the internet, I thought I'd best address another facet of this - that of application compatibility.

A lot of people seem to be of the assumption that MeeGo will be completely incompatible with everything out there, and really, this isn't the case.

For starters, take Maemo. MeeGo has a very similar stack to Maemo. It's basically Linux. It has X, Qt and Gtk+, and all the other things you're used to. Software using it will continue to work. It might need some packaging work, but that's not a big deal, and better minds on mine will hopefully come up with ways to minimise the amount of work needed here.

For Qt applications, this will already be very minimal with tools like MADDE helping cross compilation and packaging, and one can only presume that this *will* get better. In fact, Qt if anything, as with Maemo, is *helping* application compatibility, as software written for totally different platforms is easier to get running, due to Qt doing most of the heavy lifting for just about all parts of an application (audio, video, graphics..).

Again, I'd like to appeal for some sanity and a sense of calm here. This isn't the end of things, it's the start of something *much* bigger, and we're all a part of it.

Labels: , ,

Tuesday, 16 February 2010

Meego - RPM vs DEB debate.

Hopefully I've helped settle a few minds with this post to meego-dev.


After a lot (and boy, do I mean a lot) of reading, writing, and
discussing the RPMvsDEB scenario ad nauseum, and a more recent
discussion, I thought it might be good to put some positive energy
into the discussion, so here goes.

I'm sorry to add yet more fuel to this fire, but hopefully this will
help douse the flames, rather than raise them higher.

RPM - Why?
----------

Full disclaimer - I am not a packaging expert, but am writing this to
try summarise what I know for the benefit of the community as a whole
so that we can all channel the energy that has been devoted into this
issue into something more productive and exciting.

I would welcome any commentry on this. Should it be seen as suitable,
perhaps it might be an idea to put something similar to this up as an
FAQ item, as this certainly has been asked frequently.

--

A lot of discussion and questions have arisen over the past few days
through the Maemo and Moblin merge into MeeGo, with a lot of it
centering around one particular issue: that of RPM vs DEB packaging.

The current state of things is as such - Moblin uses RPM, Maemo uses
DEB - but this issue is not as simple as a packaging format, it's an
entire toolset. I'd also like to very quickly and emphathetically note
that this is purely addressing RPM and DEB, not Fedora and Debian, or
yum vs apt-get, or anything like that.

Maemo's toolset, in the form of scratchbox, sbmock, autobuilder and
co) has a number of kinks which have caused all sorts of fun issues,
but at the end of the day, have worked.

Moblin's toolset has equivilents to do package building - but then, a
lot more, which is not currently available for Maemo. There are
various other tools which are needed to further aid development, such
as the image creator
(http://moblin.org/documentation/moblin-image-creator-2/using-moblin-image-creator).

With this in mind, it is clear that Moblin's toolset has a number of
more capabilities to add that will infinitely benefit development and
QA, which is definitely an advantage to MeeGo, unfortunately, some
elements within this chain do not work with RPM.

It may of course be theoretically possible to rework these tools to
work with a different format (or multiple formats), but let us take a
step back and consider how pragmatic a decision that is, when at the
end of the day, we are talking about a format - both of which bear a
lot in common.

While possible, perhaps - just perhaps - there are other more
important priorities to focus on, and a bigger picture - above the
religious warfare - to keep in mind: that while we have gained RPM, we
have also gained organisation, a solid process and great tools built
to engage the wider community.

Labels: ,

The future of the N900, what MeeGo Means!

There's been a lot of chatter recently about the Maemo+Moblin merger, particularly, a lot of fear from N900 owners about the future of their devices, and how MeeGo fits into that. This isn't anything new, I might add - there has been a huge storm on talk.maemo.org the past month about Maemo 6 on the N900. From my perspective at least, this is all fairly ill founded, and I think that a measure of patience and calm is needed.

There isn't a lot of technical reasons why it won't happen with either of them, and with MeeGo at least, it is fully open and community driven, so there should be no *other* obstacles to people teaming up and making it happen. There has already been discussion on #meego about making this happen, and it's only day two!

In the meantime, more updates for Maemo 5 are sure to follow - PR 1.2, the next reasonably sized update is being talked about increasingly on #maemo on freenode, which hopefully means it isn't too far away.

It's very easy to be pessimistic, it's very easy to worry that your device won't be unusable.

Don't take the easy route: fit into the MeeGo community and make things happen.

Labels: , ,

Wednesday, 10 February 2010

A better place

My grandmother died last night. I'm at a bit of a loss for words at the moment, I really don't know what to think or say. I'm lucky in that it's the first loss I've had in my family for quite some time, and I won't pretend that I was particularly close to her...

For most of my life, we lived on separate continents, though we met a few times and talked on the phone. Later in life, when I finally had the physical chance to make contact with her more regularly, I didn't, because it was still 'too far', and I had various issues about her state as she suffered from dementia, etc.

Unfortunately, it has now gone from being 'too far' to 'too late'.

I suppose given her condition for the past few years, she's better off now, and I am thankful for that, but at the same time I can't help but feel a sense of emptiness and regret for not reaching out when I should have, and having let that opportunity slip.

Nan, please know that you always were, and always will be in my heart, and I will treasure the small time we had together forever.

I hope you found that better place.

Labels:

Tuesday, 9 February 2010

Simple PySide tutorial, #2: signals and slots

Second tutorial for PySide, this time focusing on Qt's signals and slots system, trying to provide a simple example of how they can be chained together to provide simpler code.

# Written by Robin Burchell  
# No licence specified or required, but please give credit where it's due, and please let me know if this helped you.
# Feel free to contact with corrections or suggestions.
#
# We're using PySide, Nokia's official LGPL bindings.
# You can however easily use PyQt (Riverside Computing's GPL bindings) by commenting these and fixing the appropriate imports.
from PySide.QtCore import *
from PySide.QtGui import *
#from PyQt4 import *
#from PyQt4.QtCore import *
#from PyQt4.QtGui import *
import sys

class MyMainWindow(QWidget):
 def __init__(self):
  QWidget.__init__(self, None)

  vbox = QVBoxLayout()

  # Let's create two sliders widgets.
  #  http://doc.trolltech.com/4.6/qslider.html
  sone = QSlider(Qt.Horizontal)
  vbox.addWidget(sone)

  stwo = QSlider(Qt.Horizontal)
  vbox.addWidget(stwo)

  # Link the first slider to the second.
  # Think of this as an event and listener system - a signal is an event, and it can have multiple listeners.
  # A slot is a listener to an event.
  #
  # Signals are generally used whenever an action occurs which might require a reaction, or feedback - for example,
  # a user clicking on a button, or a slider value changing.
  #
  # A signal can provide one (or more) parameters, which can then be used in the corresponding slot(s).
  #
  # In this example, we're linking the valueChanged signal to setValue on the second slider, meaning that whenever the
  # first slider is moved, the second slider will move with it.
  #
  # This might look strange when compared with the .NET and other typical methods of doing it, but it
  # really is much more natural and easier to express.
  # See also:
  # http://doc.trolltech.com/4.6/signalsandslots.html
  self.connect(sone, SIGNAL("valueChanged(int)"), stwo, SLOT("setValue(int)"));

  # Set the layout on the window: this means that our button will actually be displayed.
  self.setLayout(vbox)

if __name__ == '__main__':
 app = QApplication(sys.argv)
 w = MyMainWindow()
 w.show()
 app.exec_()
 sys.exit()

Labels: , , ,

Thoughts on network protocols

It was quite some time ago now, but I used to truely think that binary protocols were the spawn of satan, and that text protocols like HTTP were the only way forwards. After recently implementing my first binary protocol in quite a while, in a neat fashion too.. I'd like to revisit this though.

Firstly, I'd like to say that I'm still right. In some ways.

Text protocols are very, very easy to work with from the respects of debugging, writing code to use them, and getting up and running. They're also pretty easy to serialise just about anything with.

On the other hand, writing parsers for text protocols isn't a simplistic operation, especially when you need to take into account fully variable lengths and things like that, even if such characteristics are what make text protocols attractive in the first place...

Binary protocols also don't make things easy for themselves when you take endianness into account, which you inevitably must.. and you also must be very cautious to use fixed length types instead of wildly tossing integers around your application. But that's nothing unexpected.

In summary, it's more of a grey area than I'd have thought some years ago, but I guess that's like a lot of things in life - the more you know, the less sure you are about anything.

(But I still think emacs sucks.)

Labels: ,

Monday, 8 February 2010

Blogging challenges

After many a year of trying my hardest to be independent, and maintaining my own blog, I've decided that enough is enough.. Frankly, I'm mostly tired of having to make it all look pretty, as well as fill it full of words - so I've just decided to focus on the more important side of that, and stuck everything in blogger.

Some of the content (and all of the comments) refused to import, but I'll keep trying on them.

In the meantime, welcome back, and I hope you enjoy your regular, scheduled posting from me (as if..) - but more seriously, I will be posting a lot more often, I think.

Labels: ,