FoxPython
Ноя. 16, 2007 18:31:38
О! Я заставил таки данные в гриде изменяться под действием пользователя!
Данные из cu.fetchall() пришлось превратить из кортежа в список списков или кортеж списков (запутался я с ними).
Вот так:
# -*- coding: utf-8 -*-
import wx, wx.grid, sqlite3 as db
class MainFrame(wx.Frame):
def __init__(self, parent, title, pos, size):
wx.Frame.__init__(self, parent, -1, title, pos=pos, size=size)
mypanel=self.mypanel=wx.Panel(self, -1)
c=db.connect(database='bigbase.sqlite3')
cu=c.cursor()
cu.execute('SELECT fam, im, ot, age FROM one')
mygrid=wx.grid.Grid(mypanel, size=(400,400))
#—-вот таким страшным способом————
cuu=
for i in cu.fetchall():
tmp=
for ii in i:
tmp.append(ii)
cuu.append(tmp)
#—-делается список списков, который уже можно изменять
#—-обращаясь к нему как к массиву по индексам: cuu='новое значение'
#—-ОПУПЕТЬ! Почему создатели питона не хотят сделать обычный tuple изменяемым?????
#—-чтоб не приходилось так извращаться!
table=MyTable(cuu)
mygrid.SetTable(table, True)
class MyTable(wx.grid.PyGridTableBase):
def __init__(self, dannie):
wx.grid.PyGridTableBase.__init__(self)
self.dannie=dannie
def GetNumberRows(self):
return len(self.dannie)
pass
def GetNumberCols(self):
return len(self.dannie)
pass
def IsEmptyCell(self, row, col):
return self.dannie is not None
pass
def GetValue(self, row, col):
value = self.dannie
if value is not None:
return value
else:
return ''
pass
def SetValue(self, row, col, value):
self.dannie=value
pass
====================================
Кстати, ПроДива, в твоём примере, вновь занесённые данные в сетку, каждый раз ЗАНОВО добавляются к уже существующим в базе. А мне надо, чтобы в базу возвращались изменённые пользователем данные. Т.е. сменил пользователь в сетке кому-нибудь фамилию с “Пупкин” на “Губкин” и в базе этот конкретный “Пупкин” должен измениться на “Губкин”.
Вот как это сделать? Как узнать какое значение в сетке соответствует значению в базе sqlite? Чтобы можно было изменить его. Наверное, у записей в базе есть (да обязаны быть!) уникальные номера и следить нужно за ними. Как до них добраться? Без таких номеров, невозможно будет проконтролировать какую запись изменил пользователь. Я ведь могу в сетку выдать и отсортированный результат запроса, так что не факт, что данные в сетке расположатся в том же порядке, что и в базе.
Ламерский вопрос: какая sql-команда позволяет изменить какую-либо конкретную запись?
———————–
В твоём примере некорректно printится номер (столбца и строки) ячейки, если перед тем как щёлкнуть какую-нибудь ячейку воспользоваться бегунком прокрутки. Только повторный щёлк заставит появиться правильный номер.
———————–
Почему в гриде не работают клавиши end и home?
БОЛЬШОЕ СПАСИБО заранее.
pyuser
Ноя. 17, 2007 03:29:00
строка:
cuu=
выполнит то же преобразование что и Ваш код (да и выглядит не очень страшно)
#—-вот таким страшным способом————
cuu=
for i in cu.fetchall():
tmp=
for ii in i:
tmp.append(ii)
cuu.append(tmp)
#—-делается список списков, который уже можно изменять
FoxPython
… Как узнать какое значение в сетке соответствует значению в базе sqlite? Чтобы можно было изменить его. Наверное, у записей в базе есть (да обязаны быть!) уникальные номера и следить нужно за ними. Как до них добраться? Без таких номеров, невозможно будет проконтролировать какую запись изменил пользователь. Я ведь могу в сетку выдать и отсортированный результат запроса, так что не факт, что данные в сетке расположатся в том же порядке, что и в базе.
Ламерский вопрос: какая sql-команда позволяет изменить какую-либо конкретную запись?
в идеале у каждой таблицы должно быть ключевое поле (primary, unique …)
тогда обновление очень просто:
update table_name set field_name=new_value, … where (key_field=key_value)
… - все обновляемые поля
если ключевые поля не предусмотрены то немного сложнее:
update table_name set field_name=new_value, … where ((field_name=old_value) and …))
первое … - все обновляемые поля,
а второе (после ключевого слова where) … - все поля таблицы
FoxPython
Ноя. 17, 2007 19:47:59
Как же я привык к фокспро (рыдающий смайл)…
Но всё равно спасибо, будем продолжать пытаться…
—————-
Давит грузом неопределённости питон 3000. Сейчас изучу питон 2.5.1, а он больше не поддерживается и не развивается! Потом все программы перелопачивать на питон 3000?
FoxPython
Ноя. 17, 2007 19:50:05
А, вообще, как ещё, кроме моего способа, можно сунуть данные из базы в таблицу PyGridTableBase? Неужели только моим способом? Что-то не верится…
PooH
Ноя. 19, 2007 11:49:35
Ну вот вам еще один вариант
# -*- coding: utf-8 -*-
import wx
import wx.grid
from sqlite3 import dbapi2 as db
class BaseTable(wx.grid.PyGridTableBase):
def __init__(self, connection):
super(BaseTable, self).__init__()
self.connection = connection
self.refresh()
def field_list(self):
ls =
ls.extend()
return ls
def refresh(self):
cur = self.connection.cursor()
cur.execute(“select %s from %s;” % (','.join(self.field_list()), self.table))
self.data =
cur.close()
def save_value(self, row, col):
if self.data:
cur = self.connection.cursor()
cur.execute(“update %s set %s = ‘%s’ where %s = %s;” % (self.table,
self.columns.name, self.data, self.primary_key, self.data))
self.connection.commit()
cur.close()
def GetNumberRows(self):
return len(self.data)
def GetNumberCols(self):
return len(self.columns)
def GetColLabelValue(self, col):
return self.columns.title
def GetRowLabelValue(self, row):
return str(row)
def IsEmptyCell(self, row, col):
return self.data is not None
def GetValue(self, row, col):
return self.data
def SetValue(self, row, col, value):
if self.data != value:
self.data = value
self.save_value(row, col)
def InsertRows(self, pos=1, numRows=1):
cur = self.connection.cursor()
for n in range(pos, pos+numRows):
cur.execute(“insert into %s(%s) values(%s);” % (self.table, ‘,’.join(),
‘,’.join()))
ls =
ls.extend()
self.data.insert(n, ls)
self.connection.commit()
cur.close()
msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_ROWS_INSERTED, pos, numRows)
self.GetView().ProcessTableMessage(msg)
return True
def DeleteRows(self, pos=0, numRows=1):
cur = self.connection.cursor()
for n in range(pos, pos+numRows):
cur.execute(“delete from %s where %s = %s;” % (self.table, self.primary_key, self.data))
self.connection.commit()
del self.data
cur.close()
msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_ROWS_DELETED, pos, numRows)
self.GetView().ProcessTableMessage(msg)
return True
class Column(object):
def __init__(self, name, title, default=None):
self.name = name
self.title = title
self.default = default
class TestTable(BaseTable):
table = ‘test’
primary_key = ‘id’
columns = (
Column('numb', u'Табельный\nномер'),
Column('fio', u'ФИО', u'Иванов Иван Иваныч'),
Column('dolzh', u'Должность',u'Стрелочник'),
)
class SimpleGrid(wx.grid.Grid):
def __init__(self, parent, model):
super(SimpleGrid, self).__init__(parent, -1)
self.SetTable(model)
for col in range(0, self.GetNumberCols()):
self.AutoSizeColumn(col, setAsMin=True)
self.Bind(wx.EVT_KEY_DOWN, self.on_key_down)
def on_key_down(self, event):
if event.ControlDown() and event.KeyCode == wx.WXK_DELETE:
self.on_delete(event)
elif event.KeyCode == wx.WXK_INSERT:
self.on_insert(event)
event.Skip()
def on_insert(self, event):
print “on_insert”
self.InsertRows(self.GetGridCursorRow())
def on_delete(self, event):
print “on_delete”
self.DeleteRows(self.GetGridCursorRow())
def create_db(name):
con = db.connect(name)
cur = con.cursor()
try:
cur.execute(“”“
create table test (
id integer primary key autoincrement,
numb integer,
fio varchar(200),
dolzh varchar(200));
”“”)
for x in xrange(0, 500):
cur.execute(u“insert into test(numb, fio, dolzh) values(%d, ‘Сотрудник %d’, ‘Должность %d’)” % (x,x,x))
except db.DatabaseError, x:
print “Не могу создать базу!”, x
print “База создана!”
con.commit()
return con
class TestFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1, “A Grid”, size=(500, 400))
grid = SimpleGrid(self, TestTable(create_db('test.db')))
if __name__ == ‘__main__’:
app = wx.PySimpleApp()
frame = TestFrame(None)
frame.Show(True)
app.MainLoop()
Только что набросал на коленке, но базовые моменты думаю будут понятны
FoxPython
Ноя. 20, 2007 17:23:46
Спасибо за пример. Счас буду по полочкам раскладывать.
———————————————————————-
Если, вдруг, кому охота ответить:
class Myclass():
a=1
b=2
——————————————–
class Myclass():
def __init__(self, tralivali):
self.a=1
self.b=2
Это что, получается, что метод __init__ не являются целиком и полностью принадлежностью класса Myclass? Ведь метод __init__ описан в самом этом классе. Логично же, что он относится ИМЕННО к нему. Зачем же иниту передавать self?
———————————————————————-
П.С. У Вас у всех в примерах после того как программа выдаст “не могу создать базу”, вдобавок напишет и “база создана”. Так низя! Дезинформация, понимашь!
Viper
Ноя. 20, 2007 18:20:08
FoxPython
Это что, получается, что метод __init__ не являются целиком и полностью принадлежностью класса Myclass? Ведь метод __init__ описан в самом этом классе. Логично же, что он относится ИМЕННО к нему. Зачем же иниту передавать self?
__init__ является целиком и полностью принадлежностью класса, а self указывает на обьект этого класса. :)
PooH
Ноя. 22, 2007 07:25:08
FoxPython
П.С. У Вас у всех в примерах после того как программа выдаст “не могу создать базу”, вдобавок напишет и “база создана”. Так низя! Дезинформация, понимашь!
Ну а что ты хотел для простого примера?! Я просто взял код создания базы из предыдущего примера. В серьезных приложениях база обычно создается при установке.
По поводу того примера что постил я:
1. Лучше для работы с базой воспользоваться каким-нибудь готовым ОРМ, например SQLObject или SQLAlchemy. Тогда код BaseTable упрощается до безобразия
2. У меня изменения в базу постятся при изменении ячейки, лучше, наверно, постить на переходе на другую строку, как принято в фоксе и прочих делфи
3. Для большого набора данных лучше использовать буфер и прокручивать набор через него
4. Интересно так же расширить интерфейс BaseTable методами для поиска, фильтрации и т.д. и написать обобщенный грид для всего этого дела
5. TestTable, наследуемый от BaseTable, содержит только описание конкретного набора данных, интересно добавить в это описание валидаторы и рендеры.
6. В обработчике on_key_down, лучше не вызывать напрямую on_insert и on_delete, а создать для этого свои события и инициировать их
Если найду время накатаю более идеологически грамотный пример ;)
pythonwin
Ноя. 24, 2007 12:10:59
PooH, спасибо за последовательность действий и за код :)
PooH
1. Лучше для работы с базой воспользоваться каким-нибудь готовым ОРМ, например SQLObject или SQLAlchemy. Тогда код BaseTable упрощается до безобразия
согласен это очень хороший вариант, но только если не критична эффективность кода и данных не много (в основном ячеек). Если же таблицы большие или в записи есть большое поле, то лучше делать запросы на более низком уровне (например на sqlbuilder или да просто запросы на sql)
PooH
3. Для большого набора данных лучше использовать буфер и прокручивать набор через него
для экономии ОЗУ можно использовать временные файлы :)
PooH
Ноя. 24, 2007 12:41:26
pythonwin
Если же таблицы большие или в записи есть большое поле, то лучше делать запросы на более низком уровне (например на sqlbuilder или да просто запросы на sql)
А здесь поможет lazy loading в ОРМ для больших полей.
pythonwin
PooH
3. Для большого набора данных лучше использовать буфер и прокручивать набор через него
для экономии ОЗУ можно использовать временные файлы :)
Здесь речь не столько об экономии ОЗУ, сколько об экономии сетевого трафика :) Обычно никто не будет прокручивать в гриде набор из 1000 и более записей, а воспользуется поиском или фильтром. Так зачем это тянуть на клиента?!