Monthly Archives: January 2009

Time for a re-design.

Hold the phone — I found PyEphem and its exactly what I was looking for. PyEphem provides scientific-grade astronomical computations. Given a date and location, it can compute the positions of, well …anything. So my Orbital Motion application basically becomes a front end GUI while PyEphem does the back-end computations. Perfect. Version 0.0.3 is gonna be way different.

You can stop laughing now

You can now find version 0.0.2 posted in the Ephemeris Project section. I got the data classes created and the functions all lined up. This project is working so well that I’m actually starting to think that I’m no longer crazy. As promised here are some lessons learned with PyQt.

self.stupid = QWidget()

Although all classes can see all other classes, unless you specify “self” for your QWidget definitions they can’t be seen by any other functions within that class. It took me a while to figure out that “self” is needed. I’m sure there is some Id and Ego joke in there somewhere …

You can’t re-use widgets.

When creating QWidgets inside a loop, be sure to create unique QWidgets each time, even for non-visual artifacts. In this example I needed to create a list of TableWidgetItem() containing strings based on calculated dateTime() objects. I discovered that unique dateTime() items for each calculation had to be created in addition to the TableWidgetItem(). If the same dateTime() was used for the multiple calculations over and over, even when its associated to a unique TableWidgetItem(), Python eventually crashes if the subroutine is rerun. Weird, I know. Took me a while to fix it because the solution is counter-intuitive.

self.myTable = QTableWidget()
interval = 3600 # 1 hour example
neededRows = 744 #1 month example assuming above interval
stamps = []
cells = []
initialDateTime = self.timelineStartStamp.dateTime()
for n in range(0,neededRows):
     cells.append(QTableWidgetItem())
     stamps.append(initialDateTime.addSecs(n*interval))
     cells[n].setText(stamps[n].toString(“yyyy.MM.dd hh:mm:ss”))
     self.myTable.setItem(n,0,cells[n])

Signals and Slots.

Setting up signals is pretty easy, especially for QPushButtons and such. But if you’re needing to run a function triggered by a change in a spinbox, make sure you follow the self.connect definition exactly, otherwise it won’t work. In this example, when the spinbox is used an index is generated and passed to the function. The function NEEDS to accept the index, even if you’re not going to use it. Otherwise nothing happens.

def whatever(self)
    self.myBox = QSpinBox()
    self.myBox.setRange(0,89)
    self.connect(self.myBox,SIGNAL(“valueChanged(int)”), self.myBoxChanged)

def myBoxChanged(self, i):
    pass
    #continue function even if you don’t use i

 

Now for the cool part where I get to do work out some Astronomy calculations.

My widget’s got two thingys

I’ve posted version 0.0.1 of “Orbital Motion” on the site under the Ephemeris Project tab. The GUI development is basically finished. Nothing works, but the visual framework is there.

Things started working much better after I ditched QDesigner. Although its a beautiful tool, I found that I had much more control and understanding if I built each widget manually.

Everything in Qt is based on QWidgets with each having its own class, methods, and signals (basically OOP foundations). You can see them all here. Anytime you need something, you make a copy of its class then make changes to that copy by calling its methods. Once you understand that relationship, everything else becomes really easy. I’ve prepared the following diagram and programming example in the hopes of demonstrating this:

import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *

class Main(QMainWindow):
    def __init__(self, parent=None):
       super(Main, self).__init__(parent)
       self.centralThingy = QWidget()
       self.centralLayout = QGridLayout()
       self.thingy1 = QRadioButton(“Thingy 1”)
       self.thingy1.setChecked(True)
       self.thingy2 = QRadioButton(“Thingy 2”)
       self.centralLayout.addWidget(self.thingy1,0,0)
       self.centralLayout.addWidget(self.thingy2,1,0)
       self.centralThingy.setLayout(self.centralLayout)
       self.setCentralWidget(self.centralThingy)

app = QApplication(sys.argv)
form = Main()
form.show()
app.exec_()

Step 1 – Define the Widget. A widget is a fancy name for a box to put stuff in.
Step 2 – Define the Layout. This explains how stuff appears in the box.
Step 3 – Define the Thingys. In this case, two radio buttons. But its could be anything.
Step 4 – Add the Thingys to the Layout. This tells the Layout how to show the stuff.
Step 5 – Set the Layout on the Widget. This puts the stuff in the box.
Step 6 – Set the Widget as the Central Widget. This makes the box visible.

Most PyQt classes follow this principle. The thingys could be tables, the layouts could be policies and the Central Widget could be … well just a plain old Widget, but you get the idea.

OOP with Python just makes sense …

Over the holidays I spent my free time fiddling with Python. I know … exciting isn’t it?

Up until now, I’ve been heavily into Perl, but it was never easy doing any Rapid Application Development for Windows. Personally I prefer Linux, but my Dell came with a shiny Aero theme. So Windows it is.

Perl is way too unstructured and forget about OOP. Any widgets implemented in Perl quickly turns into a “->” nightmare. Not only is Python well structured (no need for {}) but it lends itself to OOP very well. I actually understand it now.

Example:

class object:
     def __init__(self, name):
         self.name = name

thingy = object(“pencil”)
print (“My thingy is a %s.” % thingy.name)

Wow. Can’t get any easier than that. Now that I’ve mastered OOP then, in the tradition of complete programming naivity, I’ll take a bigger bite than I can chew and develop an entire PyQt application from scratch.

Merging my interest in Astronomy and mastery in Python (can you tell I’m being sarcastic here) I’ve decided a PyQt Ephemeris tool would both be challenging and interesting. Plus it would probably look really cool.

I found a great web-based Ephemeris tool HERE that’s a good starting point. So I’ll be posting my PyQt struggles online here for you all to laugh over.