# PyUI
# Copyright (C) 2001-2002 Sean C. Riley
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of version 2.1 of the GNU Lesser General Public
# License as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
""" This module is for interacting with data sources using Twisted Enterprise -
a object-relational mapping interface for python that uses the Twisted
network/server framework.
Info on Twisted can be found at http://www.twistedmatrix.com. This code worked
with Twisted verion 0.99r4.
An example for using these Boat classes (Row Boat - get it?) can
be found in pyui/tests/boatTest.py
"""
import time
import pyui
import traceback
from twisted.enterprise import adbapi,row,sqlreflector,util
from twisted.enterprise.dbcred import *
class BoatChildrenFrame(pyui.widgets.Frame):
"""This is a frame that shows the children of a particular row for a relationship.
It uses a BoatSheet internally
"""
def __init__(self, x, y, reflector, parentRow, relationship, dataIn=None):
self.reflector = reflector
self.relationship = relationship
self.parentRow = parentRow
pyui.widgets.Frame.__init__(self, x, y, 400,180, "Rows from %s for %s" % (relationship.childRowClass.rowTableName, parentRow.getKeyTuple()) )
self.setLayout(pyui.layouts.GridLayoutManager(1,1))
where = self.reflector.buildWhereClause(relationship, parentRow)
self.sheet = BoatSheet(self.reflector, relationship.childRowClass, dataIn, where)
self.replacePanel(self.sheet)
class BoatFormFrame(pyui.widgets.Frame):
"""This is a frame that displays all of the data for a row in a form
and allows the user to edit the data. Uses a BoatFormFrame internally.
"""
def __init__(self, reflector,rowClass, rowNum, sheet):
self.reflector = reflector
self.rowClass = rowClass
self.rowNum = rowNum
self.sheet = sheet
l = len(rowClass.rowColumns)
pyui.widgets.Frame.__init__(self, 50,50,400,22*(l+2), "Row %s" % rowClass)
self.setLayout(pyui.layouts.TableLayoutManager(2,l+1))
self.form = BoatFormPanel(rowClass)
self.updateButton = pyui.widgets.Button("Save", self.onButton)
self.closeButton = pyui.widgets.Button("Cancel", self.onCloseButton)
self.addChild(self.form, (0,0,2,l))
self.addChild(self.updateButton, (1,l,1,1) )
self.addChild(self.closeButton, (0,l,1,1) )
self.pack()
def populate(self, row):
self.form.populate(row)
def onButton(self, but):
self.form.process()
self.reflector.updateRow(self.form.object).addCallback(self.onUpdate)
def onUpdate(self, but):
print "form row updated."
self.sheet.updateRowAt(self.form.object, self.rowNum)
self.destroy()
def onCloseButton(self, but):
self.destroy()
class BoatFormPanel(pyui.widgets.FormPanel):
"""The actual form panel used by the BoatFormFrame.
"""
def __init__(self, rowClass):
self.rowClass = rowClass
fieldList = []
for name, ctype in rowClass.rowColumns:
if util.dbTypeMap[ctype] == util.NOQUOTE:
fieldType = "int"
else:
fieldType = "string"
if (name,ctype) in rowClass.rowKeyColumns:
fieldType = "label"
fieldList.append( (fieldType, name, "%s:"%name, 1, 24) )
pyui.widgets.FormPanel.__init__(self, fieldList )
class BoatNewFormFrame(pyui.widgets.Frame):
"""Frame for Form for entering a new row
"""
def __init__(self, reflector,rowClass, sheet):
self.reflector = reflector
self.rowClass = rowClass
self.sheet = sheet
l = len(rowClass.rowColumns)
pyui.widgets.Frame.__init__(self, 50,50,400,22*(l+2), "Row %s" % rowClass)
self.setLayout(pyui.layouts.TableLayoutManager(2,l+1))
self.form = BoatNewFormPanel(rowClass)
self.updateButton = pyui.widgets.Button("Create", self.onButton)
self.closeButton = pyui.widgets.Button("Cancel", self.onCloseButton)
self.addChild(self.form, (0,0,2,l))
self.addChild(self.updateButton, (1,l,1,1) )
self.addChild(self.closeButton, (0,l,1,1) )
self.pack()
def populate(self, row):
self.form.populate(row)
def onButton(self, but):
self.form.process()
self.reflector.insertRow(self.form.object).addCallbacks(self.onInsert, self.onError)
def onInsert(self, but):
print "form row inserted.", but
self.sheet.insertRowAt(self.form.object)
self.destroy()
def onError(self, err):
print "Error on insert:", err
def onCloseButton(self, but):
self.destroy()
class BoatNewFormPanel(pyui.widgets.FormPanel):
"""The actual form panel used by the BoatNewFormFrame.
"""
def __init__(self, rowClass):
self.rowClass = rowClass
fieldList = []
for name, ctype in rowClass.rowColumns:
if util.dbTypeMap[ctype] == util.NOQUOTE:
fieldType = "int"
else:
fieldType = "string"
fieldList.append( (fieldType, name, "%s:"%name, 1, 24) )
pyui.widgets.FormPanel.__init__(self, fieldList )
newRow = self.rowClass()
self.populate(newRow)
def process_string(self, formWidget, fieldName):
"""if this is a key column, use the special assignKeyAttr method.
"""
found=0
for name, ctype in self.rowClass.rowKeyColumns:
if name == fieldName:
if util.dbTypeMap[ctype] == util.NOQUOTE:
self.object.assignKeyAttr(fieldName, formWidget.text)
else:
self.object.assignKeyAttr(fieldName, formWidget.text)
found=1
if not found:
pyui.widgets.FormPanel.process_string(self, formWidget, fieldName)
class BoatSheet(pyui.sheet.Sheet):
"""A panel that displays a set of rows for a particular rowClass/table.
It has resizable columns and rows, and allows in-place editing of the
data which will be updated immediately back into the reflector.
Has a menu that allows other operationson the rows.
"""
def __init__(self, reflector, rowClass, dataIn=None, whereIn=None):
self.reflector = reflector
self.rowClass = rowClass
self.dataIn = dataIn
pyui.sheet.Sheet.__init__(self, self.onChange)
# setup column titles
i=0
for name, ctype in rowClass.rowColumns:
self.setColumnTitle(i+1, name)
i=i+1
if (name, ctype) in rowClass.rowKeyColumns:
self.setColumnReadOnly(i)
# setup menu
actionMenu = pyui.widgets.MenuPopup()
item = actionMenu.addItem("Open...", self.onEdit)
item = actionMenu.addItem("New...", self.onNew)
item = actionMenu.addItem("Delete...", self.onDelete)
info = self.reflector.getTableInfo(rowClass)
for relationship in info.relationships:
item = actionMenu.addItem("Show Children for %s..." % relationship.childRowClass.rowTableName, self.onChildren)
item.relationship = relationship
item.sheet = self
self.addPopup(actionMenu)
# load objects
self.reflector.loadObjectsFrom(self.rowClass.rowTableName, data=dataIn, whereClause=whereIn, forceChildren=0).addCallback(self.gotRows)
def gotRows(self, rows):
y=0
for row in rows:
x=0
for colName, ctype in self.rowClass.rowColumns:
self.setCellValue(x+1, y+1, getattr(row, colName))
x=x+1
y=y+1
self.rows = rows
def onChange(self, x, y, value):
print "changed:", x, y, value
if x < 0 or y < 0 or x >= len(self.rowClass.rowColumns) or y >= len(self.rows):
print "Changed cell not on an existing row."
return
row = self.rows[y-1]
(name, ctype) = self.rowClass.rowColumns[x-1]
try:
if ctype == "int" or ctype == "int4" or ctype == "int2":
setattr(row, name, int(value))
elif ctype == "float8" or ctype == "float":
setattr(row, name, float(value))
else:
setattr(row, name, value)
except:
print "Invalid value for cell", name, ctype, value
traceback.print_exc()
return
self.reflector.updateRow(row).addCallback(self.onUpdate)
return 1
def onEdit(self, item):
pos = (self.popup.posX - self.rect[0], self.popup.posY - self.rect[1])
(x,y) = self.findCellAt(pos[0], pos[1])
if y-1 >= len(self.rows):
return
form = BoatFormFrame(self.reflector, self.rowClass, y-1, self)
form.populate(self.rows[y-1])
def onNew(self, item):
##TODO: create a new row object
pos = (self.popup.posX - self.rect[0], self.popup.posY - self.rect[1])
(x,y) = self.findCellAt(pos[0], pos[1])
form = BoatNewFormFrame(self.reflector, self.rowClass, self)
def onChildren(self, item):
pos = (self.popup.posX - self.rect[0], self.popup.posY - self.rect[1])
(x,y) = self.findCellAt(pos[0], pos[1])
row = self.rows[y-1]
f = BoatChildrenFrame(self.rect[0]+50, self.rect[1]+50, self.reflector, row, item.relationship, self.dataIn)
def onUpdate(self, stuff):
print "updated row."
def updateRowAt(self, row, y):
x=0
for colName, ctype in self.rowClass.rowColumns:
self.setCellValue(x+1, y+1, getattr(row, colName))
x=x+1
def insertRowAt(self, row):
x=0
y = self.highestRow
for colName, ctype in self.rowClass.rowColumns:
self.setCellValue(x+1, y+1, getattr(row, colName))
x=x+1
def onDelete(self, item):
pos = (self.popup.posX - self.rect[0], self.popup.posY - self.rect[1])
(x,y) = self.findCellAt(pos[0], pos[1])
if y-1 >= len(self.rows):
return
row = self.rows[y-1]
self.reflector.deleteRow(row).addCallback(self.onDeleted, row)
def onDeleted(self, null, row):
print "Deleted row."
self.rows.remove(row)
self.clear()
self.gotRows(self.rows)
class BoatReflectorFrame(pyui.widgets.Frame):
"""A Frame that contains a list of the registered RowClasses/Tables and
a tabbed dialog that can have a tab for each table/rowClass.
"""
def __init__(self, reflector, x=0, y=0, width=620, height=400, dataIn=None):
self.reflector = reflector
self.dataIn = dataIn
pyui.widgets.Frame.__init__(self, x, y, width, height, "Reflector Access")
self.splitter = pyui.widgets.SplitterPanel(ratio=20)
self.replacePanel(self.splitter)
self.tableList = pyui.widgets.ListBox(self.onSelectTable)
self.tabs = pyui.widgets.TabbedPanel()
self.tablePanels = {}
self.splitter.getFirstPanel().setLayout(pyui.layouts.TableLayoutManager(1,20))
self.splitter.getSecondPanel().setLayout(pyui.layouts.GridLayoutManager(1,1))
self.splitter.getFirstPanel().addChild(pyui.widgets.Button("Reflected Tables", None), (0,0,1,1) )
self.splitter.getFirstPanel().addChild(self.tableList, (0,1,1,19))
self.splitter.getSecondPanel().addChild(self.tabs)
self.pack()
self.populate()
def populate(self):
self.tableList.clear()
for info in self.reflector.schema.values():
self.tableList.addItem(info.rowTableName, info)
self.tableList.sortByName()
def onSelectTable(self, item):
if item:
self.switchToTable(item.data.rowTableName)
self.setDirty(1)
def switchToTable(self, tableName):
panel = self.tablePanels.get(tableName)
if panel:
self.tabs.activatePanel(panel)
else:
panel = BoatSheet(self.reflector, self.reflector.schema[tableName].rowClass, dataIn=self.dataIn)
self.tabs.addPanel(tableName, panel)
self.tablePanels[tableName]=panel
self.tabs.activatePanel(panel)
|