The repository is signed with a separate GnuPG key, which I signed with my main keys. You can download it or retrieve it from a keyserver, then you should add it to your APT keyring:
# For the "upstream" version of wicd deb http://jupiter.hanskalabs.net/debian/ wicd main
Obviously these builds are not guaranteed to work but, since I myself use these autobuilt packages, I usually try to fix problems as soon as I notice them. If you find bugs, or have proposals to improve wicd, please report them on launchpad!
$ gpg --keyserver pgp.mit.edu --recv-keys 4D00F190 # gpg --export 4D00F190 apt-key add -
QComboBox. I can't use a standard Qt model (I need to store python objects in it), so I subclassed QAbstractTableModel. Here it is, it's a fairly generic subclass of
QAbstractTableModel, with variable number of columns:
class QGenericTableModel(QAbstractTableModel): def __init__(self, columns, parent=None, *args): super(QGenericTableModel, self).__init__(parent, *args) self.columns = columns self.headers = self.table =  def rowCount(self, parent=QModelIndex()): return len(self.table) def columnCount(self, parent): return self.columns def row(self, row): return self.table[row] def setData(self, index, value, role): if index.isValid() and role == Qt.EditRole: row = index.row() t = self.table[row] if index.column() >= self.columns: return False if isinstance(value, QVariant): t[index.column()] = value.toString().simplified() else: t[index.column()] = value self.emit(SIGNAL('dataChanged'), index, index) return True return False def data(self, index, role = Qt.DisplayRole): if not index.isValid(): return QVariant() if (index.row() >= len(self.table)) or (index.row() < 0): return QVariant() if role == Qt.DisplayRole: return QVariant(self.table[index.row()][index.column()]) return QVariant() def insertRow(self, row, parent=QModelIndex()): self.insertRows(row, 1, parent) def insertRows(self, row, count, parent=QModelIndex()): self.beginInsertRows(parent, row, row+count-1) for i in xrange(count): self.table.insert(row, ['',]*self.columns) self.endInsertRows() return True def removeRow(self, row, parent=QModelIndex()): self.removeRows(row, 1, parent) def removeRows(self, row, count, parent=QModelIndex()): self.beginRemoveRows(parent, row, row+count-1) for i in reversed(xrange(count)): self.table.pop(row+i) self.endRemoveRows() return True def flags(self, index): if not index.isValid(): return Qt.ItemIsEnabled return super(QGenericTableModel, self).flags(index) Qt.ItemIsEditable def headerData(self, column, orientation, role = Qt.DisplayRole): if role != Qt.DisplayRole: return QVariant() if orientation == Qt.Horizontal: if column >= self.columns: return QVariant() return self.headers[column] return QVariant() def setHeaderData(self, column, orientation, value, role = Qt.EditRole): self.headers[column] = valueIt might not be perfect, but it works, and it's all that matters at the moment (since it's my first serious PyQt4 project). Oh, well, it worked until I tried to use it with an editable
QComboBox. And here's the app code (for the full working code, insert the above class right after the imports):
import sys from PyQt4.QtGui import * from PyQt4.QtCore import * app = QApplication(sys.argv) model = QStandardItemModel() for i, word in enumerate(['saluton', 'gxis la revido', 'hello', 'goodbye']): item = QStandardItem(word) model.setItem(i, 0, item) #model = QGenericTableModel(1) #for i, word in enumerate(['saluton', 'gxis la revido', 'hello', 'goodbye']): # model.insertRow(i) # index = model.index(i, 0) # model.setData(index, word, Qt.EditRole) combo = QComboBox() filterModel = QSortFilterProxyModel(combo) completer = QCompleter(combo) filterModel.setFilterCaseSensitivity(Qt.CaseInsensitive) filterModel.setSourceModel(model) filterModel.setFilterKeyColumn(0) completer.setModel(filterModel) completer.setCompletionColumn(0) completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) combo.setEditable(True) combo.setCompleter(completer) combo.setModel(model) combo.setModelColumn(0) if combo.isEditable(): app.connect(combo.lineEdit(), SIGNAL('textEdited(QString)'), filterModel.setFilterFixedString) combo.setModel(model) combo.setModelColumn(0) combo.show() sys.exit(app.exec_())Now, try running it. It works well when I use a
QStandardItemModel. Then, try decommenting the part where I use my subclassed model: it just doesn't work: clicking on any item makes the
QComboBoxnot change its
currentIndex, and this only happens if the combobox is editable. I suspect I'm forgetting to override some function in my model, but I don't know what exactly to override, and Google didn't help me. This is also backed up by the fact that the exact same behaviour shows up when, instead of a
QComboBox, I try to autocomplete on a
QLineEdit. So, dear readers: can anyone explain what is happening? Thanks in advance! UPDATE: it turned out that the culprit was the following bit inside
if role == Qt.DisplayRole: return QVariant(self.table[index.row()][index.column()])Adding also
Qt.EditRoleto the list of possible choices fixed the bug. YAY!
|ansgar||407 accepts (0.800 per day)||71 rejects (0.139 per day)||53 comments (0.104 per day)|
|dak||12 accepts (0.024 per day)||1 rejects (0.002 per day)||0 comments (0.000 per day)|
|dktrkranz||4319 accepts (8.485 per day)||381 rejects (0.749 per day)||104 comments (0.204 per day)|
|joerg||100 accepts (0.196 per day)||12 rejects (0.024 per day)||1 comments (0.002 per day)|
|mhy||214 accepts (0.420 per day)||14 rejects (0.028 per day)||5 comments (0.010 per day)|
|stew||67 accepts (0.132 per day)||16 rejects (0.031 per day)||7 comments (0.014 per day)|
|tolimar||1480 accepts (2.908 per day)||93 rejects (0.183 per day)||84 comments (0.165 per day)|
|twerner||278 accepts (0.546 per day)||53 rejects (0.104 per day)||26 comments (0.051 per day)|
20110516120252 process-upload dak Processing changes file gwc_0.21.16~dfsg-1_amd64.changesAlex was the super-fast FTP Team member behind the quickest accept, do you want to beat him? Join FTP Team
20110516120258 process-upload dak Moving to new gwc_0.21.16~dfsg-1_amd64.changes
20110516120339 process-new tolimar NEW ACCEPT: gwc_0.21.16~dfsg-1_amd64.changes
/etc/, so you won't be annoyed anymore during upgrades (and, let's say it, they shouldn't have been there since the beginning). However
/etc/bash_completion.d/is still around for compatibility reasons. If you want to enable a completion, just symlink it there from
/usr/share/bash-completion/completions/. If there are enough requests, I might do a simple
compdisablescript to create them. I'd appreciate if adventurous people could test it, and report bugs (if any, hopefully). And don't be scared by the tons of messages about removed conffiles Please beware that the "detection mechanism" of appropriate completions is not entirely foolproof: it might need some hacking upstream (adding meta-headers to completion files?), so I'll try to improve it in future.
So, say, using the above example, you want to see the
sites/directory of revision 20000: here you are (compare with current). This obviously works with files too.
\newcommand. You can make commands with arguments having a default value though. Now, I found myself in need of having such a functionality. I had to google a while, but finally found it, so I'm posting it here, both to share knowledge and to remember it in future :) Suppose you want to do something like
\mycmd foo bla, doing different things (in my case, I needed to add the second argument enclosed by parenthesis in the TOC and other places, but only if it was present) Here's the code:
\def\mycmd#1 \def\tempa #1 \futurelet\next\mycmd@i % Save first argument \def\mycmd@i \ifx\next\bgroup\expandafter\mycmd@ii\else\expandafter\mycmd@end\fi %Check brace \def\mycmd@ii#1 ... %Two args \def\mycmd@end ... %Single argsIn the last two lines of the code, use
\tempafor your first argument, and
#1for your second optional argument. If you need spaces there (maybe as part of some text), be sure to escape them with a backslash
\. I think it's expandable for additional arguments, but I haven't tried, and I don't feel like getting my hands dirty with TeX yet
#!/usr/bin/python import gudev client = gudev.Client(['rfkill', 'net']) for dev in client.query_by_subsystem('net'): if dev.get_sysfs_attr_as_int("type") != 1: continue driver = dev.get_driver() if not driver: parent = dev.get_parent() if parent: driver = parent.get_driver() # available: wlan, wwan, wimax if dev.get_devtype() == 'wlan': type = 'Wireless' else: type = 'Wired' print type, dev.get_name(), driver, dev.get_sysfs_path()However, I have a few remarks. First, I don't understand why ethernet devices don't have a "devtype" attribute. How can I be sure that, failing to be "wlan", "wwan" or "wimax", it's a wired one? (not that any other kind of device comes to my mind, heh). Second, I had to look through Linux sources to find the possible values for DEVTYPE; in particular, I grepped for
./drivers/net/. And the documentation you pointed me to, while being useful for other reasons (the rfkill support), didn't solve my doubts about the sysfs hierarchy. Rather, your comment about DEVTYPE helped quite a lot in this particular problem. Thank you!
#!/usr/bin/python import gudev client = gudev.Client(['rfkill', 'net']) for dev in client.query_by_subsystem('net'): if dev.get_sysfs_attr_as_int("type") != 1: continue driver = dev.get_driver() if not driver: parent = dev.get_parent() if parent: driver = parent.get_driver() print type, dev.get_name(), driver, dev.get_sysfs_path()This will print all network devices with ethernet-encapsulated packets (that's what sysfs type "1" is). Here's the output on my system:
I'm still missing how to reliably detect if a device is a "wired" or a "wireless" one. I suspect that checking the existence of
eth0 e1000e /sys/devices/pci0000:00/0000:00:19.0/net/eth0 wlan0 b43 /sys/devices/pci0000:00/0000:00:1c.1/0000:10:00.0/ssb0:0/net/wlan0
Something's definitely wrong.
# free -m -bash: fork: Cannot allocate memory
Then, you need some python code to use the mapnik library. Let's call it
# apt-get install python-mapnik
#!/usr/bin/python import mapnik ### # Configuration ### style = 'style.xml' output = 'output.svg' width = 1280 height = 800 bbox = '12.58664,37.65759,12.5945,37.66325' ### # Don't touch below! ### bbox = bbox.split(',') ll = mapnik.Coord(float(bbox), float(bbox)) tr = mapnik.Coord(float(bbox), float(bbox)) mymap = mapnik.Map(width, height) mapnik.load_map(mymap, style) map_bbox = mapnik.Envelope(ll, tr) mymap.zoom_to_box(map_bbox) mapnik.render_to_file(mymap, output)For the purposes of this post, we're going to create a 1280x800 map -- adjust that to suit your needs -- and we specified a bounding box corresponding to that area. That's composed as west,south,east,north coordinates. Be sure to get them right :-). Now we need to write a Mapnik stylesheet, we'll call it
style.xml. That's a XML document, you can find the documentation at the official website. The skeleton of a stylesheet is as follows:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE Map> <Map bgcolor="lightblue" srs="+proj=latlong +datum=WGS84"> <Style name="..."> ... </Style> <Layer name="..."> <Datasource> ... </Datasource> </Layer> </Map>The stylesheet has an arbitrary number of Style elements and an arbitrary number of Layer elements. The former class of elements define the appearance of the features, while the latter defines where to get data from. Each layer has a Datasource child: here's where you say where to get your data from. We're going to use the osm input plugin, which lets you parse the dump directly. This has some drawbacks:
highway=residentialroads. Each style can have one or more Rule element. These define what elements that particular style should apply to. Each rule can have different rendering settings. The given dump has different roads: residential, unclassified, tertiary and secondary. Let's add a rule for each. Here's the first of them:
<Rule> <Filter>[highway] = 'residential'</Filter> <LineSymbolizer> <CssParameter name="stroke">#ffffff</CssParameter> <CssParameter name="stroke-width">5</CssParameter> <CssParameter name="stroke-linejoin">round</CssParameter> <CssParameter name="stroke-linecap">round</CssParameter> </LineSymbolizer> <TextSymbolizer name="name" face_name="DejaVu Sans Book" size="8" fill="#000" halo_radius="1" spacing="300" placement="line" /> </Rule>It is quite straightforward: you define what elements the rule should apply to, and then add some symbolizer. For a detailed description of each symbolizer, please read the documentation. The example dump also has a railway and some steps. To render them, we use the stroke-dasharray property:
<Rule> <Filter>[highway] = 'steps'</Filter> <LineSymbolizer> <CssParameter name="stroke">#ff0000</CssParameter> <CssParameter name="stroke-width">5.0</CssParameter> <CssParameter name="stroke-dasharray">2,1</CssParameter> </LineSymbolizer> </Rule> <Rule> <Filter>[railway] = 'rail'</Filter> <LineSymbolizer> <CssParameter name="stroke">#222222</CssParameter> <CssParameter name="stroke-width">3</CssParameter> <CssParameter name="stroke-linejoin">round</CssParameter> </LineSymbolizer> <LineSymbolizer> <CssParameter name="stroke">white</CssParameter> <CssParameter name="stroke-width">1</CssParameter> <CssParameter name="stroke-linejoin">round</CssParameter> <CssParameter name="stroke-dasharray">10,20</CssParameter> </LineSymbolizer> </Rule>Also here, it is quite simple. The values given to stroke-dasharray determine the size of the dashes, and they can be alternated. So, for the steps we have 2px of #ff0000 and 1px of trasparency, while for the rail we have 10px of white and 20px of #222222. What if we want to add some markers to the map? Easy, just use another symbolizer, called PointSymbolizer:
<Rule> <Filter>[highway] = 'stop'</Filter> <PointSymbolizer width='10' height='22' file='stop.png' type='png' allow_overlap='true' opacity='1.0' /> </Rule>Here we mark stop signals with a pre-rendered stop.png (only png and tiff are currently supported, and svg support is only in the development version). What to do next? Well, we can make this map really nice. We'll render buildings with a pseudo-3D effect, which, however, does not reflect real heights -- but it's rather nice to see. How to achieve this? BuildingSymbolizer is the answer.
<Rule> <Filter>[building] <> ''</Filter> <BuildingSymbolizer> <CssParameter name="fill">#00ff00</CssParameter> <CssParameter name="fill-opacity">0.4</CssParameter> <CssParameter name="height">0.00007</CssParameter> </BuildingSymbolizer> </Rule>Here we just draw
building=*. Regarding the height, you need to play a bit -- it took a while before I found a good value of it. One thing missing in this stylesheet, is the rendering of coastlines. Since we don't have a coastline here, I'm skipping it, but the easy way is: use another rule. This is really just a trick; OpenStreetMap renders the coastline in a different way respect to other features, that's why I'm using this trick. I'm not posting the whole stylesheet, but you can download it. You can also get the script, the OSM dump and the stop.png image. Now, you should be able to just do:
and you will have your map in
output.svgHere's what you would get if you use the files provided above (click to see the original SVG version):
#!/usr/bin/python # -*- coding: utf-8 -*- import xml.etree.cElementTree as etree def parse(filename): source = open(filename) context = etree.iterparse(source, events=("start", "end")) context = iter(context) event, root = context.next() for event, elem in context: if event == "end": root.clear() continue print elem.tag if __name__ == '__main__': import sys parse(sys.argv)And here the results:
However, since some time, I wanted to learn Vala, so I tried to do this very same task with it. Here's the code:
$ /usr/bin/time ./parsexml.py ~/osmstats/dumps/italy.osm.bz2.20100912.out 1>/dev/null 413.22user 6.15system 8:57.80elapsed 77%CPU (0avgtext+0avgdata 21200maxresident)k 7739552inputs+0outputs (10major+1470minor)pagefaults 0swaps
using Xml; class XmlParser public void parse_file(string path) var handler = SAXHandler(); void* user_data = null; handler.startElement = start_element; handler.user_parse_file(user_data, path); public void start_element(string name, string attr) stdout.printf("%s\n", name); int main(string args) Parser.init(); var parser = new XmlParser(); parser.parse_file(args); Parser.cleanup(); return 0;You need to compile it with:
valac --pkg libxml-2.0 xml.valaAnd here's the result:
Both these codes are, however, CPU-hungry. But at least they don't swap Needless to say that I'm planning to switch my scripts to Vala for 2.0.
$ /usr/bin/time ./xml ~/osmstats/dumps/italy.osm.bz2.20100912.out 1>/dev/null 122.01user 4.03system 3:14.61elapsed 64%CPU (0avgtext+0avgdata 6352maxresident)k 7738984inputs+0outputs (0major+461minor)pagefaults 0swaps