[Thuban-commits] r2886 - in branches/WIP-pyshapelib-Unicode/thuban: . Extensions/wms Thuban Thuban/Lib Thuban/Model Thuban/UI libraries/thuban test
scm-commit@wald.intevation.org
scm-commit at wald.intevation.org
Tue Aug 18 22:04:13 CEST 2009
Author: bramz
Date: 2009-08-18 22:04:10 +0200 (Tue, 18 Aug 2009)
New Revision: 2886
Modified:
branches/WIP-pyshapelib-Unicode/thuban/ChangeLog
branches/WIP-pyshapelib-Unicode/thuban/Extensions/wms/layer.py
branches/WIP-pyshapelib-Unicode/thuban/Thuban/Lib/fileutil.py
branches/WIP-pyshapelib-Unicode/thuban/Thuban/Model/resource.py
branches/WIP-pyshapelib-Unicode/thuban/Thuban/Model/transientdb.py
branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/__init__.py
branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/application.py
branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/classgen.py
branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/colordialog.py
branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/dock.py
branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/mainwindow.py
branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/projdialog.py
branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/projlist.py
branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/resource.py
branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/view.py
branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/viewport.py
branches/WIP-pyshapelib-Unicode/thuban/Thuban/__init__.py
branches/WIP-pyshapelib-Unicode/thuban/Thuban/version.py
branches/WIP-pyshapelib-Unicode/thuban/libraries/thuban/gdalwarp.cpp
branches/WIP-pyshapelib-Unicode/thuban/setup.py
branches/WIP-pyshapelib-Unicode/thuban/test/test_baserenderer.py
Log:
Forward porting trunk (2864:2885] to WIP-pyshapelib-Unicode branch.
Modified: branches/WIP-pyshapelib-Unicode/thuban/ChangeLog
===================================================================
--- branches/WIP-pyshapelib-Unicode/thuban/ChangeLog 2009-08-18 13:35:30 UTC (rev 2885)
+++ branches/WIP-pyshapelib-Unicode/thuban/ChangeLog 2009-08-18 20:04:10 UTC (rev 2886)
@@ -1,3 +1,101 @@
+2009-08-18 Bram de Greve <bram.degreve at bramz.net>
+
+ * Forward porting trunk (2864:2885] to WIP-pyshapelib-Unicode branch.
+
+2009-08-18 Didrik Pinte <dpinte at dipole-consulting.com>
+
+ * NEWS : updated to 1.2.2 release
+ * Thuban/UI/view.py : improved mouse wheel zooming support
+ * Thuban/Model/resource.py : updated for latest GDAL support
+ * Thuban/UI/viewport.py : unconstrainted the scale bounds
+ * Thuban/version.py : updated to 1.2.2 (before setting it back to 1.2.svn)
+
+2009-08-13 Didrik Pinte <dpinte at dipole-consulting.com>
+
+ * Thuban/UI/classgen.py : applying patch from Anthony Lenton solving a
+ problem generating class on 64bits system
+
+2009-08-13 Didrik Pinte <dpinte at dipole-consulting.com>
+
+ * Thuban/UI/projdialog.py : removing inexistant ProjPanel.Clear() method calls
+
+2009-07-09 Didrik Pinte <dpinte at dipole-consulting.com>
+
+ * libraries/thuban/gdalwarp.py : fixing bug loading files with GDAL thanks to
+ Evan Rouault
+ * test/test_baserenderer.py : added a test replicating the problem loading
+ files with GDAL
+
+2009-06-29 Didrik Pinte <dpinte at dipole-consulting.com>
+
+ * Thuban/UI/resource.py : freezing support improved
+
+2009-06-27 Didrik Pinte <dpinte at dipole-consulting.com>
+
+ * Thuban/UI/view.py : added mouse wheel zoom support
+
+2009-06-02 Didrik Pinte <dpinte at dipole-consulting.com>
+
+ * Thuban/UI/projlist.py : removed called to inexistant __del__ method on
+ wx.ListCtrl
+
+2009-05-18 Didrik Pinte <dpinte at dipole-consulting.com>
+
+ * Thuban/UI/colordialog.py : update wxpython lib to wx (will not work with 2.4)
+ * Thuban/UI/dock.py : removed deprecated call to SetSashBorder
+ * Thuban/UI/projdialog.py : fix a utf problem when calling pyprojection
+
+2009-05-18 Didrik Pinte <dpinte at dipole-consultin.com>
+
+ * Thuban/UI/application.py : removed unneeded hardcoded load of extensions
+
+2009-05-18 Didrik Pinte <dpinte at dipole-consulting.com>
+
+ * Thuban/UI/__init__.py, application.py : bugfix for wxpython 2.8
+ * setup.py : removed some unneeded dll's from wxproj compilation
+ * Thuban/Lib/fileutil.py : added get_thuban_dir() method managing freezed path's
+ * Thuban/__init__.py, Thuban/Model/resource.py : uses the new get_thuban_dir() method
+
+2009-05-18 Didrik Pinte <dpinte at dipole-consulting.com>
+
+ * setup.py : updated to run with wxpython 2.8
+
+2009-04-04 Didrik Pinte <dpinte at dipole-consulting.com>
+
+ * Thuban/UI/mainwindow.py : completed command methods by allowing to remove
+ from menu and toolbar
+
+2009-03-30 Didrik Pinte <dpinte at dipole-consulting.com>
+
+ * Thuban/version.py : resetted to svn release (thanks to Bernhard check)
+
+2009-03-20 Didrik Pinte <dpinte at dipole-consulting.com>
+
+ * Thuban/Model/transient.db.py, Thuban/version.py : added support for sqlite3 module
+
+2009-03-18 Didrik Pinte <dpinte at dipole-consulting.com>
+
+ * Thuban/UI/view.py, viewport.py : support mouse double click events
+ * Thuban/UI/mainwindow.py : support tools providing a wx.Bitmap icon in
+ place of a path to Thuban resources
+
+2009-02-19 Didrik Pinte <dpinte at dipole-consulting.com>
+
+ * Thuban/UI/dock.py : bugfix for docking/undocking events
+
+2008-12-03 Didrik Pinte <dpinte at dipole-consulting.com>
+
+ * Extensions/wms/layer.py : added a very simple caching mechanism
+
+2008-12-03 Didrik Pinte <dpinte at dipole-consulting.com>
+
+ * Extensions/wms/layer.py : bugfix in projection management
+
+2008-11-18 Didrik Pinte <dpinte at dipole-consulting.com>
+
+ * libraries/thuban/gdalwarp.cpp : support the use of GDAL MEM driver for GDAL
+ versions >= 1.5
+
2008-007-24 Bernhard Reiter <bernhard at intevation.de>
* NEWS: Added summary of changes since the 1.2.1 release. Fixed some
Modified: branches/WIP-pyshapelib-Unicode/thuban/Extensions/wms/layer.py
===================================================================
--- branches/WIP-pyshapelib-Unicode/thuban/Extensions/wms/layer.py 2009-08-18 13:35:30 UTC (rev 2885)
+++ branches/WIP-pyshapelib-Unicode/thuban/Extensions/wms/layer.py 2009-08-18 20:04:10 UTC (rev 2886)
@@ -102,6 +102,7 @@
self.error_msg = None
self.wms_layers = []
self.capabilities = None
+ self.cached_bbox = None
# Change the cursor to demonstrate that we're busy but working
ThubanBeginBusyCursor()
@@ -124,26 +125,29 @@
self.error_msg = _('No LatLonBoundingBox found for top layer %s')\
% top_layer
return
- top_srs = foo[0][foo[0].index(":")+1:]
+ # extract only the EPSG code from the EPSG string
+ # received 'EPSG:4326' but keep only '4326'
+ top_srs = foo[0].split(':')[1]
+ # LatLonBox of the top layer
+ bbox = self.wmsserver.contents[top_layer].boundingBoxWGS84
+ self.latlonbbox = (float(bbox[0]),
+ float(bbox[1]),
+ float(bbox[2]),
+ float(bbox[3]))
+
# BoundingBox of the top layer
- bbox = self.wmsserver[top_layer].boundingBox
+ # Do we really need to know the bbox not in WGS84 ?
+ bbox = self.wmsserver.contents[top_layer].boundingBox
if bbox is None or len(bbox) == 0:
- self.error_msg = _('No BoundingBox found for layer %s and EPSG %s')\
- % (top_layer, top_srs)
+ # self.error_msg = _('No BoundingBox found for layer %s and EPSG %s')\
+ # % (top_layer, top_srs)
self.bbox = None
else :
self.bbox = (float(bbox[0]),
float(bbox[1]),
float(bbox[2]),
float(bbox[3]))
-
- # LatLonBox of the top layer
- bbox = self.wmsserver[top_layer].boundingBoxWGS84
- self.latlonbbox = (float(bbox[0]),
- float(bbox[1]),
- float(bbox[2]),
- float(bbox[3]))
if self.bbox is None:
self.bbox =self.latlonbbox
@@ -328,6 +332,14 @@
bbox_dict = { 'minx': bbox[0], 'miny': bbox[1],
'maxx': bbox[2], 'maxy': bbox[3] }
+ if self.cached_bbox is not None:
+ for i in xrange(4):
+ if self.cached_bbox[i] != bbox[i]:
+ break
+ else:
+ if self.cached_response is not None:
+ return self.cached_response, self.format
+ self.cached_bbox = bbox
# Change the cursor to demonstrate that we're busy but working
ThubanBeginBusyCursor()
@@ -339,7 +351,9 @@
bbox=bbox,
size=(width, height),
format=self.wmsformat,
- transparent=True)
+ transparent=False)
+ self.cached_response = wms_response.read()
+
ThubanEndBusyCursor()
- return wms_response.read() , self.format
+ return self.cached_response , self.format
Modified: branches/WIP-pyshapelib-Unicode/thuban/Thuban/Lib/fileutil.py
===================================================================
--- branches/WIP-pyshapelib-Unicode/thuban/Thuban/Lib/fileutil.py 2009-08-18 13:35:30 UTC (rev 2885)
+++ branches/WIP-pyshapelib-Unicode/thuban/Thuban/Lib/fileutil.py 2009-08-18 20:04:10 UTC (rev 2886)
@@ -13,6 +13,7 @@
import os
import os.path
+import sys
from tempfile import mktemp
from string import join
@@ -159,7 +160,6 @@
Under posix systems use the os.expanduser() method.
Under Win32 try to read the "Explorer/Shell Folders/" value "AppData".
"""
-
if os.name == 'posix':
dir = os.path.expanduser("~/.thuban")
if not os.path.isdir(dir):
@@ -191,13 +191,24 @@
dir = os.path.join(guess, "thuban")
if not os.path.isdir(dir):
os.mkdir(dir)
+
+ return str(dir)
- return dir
-
else:
raise RuntimeError(_("No implementation of get_application_dir"
" available for platform") + os.name)
+def get_thuban_dir():
+ """Determine the path to the where the Thuban directory is stored
+
+ This method is needed to solve problems when the application is frozen
+ """
+ if hasattr(sys, 'frozen'):
+ res_path = os.path.normpath(os.path.dirname(sys.executable))
+ else:
+ res_path = os.path.normpath(os.path.join(os.path.dirname(__file__), os.pardir ,os.pardir))
+ return res_path
+
# bind the appropriate version of relative_filename for the platform
# we're currently running on.
if os.name == "posix":
Modified: branches/WIP-pyshapelib-Unicode/thuban/Thuban/Model/resource.py
===================================================================
--- branches/WIP-pyshapelib-Unicode/thuban/Thuban/Model/resource.py 2009-08-18 13:35:30 UTC (rev 2885)
+++ branches/WIP-pyshapelib-Unicode/thuban/Thuban/Model/resource.py 2009-08-18 20:04:10 UTC (rev 2886)
@@ -20,16 +20,15 @@
import Thuban
from Thuban import _
-from Thuban.Lib.fileutil import get_application_dir
+from Thuban.Lib.fileutil import get_application_dir, get_thuban_dir
from Thuban.Model.xmlreader import XMLReader
from Thuban.Model.xmlwriter import XMLWriter
from Thuban.Model.proj import Projection, ProjFile
from xml.sax import SAXParseException
-projdir = \
- os.path.join(Thuban.__path__[0], os.pardir, "Resources", "Projections")
+projdir = os.path.join(get_thuban_dir(), "Resources", "Projections")
PROJ_EXT = ".proj"
@@ -39,8 +38,10 @@
#
# GDAL is supported if we can import both the thuban specific gdalwarp
# module and the GDAL python bindings.
-for _module in ("gdalwarp", "gdal"):
+osgeo_gdal_version = False
+for _module in ("gdalwarp", "osgeo.gdal"):
try:
+ print "importing %s" % _module
__import__(_module)
except ImportError, val:
gdal_support_status = (_("No GDAL support because module '%s'"
Modified: branches/WIP-pyshapelib-Unicode/thuban/Thuban/Model/transientdb.py
===================================================================
--- branches/WIP-pyshapelib-Unicode/thuban/Thuban/Model/transientdb.py 2009-08-18 13:35:30 UTC (rev 2885)
+++ branches/WIP-pyshapelib-Unicode/thuban/Thuban/Model/transientdb.py 2009-08-18 20:04:10 UTC (rev 2886)
@@ -24,15 +24,20 @@
# and named.
# The sqlite2 boolean variable is used to manage specific part of the code
try:
- # Using SQLITE 2.x
+
+ # Using SQLITE 3.x
+ import sqlite3 as sqlite
sqlite2 = True
- from pysqlite2 import dbapi2 as sqlite
except ImportError:
- # Using SQLITE 1.x
- sqlite2 = False
- import sqlite
+ try :
+ # Using SQLITE 2.x
+ sqlite2 = True
+ from pysqlite2 import dbapi2 as sqlite
+ except ImportError:
+ # Using SQLITE 1.x
+ sqlite2 = False
+ import sqlite
-
from base import TitledObject
import table
Modified: branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/__init__.py
===================================================================
--- branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/__init__.py 2009-08-18 13:35:30 UTC (rev 2885)
+++ branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/__init__.py 2009-08-18 20:04:10 UTC (rev 2886)
@@ -80,7 +80,7 @@
Thuban.set_internal_encoding(encoding)
-install_wx_translation()
+#install_wx_translation()
# We define a function that will return the internal representation
# for a value returned from wxString.
Modified: branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/application.py
===================================================================
--- branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/application.py 2009-08-18 13:35:30 UTC (rev 2885)
+++ branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/application.py 2009-08-18 20:04:10 UTC (rev 2886)
@@ -32,6 +32,8 @@
from Thuban.Model.layer import RasterLayer
import Thuban.Model.resource
+from Thuban.UI import install_wx_translation
+
from extensionregistry import ext_registry
import view
@@ -53,10 +55,13 @@
class derived from wxApp. In Thuban the application class holds
references to the main window and the session.
"""
-
- def OnInit(self):
+
+ def OnInit(self):
+
sys.excepthook = self.ShowExceptionDialog
+ install_wx_translation()
+
# Initialize instance variables before trying to create any
# windows. Creating windows can start an event loop if
# e.g. message boxes are popped up for some reason, and event
@@ -115,7 +120,7 @@
try:
if tb.tb_next is not None:
# The ImportError exception was raised from
- # inside the thubanstart module.
+ # inside the thubanstart module.
sys.stderr.write(_("Cannot import the thubanstart"
" module\n"))
traceback.print_exc(None, sys.stderr)
Modified: branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/classgen.py
===================================================================
--- branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/classgen.py 2009-08-18 13:35:30 UTC (rev 2885)
+++ branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/classgen.py 2009-08-18 20:04:10 UTC (rev 2886)
@@ -409,7 +409,7 @@
self.numGroupsChanging = False
self.steppingChanging = False
- self.numGroupsCtrl.SetRange(1, sys.maxint)
+ self.numGroupsCtrl.SetRange(1, 2**31-1)
self.numGroupsCtrl.SetValue(1)
self.stepCtrl.SetValue("1")
@@ -832,7 +832,7 @@
_("Retrieve from Table"))
self.spin_numClasses = wx.SpinCtrl(self, -1, style=wx.TE_RIGHT)
- self.spin_numClasses.SetRange(2, sys.maxint)
+ self.spin_numClasses.SetRange(2, 2**31-1)
self.spin_numClasses.SetValue(2)
Modified: branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/colordialog.py
===================================================================
--- branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/colordialog.py 2009-08-18 13:35:30 UTC (rev 2885)
+++ branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/colordialog.py 2009-08-18 20:04:10 UTC (rev 2886)
@@ -22,7 +22,7 @@
# It was not available with the very first versions of wxWindows 2.4
# (I don't know the exact version though)
try:
- from wxPython.lib.colourchooser import wxPyColourChooser
+ from wx.lib.colourchooser import wxPyColourChooser
_wxPyColourChooser = True
wx.InitAllImageHandlers() # should be somewhere at Thuban startup?
except:
Modified: branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/dock.py
===================================================================
--- branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/dock.py 2009-08-18 13:35:30 UTC (rev 2885)
+++ branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/dock.py 2009-08-18 20:04:10 UTC (rev 2886)
@@ -28,8 +28,11 @@
from messages import DOCKABLE_DOCKED, DOCKABLE_UNDOCKED, DOCKABLE_CLOSED
-ID_BUTTON_DOCK = 4001
-ID_BUTTON_CLOSE = 4002
+# Commented ID's because ID's are the same as the ID's of the legend buttons ...
+#ID_BUTTON_DOCK = 4001
+#ID_BUTTON_CLOSE = 4002
+ID_BUTTON_DOCK = wx.ID_ANY
+ID_BUTTON_CLOSE = wx.ID_ANY
PANEL_ID = 3141
@@ -54,7 +57,7 @@
self.parent = parent
- #self.SetDockParent(None)
+ self.SetDockParent(parent)
#parent.SetPanel(self)
def Create(self):
@@ -92,7 +95,7 @@
class DockableWindow(Publisher):
def __getattr__(self, attr):
- return getattr(self.__topWindow, attr)
+ return getattr(self.__topWindow, attr)
def __init__(self, parent, id, name, title, dockWindow, orient):
"""Create the dockable window.
@@ -117,6 +120,7 @@
else:
self.__topWindow = self.__floatWindow
+
self.__floatSize = None
self.__floatPosition = None
@@ -192,6 +196,11 @@
self.SetDockSize(self.__dockWindow.GetSize())
if wasVisible: self.Show(True)
+
+ # rebind the events to the correct window
+ # FIXME : should we remove the previous bindings ?
+ self.__topWindow.Bind(wx.EVT_BUTTON, self._OnToggleDock, self.__dockButton)
+ self.__topWindow.Bind(wx.EVT_BUTTON, self._OnButtonClose, self.__closeX)
self.issue(DOCKABLE_DOCKED, self.__id, self)
@@ -226,6 +235,11 @@
self.SetPosition(self.__floatPosition)
if self.__floatSize is not None:
self.SetSize(self.__floatSize)
+
+ # rebind the events to the correct window
+ # FIXME : should we remove the previous bindings ?
+ self.__topWindow.Bind(wx.EVT_BUTTON, self._OnToggleDock, self.__dockButton)
+ self.__topWindow.Bind(wx.EVT_BUTTON, self._OnButtonClose, self.__closeX)
self.__dockPanel.SetSize(self.__topWindow.GetClientSize())
@@ -279,7 +293,7 @@
#
def _OnButtonClose(self, event):
- #self.Close()
+ self.Close()
self.Show(False)
def _OnClose(self, force = False):
@@ -364,11 +378,11 @@
bmp = resource.GetBitmapResource(CLOSE_BMP, wx.BITMAP_TYPE_XPM)
- closeX = wx.BitmapButton(self.__dockPanel, ID_BUTTON_CLOSE, bmp,
+ self.__closeX = wx.BitmapButton(self.__dockPanel, ID_BUTTON_CLOSE, bmp,
size = wx.Size(bmp.GetWidth() + 4,
bmp.GetHeight() + 4),
style = wx.BU_EXACTFIT | wx.ADJUST_MINSIZE)
- closeX.SetToolTip(wx.ToolTip(_("Close")))
+ self.__closeX.SetToolTip(wx.ToolTip(_("Close")))
#
# fill in the sizer in an order appropriate to the orientation
@@ -377,9 +391,9 @@
headerBox.Add(text, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTRE_VERTICAL, 0)
headerBox.Add((1, 5), 1, wx.GROW)
headerBox.Add(self.__dockButton, 0, wx.ALIGN_RIGHT, 0)
- headerBox.Add(closeX, 0, wx.ALIGN_RIGHT | wx.LEFT, 4)
+ headerBox.Add(self.__closeX, 0, wx.ALIGN_RIGHT | wx.LEFT, 4)
else:
- headerBox.Add(closeX, 0, wx.ALIGN_RIGHT | wx.BOTTOM, 4)
+ headerBox.Add(self.__closeX, 0, wx.ALIGN_RIGHT | wx.BOTTOM, 4)
headerBox.Add(self.__dockButton, 0, wx.ALIGN_RIGHT, 0)
headerBox.Add(text, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTRE_VERTICAL, 0)
@@ -392,8 +406,8 @@
sizer.SetSizeHints(self.__dockPanel)
sizer.SetSizeHints(self.__floatWindow)
- self.Bind(wx.EVT_BUTTON, self._OnToggleDock, self.__dockPanel, id=ID_BUTTON_DOCK)
- self.Bind(wx.EVT_BUTTON, self._OnButtonClose, self.__dockPanel, id=ID_BUTTON_CLOSE)
+ self.__topWindow.Bind(wx.EVT_BUTTON, self._OnToggleDock, self.__dockButton)
+ self.__topWindow.Bind(wx.EVT_BUTTON, self._OnButtonClose, self.__closeX)
class DockFrame(wx.Frame):
@@ -453,8 +467,7 @@
sash.SetOrientation(orient)
sash.SetAlignment(align)
sash.SetSashVisible(DockFrame.layout2oppSash[align], True)
- sash.SetSashBorder(DockFrame.layout2oppSash[align], True)
-
+
win = DockableWindow(self, id, name, title, sash, orient)
self.__RegisterDock(name, win)
Modified: branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/mainwindow.py
===================================================================
--- branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/mainwindow.py 2009-08-18 13:35:30 UTC (rev 2885)
+++ branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/mainwindow.py 2009-08-18 20:04:10 UTC (rev 2886)
@@ -106,7 +106,7 @@
# creat the menubar from the main_menu description
self.SetMenuBar(self.build_menu_bar(main_menu))
-
+
# Similarly, create the toolbar from main_toolbar
toolbar = self.build_toolbar(main_toolbar)
# call Realize to make sure that the tools appear.
@@ -197,6 +197,13 @@
if command.IsDynamic():
self.Bind(wx.EVT_UPDATE_UI, self.update_command_ui, id=ID)
+ def unbind_command_events(self, command, ID):
+ """Unbind the necessary events for the given command and ID"""
+ if self.events_bound.has_key(ID):
+ self.Unbind(wx.EVT_MENU, self.invoke_command, id=ID)
+ if command.IsDynamic():
+ self.Unbind(wx.EVT_UPDATE_UI, self.update_command_ui, id=ID)
+
def build_menu_bar(self, menudesc):
"""Build and return the menu bar from the menu description"""
menu_bar = wx.MenuBar()
@@ -266,6 +273,17 @@
else:
print _("Unknown command %s") % name
+ def remove_menu_command(self, menu, name):
+ if name is None:
+ return
+ else:
+ command = registry.Command(name)
+ if command is not None:
+ assert isinstance(menu, wx.Menu)
+ ID = self.get_id(name)
+ menu.Remove(ID)
+ self.unbind_command_events(command, ID)
+
def add_toolbar_command(self, toolbar, name):
"""Add the command with name name to the toolbar toolbar.
@@ -279,7 +297,10 @@
command = registry.Command(name)
if command is not None:
ID = self.get_id(name)
- bitmap = resource.GetBitmapResource(command.Icon(),
+ if isinstance(command.Icon(), wx.Bitmap):
+ bitmap = command.Icon()
+ else:
+ bitmap = resource.GetBitmapResource(command.Icon(),
wx.BITMAP_TYPE_XPM)
toolbar.AddTool(ID, bitmap,
shortHelpString = command.HelpText(),
@@ -288,6 +309,19 @@
else:
print _("Unknown command %s") % name
+ def remove_toolbar_command(self, toolbar, name):
+ """Remove the command with name name from the toolbar
+ """
+ if name is None:
+ return
+ else:
+ command = registry.Command(name)
+ if command is not None:
+ ID = self.get_id(name)
+ assert isinstance(toolbar, wx.ToolBar)
+ toolbar.RemoveTool(ID)
+ self.unbind_command_events(command, ID)
+
def Context(self):
"""Return the context object for a command invoked from this window
"""
Modified: branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/projdialog.py
===================================================================
--- branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/projdialog.py 2009-08-18 13:35:30 UTC (rev 2885)
+++ branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/projdialog.py 2009-08-18 20:04:10 UTC (rev 2886)
@@ -575,8 +575,11 @@
# self.curProjPanel should always contain the most relevant data
# for a projection
if self.curProjPanel is not None:
+ # remove internal_from_wxstring call in profit of a str call
+ # Projection parameters must be str (otherwise swig does not treat them as is)
+ # this is kind of "works for me". I did not took time to investigate the real issue with internal_from_wxstring
parameters = \
- map(internal_from_wxstring,self.curProjPanel.GetParameters())
+ map(str,self.curProjPanel.GetParameters())
if parameters is not None:
return Projection(parameters, self.projname.GetValue())
@@ -806,7 +809,6 @@
self.__falseNorth.Clear()
self.__scale.Clear()
- ProjPanel.Clear(self)
ID_UTMPANEL_ZONE = 4001
ID_UTMPANEL_SOUTH = 4002
@@ -860,7 +862,6 @@
def Clear(self):
self.__zone.SetValue(1)
self.__south.SetValue(False)
- ProjPanel.Clear(self)
def _OnPropose(self, event):
"""Call the propose dialog.
@@ -950,7 +951,6 @@
self.__falseEast.Clear()
self.__falseNorth.Clear()
- ProjPanel.Clear(self)
class GeoPanel(ProjPanel):
"""Projection Panel for a Geographic Projection."""
@@ -987,7 +987,7 @@
return params
def Clear(self):
- ProjPanel.Clear(self)
+ pass
def _DoLayout(self):
sizer = wx.BoxSizer(wx.HORIZONTAL)
Modified: branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/projlist.py
===================================================================
--- branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/projlist.py 2009-08-18 13:35:30 UTC (rev 2885)
+++ branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/projlist.py 2009-08-18 20:04:10 UTC (rev 2886)
@@ -63,8 +63,7 @@
self.Bind(wx.EVT_IDLE, self.OnIdle)
def __del__(self):
- wx.ListCtrl.__del__()
- Publisher.__del__()
+ Publisher.__del__(self)
def _subscribe_proj_files(self):
"""Subscribe to the messages of self.proj_files"""
Modified: branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/resource.py
===================================================================
--- branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/resource.py 2009-08-18 13:35:30 UTC (rev 2885)
+++ branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/resource.py 2009-08-18 20:04:10 UTC (rev 2886)
@@ -14,11 +14,12 @@
import os
import Thuban
+from Thuban.Lib.fileutil import get_thuban_dir
import wx
-bitmapdir = os.path.join(Thuban.__path__[0], os.pardir, "Resources", "Bitmaps")
+bitmapdir = os.path.join(get_thuban_dir(), "Resources", "Bitmaps")
bitmap_extensions = {wx.BITMAP_TYPE_XPM: ".xpm",
wx.BITMAP_TYPE_ANY: ""}
Modified: branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/view.py
===================================================================
--- branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/view.py 2009-08-18 13:35:30 UTC (rev 2885)
+++ branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/view.py 2009-08-18 20:04:10 UTC (rev 2886)
@@ -122,16 +122,18 @@
# subscribe the WX events we're interested in
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
+ self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDoubleClick)
self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
self.Bind(wx.EVT_MIDDLE_DOWN, self.OnMiddleDown)
self.Bind(wx.EVT_MIDDLE_UP, self.OnMiddleUp)
self.Bind(wx.EVT_MOTION, self.OnMotion)
+ self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow)
self.Bind(wx.EVT_SIZE, self.OnSize)
self.Bind(wx.EVT_IDLE, self.OnIdle)
def __del__(self):
- wx.Window.__del__(self)
+ #wx.Window.__del__(self)
ViewPort.__del__(self)
def PreviewBitmap(self):
@@ -401,6 +403,11 @@
self.dragging = 0
self.MouseLeftUp(event)
+ def OnLeftDoubleClick(self, event):
+ """ Handle EVT_LEFT_DCLICK
+ """
+ self.MouseDoubleClick(event)
+
def OnMotion(self, event):
if self.dragging:
self.tool.Hide(self.drag_dc)
@@ -412,6 +419,16 @@
def OnLeaveWindow(self, event):
self.set_current_position(None)
+
+
+ def OnMouseWheel(self, event):
+ """
+ Manages the zoom on wheel event
+ """
+ if event.GetWheelRotation() > 0:
+ factor = 2
+ else: factor = 1.0/2.0
+ self.ZoomFactor(factor, center=(event.m_x, event.m_y))
def OnSize(self, event):
# the window's size has changed. We have to get a new bitmap. If
Modified: branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/viewport.py
===================================================================
--- branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/viewport.py 2009-08-18 13:35:30 UTC (rev 2885)
+++ branches/WIP-pyshapelib-Unicode/thuban/Thuban/UI/viewport.py 2009-08-18 20:04:10 UTC (rev 2886)
@@ -95,6 +95,9 @@
def MouseUp(self, event):
if self.dragging:
self.drag_stop(event.m_x, event.m_y)
+
+ def MouseDoubleClick(self, event):
+ pass
def Cancel(self):
self.dragging = 0
@@ -385,7 +388,7 @@
# into 16bit signed integers.
max_len = max(pwidth, pheight)
if max_len:
- max_scale = 32767.0 / max_len
+ max_scale = sys.maxint #.0 #/ max_len
else:
# FIXME: What to do in this case? The bbox is effectively
# empty so any scale should work.
@@ -399,7 +402,7 @@
if pheight:
scales.append(wheight / pheight)
if scales:
- min_scale = 0.5 * min(scales)
+ min_scale = 0.0001 * min(scales)
else:
min_scale = scale
@@ -431,6 +434,8 @@
scale = min_scale
self.scale = scale
+ if self.scale == 0:
+ raise Exception("Scale exception : %s %s")
# determine new offset to preserve the center
self.offset = (wwidth/2 - scale * pcenterx,
@@ -642,6 +647,10 @@
self.set_current_position(event)
if self.tool is not None:
self.tool.MouseMove(event)
+
+ def MouseDoubleClick(self, event):
+ if self.tool is not None:
+ self.tool.MouseDoubleClick(event)
def shape_selected(self, layer, shape):
"""Receiver for the SHAPES_SELECTED messages. Redraw the map."""
Modified: branches/WIP-pyshapelib-Unicode/thuban/Thuban/__init__.py
===================================================================
--- branches/WIP-pyshapelib-Unicode/thuban/Thuban/__init__.py 2009-08-18 13:35:30 UTC (rev 2885)
+++ branches/WIP-pyshapelib-Unicode/thuban/Thuban/__init__.py 2009-08-18 20:04:10 UTC (rev 2886)
@@ -8,6 +8,7 @@
# Read the file COPYING coming with Thuban for details.
import os
+import sys
# Thuban Message Translation
#
@@ -42,9 +43,9 @@
# Thedirectory holding the translation files (actually they're in
# language specific subdirectories of _message_dir)
-_message_dir = os.path.join(os.path.dirname(__file__), os.pardir, "Resources",
- "Locale")
+
+
def _(s):
"""Return a localized version of the the string s
@@ -56,6 +57,9 @@
"""
return _translation_function(s)
+from Lib.fileutil import get_thuban_dir;
+_message_dir = os.path.join(get_thuban_dir(), "Resources", "Locale")
+
def gettext_identity(s):
"""Default gettext implementation which returns the string as is"""
return s
Modified: branches/WIP-pyshapelib-Unicode/thuban/Thuban/version.py
===================================================================
--- branches/WIP-pyshapelib-Unicode/thuban/Thuban/version.py 2009-08-18 13:35:30 UTC (rev 2885)
+++ branches/WIP-pyshapelib-Unicode/thuban/Thuban/version.py 2009-08-18 20:04:10 UTC (rev 2886)
@@ -41,7 +41,7 @@
#
thuban_branch = "1.2"
-thuban_release = "svn"
+thuban_release = "2"
@@ -115,21 +115,29 @@
# PySQLite
try:
- from pysqlite2 import dbapi2 as sqlite
+ import sqlite3 as sqlite
except ImportError:
- import sqlite
+ try:
+ from pysqlite2 import dbapi2 as sqlite
+ except ImportError:
+ import sqlite
versions['pysqlite'] = sqlite.version
versions['pysqlite-tuple'] = make_tuple(sqlite.version)
# SQLite
try:
- from pysqlite2._sqlite import sqlite_version
+ from sqlite3 import sqlite_version
versions['sqlite'] = sqlite_version
versions['sqlite-tuple'] = make_tuple(sqlite_version)
except ImportError:
- from _sqlite import sqlite_version
- versions['sqlite'] = sqlite_version()
- versions['sqlite-tuple'] = make_tuple(sqlite_version())
+ try:
+ from pysqlite2._sqlite import sqlite_version
+ versions['sqlite'] = sqlite_version
+ versions['sqlite-tuple'] = make_tuple(sqlite_version)
+ except ImportError:
+ from _sqlite import sqlite_version
+ versions['sqlite'] = sqlite_version()
+ versions['sqlite-tuple'] = make_tuple(sqlite_version())
# GDAL
from Thuban.Model.resource import has_gdal_support
Modified: branches/WIP-pyshapelib-Unicode/thuban/libraries/thuban/gdalwarp.cpp
===================================================================
--- branches/WIP-pyshapelib-Unicode/thuban/libraries/thuban/gdalwarp.cpp 2009-08-18 13:35:30 UTC (rev 2885)
+++ branches/WIP-pyshapelib-Unicode/thuban/libraries/thuban/gdalwarp.cpp 2009-08-18 20:04:10 UTC (rev 2886)
@@ -146,6 +146,7 @@
#include "gdal.h"
#include "gdal_alg.h"
#include "gdal_priv.h"
+
#include "gdalwarper.h"
#include "cpl_string.h"
#include "ogr_srs_api.h"
@@ -160,6 +161,11 @@
#define OPTS_ALPHA 2
#define OPTS_INVERT_MASK_BITS 4
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
CPL_CVSID("$Id$");
static GDALDatasetH
@@ -175,8 +181,14 @@
static int nForcePixels=0, nForceLines=0;
static int bEnableDstAlpha = FALSE, bEnableSrcAlpha = FALSE;
static int bMakeMask, bMakeAlpha, bInvertMask;
-const char *pszSrcFilename = NULL, *pszDstFilename = "MEM:::";
+const char *pszSrcFilename = NULL;
+# if GDAL_VERSION_MINOR < 5
+const char *pszDstFilename = "MEM:::";
+# else
+const char *pszDstFilename = "IMG:::";
+# endif
+
static PyMethodDef gdalwarp_methods[] = {
{ "ProjectRasterFile", ProjectRasterFile, METH_VARARGS },
{ "get_gdal_version", get_gdal_version, METH_VARARGS },
@@ -199,9 +211,7 @@
/************************************************************************/
/* initgdalwarp */
/************************************************************************/
-#ifdef __cplusplus
-extern "C"
-#endif
+
void initgdalwarp(void)
{
PyObject * shapelib = NULL;
@@ -226,7 +236,7 @@
char *pszResult = NULL;
CPLErrorReset();
-
+
hSRS = OSRNewSpatialReference( NULL );
if( OSRSetFromUserInput( hSRS, pszUserInput ) == OGRERR_NONE )
OSRExportToWkt( hSRS, &pszResult );
@@ -238,8 +248,7 @@
pszUserInput );
exit( 1 );
}
-#endif
-
+#endif
OSRDestroySpatialReference( hSRS );
return pszResult;
@@ -699,6 +708,7 @@
/* -------------------------------------------------------------------- */
/* Open source dataset. */
/* -------------------------------------------------------------------- */
+
hSrcDS = GDALOpen( pszSrcFilename, GA_ReadOnly );
if( hSrcDS == NULL )
@@ -1011,8 +1021,11 @@
//GDALDumpOpenDatasets( stderr );
- GDALDestroyDriverManager();
-
+ // Do not call GDALDestroyDriverManager as we will most probably use
+ // ProjectRasterFile more than once.
+ // Thanks to Evan Rouault / 2009-07-09
+ //GDALDestroyDriverManager();
+
# if PY_VERSION_HEX >=0x02040000
if (savedlocale)
setlocale( LC_NUMERIC, savedlocale);
@@ -1101,8 +1114,10 @@
NULL, pszTargetSRS,
TRUE, 1000.0, nOrder );
- if( hTransformArg == NULL )
+ if( hTransformArg == NULL ) {
+ GDALClose( hSrcDS );
return NULL;
+ }
//
// This could happen if the proj library didn't load correctly
@@ -1117,20 +1132,22 @@
/* -------------------------------------------------------------------- */
/* Get approximate output definition. */
/* -------------------------------------------------------------------- */
+ double adfExtent[4];
if( GDALSuggestedWarpOutput( hSrcDS,
GDALGenImgProjTransform, hTransformArg,
- adfDstGeoTransform, &nPixels, &nLines )
- != CE_None )
+ adfDstGeoTransform, &nPixels, &nLines)
+ != CE_None ) {
+ GDALClose( hSrcDS );
return NULL;
-
- GDALDestroyGenImgProjTransformer( hTransformArg );
-
+ }
+
+ GDALDestroyGenImgProjTransformer( hTransformArg );
+
/* -------------------------------------------------------------------- */
/* Did the user override some parameters? */
/* -------------------------------------------------------------------- */
if( dfXRes != 0.0 && dfYRes != 0.0 )
{
- CPLAssert( nForcePixels == 0 && nForceLines == 0 );
if( dfMinX == 0.0 && dfMinY == 0.0 && dfMaxX == 0.0 && dfMaxY == 0.0 )
{
dfMinX = adfDstGeoTransform[0];
@@ -1151,10 +1168,10 @@
{
if( dfMinX == 0.0 && dfMinY == 0.0 && dfMaxX == 0.0 && dfMaxY == 0.0 )
{
- dfMinX = adfDstGeoTransform[0];
- dfMaxX = adfDstGeoTransform[0] + adfDstGeoTransform[1] * nPixels;
- dfMaxY = adfDstGeoTransform[3];
- dfMinY = adfDstGeoTransform[3] + adfDstGeoTransform[5] * nLines;
+ dfMinX = adfDstGeoTransform[0];
+ dfMaxX = adfDstGeoTransform[0] + adfDstGeoTransform[1] * nPixels;
+ dfMaxY = adfDstGeoTransform[3];
+ dfMinY = adfDstGeoTransform[3] + adfDstGeoTransform[5] * nLines;
}
dfXRes = (dfMaxX - dfMinX) / nForcePixels;
@@ -1177,6 +1194,7 @@
nPixels = (int) ((dfMaxX - dfMinX + (dfXRes/2.0)) / dfXRes);
nLines = (int) ((dfMaxY - dfMinY + (dfYRes/2.0)) / dfYRes);
+
adfDstGeoTransform[0] = dfMinX;
adfDstGeoTransform[3] = dfMaxY;
}
@@ -1198,9 +1216,9 @@
hDstDS = GDALCreate( hDriver, pszFilename, nPixels, nLines,
nDstBandCount, eDT, papszCreateOptions );
- if( hDstDS == NULL )
+ if( hDstDS == NULL ) {
return NULL;
-
+ }
/* -------------------------------------------------------------------- */
/* Write out the projection definition. */
/* -------------------------------------------------------------------- */
@@ -1226,7 +1244,10 @@
hCT = GDALGetRasterColorTable( GDALGetRasterBand(hSrcDS,1) );
if( hCT != NULL )
GDALSetRasterColorTable( GDALGetRasterBand(hDstDS,1), hCT );
-
+
return hDstDS;
}
+#ifdef __cplusplus
+}
+#endif
Modified: branches/WIP-pyshapelib-Unicode/thuban/setup.py
===================================================================
--- branches/WIP-pyshapelib-Unicode/thuban/setup.py 2009-08-18 13:35:30 UTC (rev 2885)
+++ branches/WIP-pyshapelib-Unicode/thuban/setup.py 2009-08-18 20:04:10 UTC (rev 2886)
@@ -86,19 +86,20 @@
basedir = os.path.dirname(sys.argv[0])
# Directories where Proj4 is installed
- proj4_prefix = os.path.join(basedir, "..", "proj-4.4.9", "src")
+ proj4_prefix = os.path.join(basedir, "..", "proj-4.5.0", "src")
proj4_incdir = proj4_prefix
proj4_libdir = proj4_prefix
proj4_lib = "proj_i"
# Define include and lib directories for wxWindows and
- wx_prefix = os.path.join(basedir, "..", "wxPython-2.6.3.3")
+ wx_prefix = os.path.join(basedir, "..", "wxPython-2.8.9.2")
wx_inc = [os.path.join(wx_prefix, 'lib', 'vc_dll', 'mswuh'),
os.path.join(wx_prefix, "include")]
wx_lib = [os.path.join(wx_prefix, "lib", "vc_dll")]
# Define include and lib directories for GDAL
- gdal_prefix = os.path.join(basedir, "..", "gdal-1.4.0")
+ #gdal_prefix = os.path.join(basedir, "..", "gdal-1.4.1")
+ gdal_prefix = os.path.join(basedir, "..", "gdal-1.5.4")
gdal_inc = [os.path.join(gdal_prefix, 'alg'),
os.path.join(gdal_prefix, 'ogr'),
os.path.join(gdal_prefix, 'port'),
@@ -136,29 +137,28 @@
# the values of wx_defs and wx_libs. copied from the wxPython
# setup.py
wx_cs_params[CS_DEFS] = \
- [ ('WIN32', None), # Some of these are no longer
- ('__WIN32__', None), # necessary. Anybody know which?
- ('_WINDOWS', None),
- ('__WINDOWS__', None),
- ('WINVER', '0x0400'),
- ('__WIN95__', None),
- ('STRICT', None),
+ [ #('WIN32', None), # Some of these are no longer
+ #('__WIN32__', None), # necessary. Anybody know which?
+ #('_WINDOWS', None),
+ #('__WINDOWS__', None),
+ #('WINVER', '0x0400'),
+ #('__WIN95__', None),
+ #('STRICT', None),
- ('__WXMSW__', None),
- ('WXUSINGDLL', '1'),
+ #('__WXMSW__', None),
+ #('WXUSINGDLL', '1'),
- ('SWIG_GLOBAL', None),
- ('HAVE_CONFIG_H', None),
- ('WXP_USE_THREAD', '1'),
+ #('SWIG_GLOBAL', None),
+ #('HAVE_CONFIG_H', None),
+ #('WXP_USE_THREAD', '1'),
]
-
+
wx_cs_params[CS_INCDIRS] = wx_inc
wx_cs_params[CS_LIBDIRS] = wx_lib
- wx_cs_params[CS_LIBS] = ["wxmsw26uh"] \
- + ['kernel32', 'user32', 'gdi32', 'comdlg32',
- 'winspool', 'winmm', 'shell32', 'oldnames',
- 'comctl32', 'odbc32', 'ole32', 'oleaut32',
- 'uuid', 'rpcrt4', 'advapi32', 'wsock32']
+ wx_cs_params[CS_LIBS] = ['wxmsw28uh_core' ]#'wxmsw28uh_core' , 'wxmsw28uh_stc', 'wxbase28uh' ,\
+ #'wxmsw28uh_html' , 'wxmsw28uh_richtext' , 'wxmsw28uh_adv' , \
+ #'wxmsw28uh_xrc' , 'wxmsw28uh_aui', 'wxmsw28uh_gl' , 'wxmsw28uh_gizmos' , \
+ #'wxbase28uh_net' , 'wxbase28uh_xml']
gdal_config_script = ""
gdal_cs_params = [[] for i in range(CS_NUM_PARAMS)]
Modified: branches/WIP-pyshapelib-Unicode/thuban/test/test_baserenderer.py
===================================================================
--- branches/WIP-pyshapelib-Unicode/thuban/test/test_baserenderer.py 2009-08-18 13:35:30 UTC (rev 2885)
+++ branches/WIP-pyshapelib-Unicode/thuban/test/test_baserenderer.py 2009-08-18 20:04:10 UTC (rev 2886)
@@ -344,7 +344,34 @@
img_data = renderer.projected_raster_layer(layer, "", "",
(-24, 65, -23, 66), [0, 0], (20, 20), opts)
self.assertEquals(img_data, data)
-
+
+ def test_projected_raster_broken(self):
+ """
+ Until using gdal 1.5, we could load more than one file with gdal
+ without problems. After 1.5 we had problems having this message in
+ the stdout when loading a second file with GDAL:
+
+ ERROR 4: `../Data/iceland/island.tif' not recognised as a
+ supported file format.
+
+ This was only after a call to ProjectRasterFile.
+
+ If this test passes without IOException, the problem will be solved.
+ """
+ tiffile= os.path.join("..", "Data", "iceland",
+ "island.tif")
+ projection = "+proj=latlong +to_meter=0.017453 +ellps=clrk66"
+ new_projection = "+proj=utm +zone=27 +ellps=clrk66"
+
+ from osgeo.gdal import Open
+ t1 = RasterLayer("rast",tiffile)
+ v = ProjectRasterFile(tiffile,projection, new_projection, \
+ (322003.1320390497, 6964094.1718668584,\
+ 876022.1891829354, 7460469.6276894147), \
+ [0, 0], (10,5), 1)
+ v =None
+ t2 = RasterLayer("rast3", tiffile)
+
def test_projected_raster_decimalcommalocale(self):
if not Thuban.Model.resource.has_gdal_support():
raise support.SkipTest(Thuban.Model.resource.gdal_support_status)
More information about the Thuban-commits
mailing list