Debian sid
wx2.8 V. 2.8.12.1-3
···
===================
Hi list,
I'm trying to build a working UltimateListCtrl using the demo as a base,
that seeks its data from a database and cache it.
When I launch it I've got a white window and only the columns headers text
are visible - and it throw a bunch of internal errors.
I traced both the example and my test (w/ eric) and found that it fails while
calling the "sizer.Layout()" function (see @ the end of code).
What I understand is the widget can't calculate its height, but why?
I think I'm close to the solution but I miss(understand?) something in the
Virtual chain that stuck me completely.
Here are the command line errors and code (errors are "complete" as I just
launched and immediately closed the window; but if I hover the widget, it
continues to throw such error lines):
========================================= ERRORS
$ python UltimateVirtualDemo_DB_2011-11-22_01_clean.py
Traceback (most recent call last):
File "/usr/lib/python2.7/dist-packages/wx-2.8-gtk2-unicode/wx/lib/agw/ultimatelistctrl.py", line 12138, in OnSize
self._mainWin.RecalculatePositions()
File "/usr/lib/python2.7/dist-packages/wx-2.8-gtk2-unicode/wx/lib/agw/ultimatelistctrl.py", line 9533, in RecalculatePositions
entireHeight = count*lineHeight + LINE_SPACING
TypeError: can only concatenate tuple (not "int") to tuple
Traceback (most recent call last):
File "/usr/lib/python2.7/dist-packages/wx-2.8-gtk2-unicode/wx/lib/agw/ultimatelistctrl.py", line 12138, in OnSize
self._mainWin.RecalculatePositions()
File "/usr/lib/python2.7/dist-packages/wx-2.8-gtk2-unicode/wx/lib/agw/ultimatelistctrl.py", line 9533, in RecalculatePositions
entireHeight = count*lineHeight + LINE_SPACING
TypeError: can only concatenate tuple (not "int") to tuple
Traceback (most recent call last):
File "/usr/lib/python2.7/dist-packages/wx-2.8-gtk2-unicode/wx/lib/agw/ultimatelistctrl.py", line 12138, in OnSize
self._mainWin.RecalculatePositions()
File "/usr/lib/python2.7/dist-packages/wx-2.8-gtk2-unicode/wx/lib/agw/ultimatelistctrl.py", line 9533, in RecalculatePositions
entireHeight = count*lineHeight + LINE_SPACING
TypeError: can only concatenate tuple (not "int") to tuple
Traceback (most recent call last):
File "/usr/lib/python2.7/dist-packages/wx-2.8-gtk2-unicode/wx/lib/agw/ultimatelistctrl.py", line 12138, in OnSize
self._mainWin.RecalculatePositions()
File "/usr/lib/python2.7/dist-packages/wx-2.8-gtk2-unicode/wx/lib/agw/ultimatelistctrl.py", line 9533, in RecalculatePositions
entireHeight = count*lineHeight + LINE_SPACING
TypeError: can only concatenate tuple (not "int") to tuple
Traceback (most recent call last):
File "/usr/lib/python2.7/dist-packages/wx-2.8-gtk2-unicode/wx/lib/agw/ultimatelistctrl.py", line 12138, in OnSize
self._mainWin.RecalculatePositions()
File "/usr/lib/python2.7/dist-packages/wx-2.8-gtk2-unicode/wx/lib/agw/ultimatelistctrl.py", line 9533, in RecalculatePositions
entireHeight = count*lineHeight + LINE_SPACING
TypeError: can only concatenate tuple (not "int") to tuple
Traceback (most recent call last):
File "/usr/lib/python2.7/dist-packages/wx-2.8-gtk2-unicode/wx/lib/agw/ultimatelistctrl.py", line 7002, in OnPaint
self.RecalculatePositions(False)
File "/usr/lib/python2.7/dist-packages/wx-2.8-gtk2-unicode/wx/lib/agw/ultimatelistctrl.py", line 9533, in RecalculatePositions
entireHeight = count*lineHeight + LINE_SPACING
TypeError: can only concatenate tuple (not "int") to tuple
Traceback (most recent call last):
File "/usr/lib/python2.7/dist-packages/wx-2.8-gtk2-unicode/wx/lib/agw/ultimatelistctrl.py", line 12177, in OnInternalIdle
self._mainWin.RecalculatePositions()
File "/usr/lib/python2.7/dist-packages/wx-2.8-gtk2-unicode/wx/lib/agw/ultimatelistctrl.py", line 9533, in RecalculatePositions
entireHeight = count*lineHeight + LINE_SPACING
TypeError: can only concatenate tuple (not "int") to tuple
Traceback (most recent call last):
File "/usr/lib/python2.7/dist-packages/wx-2.8-gtk2-unicode/wx/lib/agw/ultimatelistctrl.py", line 12177, in OnInternalIdle
self._mainWin.RecalculatePositions()
File "/usr/lib/python2.7/dist-packages/wx-2.8-gtk2-unicode/wx/lib/agw/ultimatelistctrl.py", line 9533, in RecalculatePositions
entireHeight = count*lineHeight + LINE_SPACING
TypeError: can only concatenate tuple (not "int") to tuple
========================================= /ERRORS
========================================= CODE
# -*- coding: utf-8 -*-
import wx
import images
import random
import os, sys
import psycopg2
try:
dirName = os.path.dirname(os.path.abspath(__file__))
except:
dirName = os.path.dirname(os.path.abspath(sys.argv[0]))
sys.path.append(os.path.split(dirName)[0])
from wx.lib.agw import ultimatelistctrl as ULC
#############################################################
# Size of a DB chunk (Nb of rows read in one SELECT)
DB_CHUNK_SIZE = 100L
# What range of rows is cached
DB_OFFSET = 1L
DB_LIMIT = 0L
DB_LIMIT = DB_LIMIT + DB_CHUNK_SIZE
#############################################################
class TestUltimateListCtrl(ULC.UltimateListCtrl):
def __init__(self, parent, DBcur=None, DBtab=None, DBcol=None, DBcolName=None):
ULC.UltimateListCtrl.__init__(self, parent, -1, agwStyle=wx.LC_REPORT
>wx.LC_VIRTUAL|wx.LC_HRULES|wx.LC_VRULES
>ULC.ULC_SHOW_TOOLTIPS)
if DBcur == None or DBtab == None or DBcol == None or DBcolName == None:
raise "Fn TestUltimateListCtrl - Bad call: DBcur, DBtab, DBcol |& DBcolName isn't set"
# Make parms ours
self.DBcur = DBcur
self.DBtab = DBtab
self.DBcol = DBcol
self.DBcolName = DBcolName
# Create data cache list (dict)
self.dataCache = None
# Create cache list (empty)
#self.listCache = ()
# Export a string from columns list and extract each list column name
self.DBcolListStr = ''
#for i in len(self.DBcol):
for i in range(len(DBcol)):
self.DBcolListStr += self.DBcol[i] + ","
self.InsertColumn(i, self.DBcolName[i])
# Strip last ','
self.DBcolListStr = self.DBcolListStr.rstrip(",")
self.SetColumnWidth(0, 100)
self.SetColumnWidth(1, 100)
self.SetColumnWidth(2, 100)
self.SetColumnToolTip(0,"First Column Tooltip!")
self.SetColumnToolTip(1,"Second Column Tooltip!")
self.SetColumnToolTip(2,"Third Column Tooltip!")
# Set last column to autosize
self.SetColumnWidth((len(DBcol)-1), ULC.ULC_AUTOSIZE_FILL)
# Set color of displayed lines
self.linColOdd = ULC.UltimateListItemAttr()
self.linColOdd.SetBackgroundColour(wx.NamedColour("light cyan"))
self.linColEve = ULC.UltimateListItemAttr()
self.linColEve.SetBackgroundColour(wx.NamedColour("light yellow"))
# Bind events to their methods
self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated)
self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnItemDeselected)
#
self.Bind(wx.EVT_LIST_CACHE_HINT, self.OnCacheHint)
# Now init global VARs & Virtual List
self.initData()
# Call: When click-L a line
def OnItemSelected(self, event):
self.currentItem = event.m_itemIndex
#print("OnItemSelected: %s, %s, %s, %s\n" % (self.currentItem,
# self.GetItemText(self.currentItem),
# self.getColumnText(self.currentItem, 1),
# self.getColumnText(self.currentItem, 2)))
# Call: When double-clicking on a line
def OnItemActivated(self, event):
self.currentItem = event.m_itemIndex
#print( "OnItemActivated: %s\n" % (self.GetItemText(self.currentItem)) )
# Position selected at the center (V) of the list
self.ScrollToItem(self.currentItem)
# Call: Seems to be only used by 'OnItemSelected(self, event)' log print
#def getColumnText(self, index, col):
# item = self.GetItem(index, col)
# return item.GetText()
def OnItemDeselected(self, event):
#self.log.write("OnItemDeselected: %s\n" % evt.m_itemIndex)
pass # MANDATORY: This Fn must exist
#---------------------------------------------------
# These methods are callbacks for implementing the
# "virtualness" of the list... Normally you would
# determine the text, attributes and/or image based
# on values from some external data source, but for
# this demo we'll just calculate them
# Call: Each time a cell (line/col) must be refreshed
def OnGetItemText(self, item, col):
#print "OnGetItemText: Item %d, column %d" % (item, col)
#return "Item %d, column %d" % (item, col)
return self.dataCache[item][col]
# Call: When staying too long on a line|col => Display a ToolTip (bof)
def OnGetItemToolTip(self, item, col):
if item == 0:
return "Tooltip: Item %d, column %d" % (item, col)
return None
# Colorise the 1st 3 cols of the 1st line (R, V & B)
#def OnGetItemTextColour(self, item, col):
# if item == 0 and col == 0:
# return wx.Colour(255,0,0)
# elif item == 0 and col == 1:
# return wx.Colour(0,255,0)
# elif item == 0 and col == 2:
# return wx.Colour(0,0,255)
# else:
# return None
#def OnGetItemTextColour(self, item, col):
# pass
# Call: When mouse hover a cell (line/col)
#def OnGetItemColumnImage(self, item, column):
#print ("OnGetItemColumnImage(self, item = %d, column = %d)" % (item, column) )
# return self.randomLists[item%5]
# Call: Seems to never be called (???)
#def OnGetItemImage(self, item):
# print ("OnGetItemImage(self, item = %d)" % item)
# return self.randomLists[item%5]
# Call: Seems d° as 'OnGetItemColumnImage(self, item, column)'
# BUT ONLY When mouse hover a LINE
# NB: Colorise lines background
def OnGetItemAttr(self, item):
if item % 2 == 1:
return self.linColEve
else:
return self.linColOdd
####################################################################
# Call: d° as 'OnGetItemColumnImage(self, item, column)'
# When mouse hover a cell (line/col)
def OnGetItemColumnCheck(self, item, column):
#print("OnGetItemColumnCheck(self, item = %d, column = %d)" % (item, column))
if item%3 == 0:
return True
return False
# Call: Seems to never be called (???)
#def OnGetItemCheck(self, item):
# print("OnGetItemCheck(self, item = %d)" % item)
# if item%3 == 1:
# return True
# return False
####################################################################
# Call: Many times for cells (line/col)
# Icon types for cells
#def OnGetItemColumnKind(self, item, column):
#print("OnGetItemColumnKind(self, item = %d, column = %d) CTR: %d" % (item, column, mabite) )
#if item%3 == 0:
# return 2
#elif item%3 == 1:
# return 1
#return 0
# ATTENTION: 'OnGetItemColumnCheck(self, item, column)' MUST be active for a correct icons render
############################################################## my tests
# NB: All interesting info @:
# /usr/share/pyshared/wx-2.8-gtk2-unicode/wx/lib/agw/ultimatelistctrl.py
# OR: http://xoomer.virgilio.it/infinity77/AGW_Docs/ultimatelistctrl_module.html#ultimatelistctrl
def initData(self):
# Set start order (we consider that 1st column is the pkey) - A REVOIR QD FONCTIONNEL => PLUTÔT 1 PARM
self.DB_ORDER = "id"
# Set sens or order
self.DB_ORDER_SENS = "ASC"
# Set ROW_COUNT
self.ROW_COUNT = -1L
self.ROW_COUNT = self.DBrowCount()
self.SetItemCount(self.ROW_COUNT)
# 1st fill of list
self.DoCacheHint(0L, 25L)
def DBrowCount(self):
qry = "SELECT count(*) FROM %s " % (DBtab)
DBcur.execute(qry)
row_count = DBcur.fetchone()
return row_count
# Call: Each time widget needs to refresh visible lines (+2: up & dwn)
def OnCacheHint(self, event):
DoCacheHint(event.GetCacheFrom(), event.GetCacheTo())
# Check if range of wanted lines is already cached, and query DB into cache if not
def DoCacheHint(self, cacheFrom, cacheTo):
global DB_OFFSET, DB_LIMIT
# Calc last row Nb returned by last query
DBlastRow = cacheFrom + DB_LIMIT
if cacheFrom >= (DB_OFFSET + 1L) and cacheTo <= DBlastRow:
pass # Data is cached, do nothing. Also take care of end of list
elif cacheFrom == 0L and DB_OFFSET == 0L: # Beginning of list
pass # Data is cached, do nothing
else:
# Data isn't cached, refill the list w/ the right query
# but 1st of all, reload row count in case another user
# added new rows(s)
self.ROW_COUNT = self.DBrowCount()
self.SetItemCount(self.ROW_COUNT)
halfList = ((cacheTo - cacheFrom) + 1L) / 2L
DB_OFFSET = cacheTo - (DB_LIMIT / 2L) + halfList
# Take care of list beginning
if DB_OFFSET < 0L:
DB_OFFSET = 0L
# Construct query
qry = "SELECT %s FROM %s ORDER BY %s %s LIMIT %s OFFSET %s " % (self.DBcolListStr, self.DBtab, self.DB_ORDER, self.DB_ORDER_SENS, DB_LIMIT, DB_OFFSET)
# Fill the cache
DBcur.execute(qry)
self.dataCache = DBcur.fetchall()
# Reposition the selected line at the middle (V) of the list
def ScrollToItem(self, index):
top_value = max([0, index - self.GetCountPerPage() / 2])
bottom_value = min([index + self.GetCountPerPage() / 2, self.GetItemCount() - 1])
self.EnsureVisible(top_value)
self.EnsureVisible(bottom_value)
############################################################################
class TestFrame(wx.Frame):
def __init__(self, parent, DBcur=None, DBtab=None, DBcol=None, DBcolName=None):
wx.Frame.__init__(self, parent, -1,
"UltimateListCtrl in wx.LC_VIRTUAL mode - WITH DB READING",
size=(700, 600))
panel = wx.Panel(self, -1)
sizer = wx.BoxSizer(wx.VERTICAL)
listCtrl = TestUltimateListCtrl(panel, DBcur, DBtab, DBcol, DBcolName)
sizer.Add(listCtrl, 1, wx.EXPAND)
panel.SetSizer(sizer)
# It is HERE (next line) that everything goes awry.
# In the example we go to the end of this class and jump to:
# __main__ -> frame.Show(True)
# however here we're stuck into Ultimate source, generating errors
# that are printed on command line :((
sizer.Layout()
self.SetIcon(images.Mondrian.GetIcon())
self.CenterOnScreen()
self.Show()
############################################################################
if __name__ == '__main__':
import sys
app = wx.PySimpleApp()
# Open a DB connection
DBcon = psycopg2.connect("host='anubis.defcon1' dbname='testulc' user='testulc' password='testulc'")
# Set transations to autocommit to avoid leaving an open transaction when leaving
# which would leave DB in an 'idle in transaction' (bad) state,
# isolation_level = REPEATABLE_READ (mean: use only written data out of any transaction)
# better than READ COMMITED because it forbids fail another transaction
# by stealing one of its element.
# readonly = True
# deferrable = True (might avoid concurrency row lock PB ?)
# autocommit = Commitotomatic:)
DBcon.set_session(psycopg2.extensions.ISOLATION_LEVEL_REPEATABLE_READ,
True, True, psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
# Surely a redundancy with above line ------------------------------------------------ CHECK LATER
DBcon.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
# Create DB cursor
DBcur = DBcon.cursor()
# Table name
DBtab = "public.tst1m"
# Also create column names lists (DB & Displayed)
DBcol = { 0: ("id"), 1: ("name"), 2: ("note"), }
DBcolName = { 0: ("Identité"), 1: ("Nom"), 2: ("Commentaire"), }
frame = TestFrame(None, DBcur, DBtab, DBcol, DBcolName)
frame.Show(True)
app.MainLoop()
# Close DB
DBcon.close()
========================================= /CODE
Test table is randomly filled w/ 1M rows:
CREATE TABLE tst1m(
id serial primary key,
name character varying(64) not null,
note character varying(64) not null
);
CREATE UNIQUE INDEX tst1m_name_ix ON tst1m(name);
CREATE INDEX tst1m_note_ix ON tst1m(note);
JY
--
The future is a myth created by insurance salesmen and high school counselors.