7

私の質問は、PySide の QTableView クラスからアクセスできる SQLAlchemy 宣言型モデルをセットアップする方法です。

オブジェクトリレーショナルチュートリアルのフロントエンドを本質的に実装しようとしています

残念ながら、私にはいくつかの混乱点があります。私がどこにいるかを説明しようと思います。

私は SQLAlchemy チュートリアルに従って、関連する 2 つのテーブルがあり、それらを問​​題なく操作/クエリできるようになりました。QTableView クラスを確立しようとすると、明らかに自分のモデルで setData() メソッドが必要になるか、デフォルト モデルを使用するには setItem() メソッドが必要になります

したがって、問題はモデルをどのように設計するかです。これは、データベースを照会/変更するための 2 つのメソッドのいずれかを定義することを意味すると思います。これを行う正しい方法がわかりません。

モデルは、すべての住所が表示されるまで数行で繰り返される User の姓と名のようなものであると想定されており、その後、次の User に移動します。プロンプトでこれを出力するためにネストされたforループでこれを行うことができますが、そもそもデータベースを持つという点を無効にするように見えるので、大きなリストを作成することは道だとは思いません...

また、データベースが大きくなるとどうなるかわかりません.テーブル全体がインスタンス化されてメモリに保持されますか、それともユーザーがスクロールしたときに表示されるようにQtが行と列をロードしますか?

申し訳ありませんが、明確にするよう努めています。私が追加できる追加のものがあれば、私に知らせてください。または、私が完全に間違った方向に進んでいる場合....

from PySide import QtCore, QtGui
from sqlalchemy import Column, Integer, String, Text, Sequence, ForeignKey, Date, Boolean, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship, backref, aliased
import datetime


engine = create_engine('sqlite:///reminder.db')

Base = declarative_base()

class User(Base):
    __tablename__ = 'users_db'
    id = Column(Integer, Sequence('user_id_seq'), primary_key=True)
    lastname = Column(String)
    firstname = Column(String)
    contact = Column(String)
    history = Column(Text)
    notes = Column(Text)

    addresses = relationship('Address', order_by='Address.id', 
                               backref='user', cascade='all, delete, delete-orphan')


    def __init__(self, firstname, lastname, contact):
        self.firstname = firstname
        self.lastname = lastname
        self.contact = contact

    def __repr__(self):
        return "<User('{0}', '{1}', '{2}')>".format(self.firstname, self.lastname, self.contact)


class Address(Base):
    __tablename__ = 'addresses_db'
    id = Column(Integer, primary_key=True)
    address = Column(String(150))
    date = Column(Date)
    check1 = Column(Boolean)
    check2 = Column(Boolean)

    user_id = Column(Integer, ForeignKey('users_db.id'))

    def __init__(self, address, date):
        self.address = address
        self.date = date
        self.check1 = False
        self.check2 = False

    def __repr__(self):
        return "<Address('{0}', '{1}')>".format(self.address, self.date)

if __name__ == '__main__':
    Base.metadata.create_all(engine)
    Session = sessionmaker(bind=engine)
    session = Session()
    header = [User.firstname, User.lastname, nextaddressfromUser]

>>> for user in session.query(User).all():
...     for addr in user.addresses:
...         print user.firstname, user.lastname, addr.address
4

1 に答える 1

7

まず、クエリを忘れて、使用しているループを使用します。UI で探しているものは基本的なものです。ドキュメントの不足から、基本的なことには QWhateverView よりもQTableWidget (または何か) を使用するのが最善であることがわかりました。QWhateverWidget単純なことのために独自のモデルを定義する必要はありません。そこで、QTableWidget でそれを行う方法を紹介します。(行、列)の各セルに対して QTableWidgetItem を作成する必要があります。私が遭遇した問題の 1 つは、行を追加する前に行数を設定しなければならないことでした。とにかく、ここに:

import sys
from PySide import QtGui, QtCore

class Test(QtGui.QWidget):

    def __init__(self, rows):
        super(Test, self).__init__()

        self.table = QtGui.QTableWidget()
        self.table.setColumnCount(3)
        # Optional, set the labels that show on top
        self.table.setHorizontalHeaderLabels(("First Name", "Last Name", "Address"))

        self.table.setRowCount(len(rows))
        for row, cols in enumerate(rows):
            for col, text in enumerate(cols):
                table_item = QtGui.QTableWidgetItem(text)
                # Optional, but very useful.
                table_item.setData(QtCore.Qt.UserRole+1, user)
                self.table.setItem(row, col, table_item)

        # Also optional. Will fit the cells to its contents.
        self.table.resizeColumnsToContents()

        # Just display the table here.
        layout = QtGui.QHBoxLayout()
        layout.addWidget(self.table)
        self.setLayout(layout)

if __name__ == "__main__":
    # ...
    rows = []
    # Here I have to fill it in an array, because you need to know the number of rows before adding... There might be a better solution though.
    for user in session.query(User).all():
        for addr in user.addresses:
            # These are the columns on each row (firstname, lastname, address)
            rows.append((user.firstname, user.lastname, addr.address))

    app = QtGui.QApplication(sys.argv)
    test = Test(rows)
    test.show()
    app.exec_()

使用を検討できるもう 1 つのことは、複数の列をサポートするQTreeWidgetです。テーブルのように見せることができますが、デフォルトでは編集可能なセルがなく、ここのデータにより適している可能性があります。

クエリについては、結果をループしてユーザーごとに 1 つの追加クエリを作成する必要がないように、1 つのクエリにすることをお勧めします。何かのようなもの:

query = session.query(User.firstname, User.lastname, Address.address).filter(Address.user_id == User.id)
    for row in query.all():
        # firstname, lastname, address = row
        rows.append(row)

多くの行の場合、解決策があると思いますが、独自のモデルを定義LIMITし、クエリで s を使用する必要があります。ドキュメントやチュートリアルが不足しているため、それほど簡単ではありません...

__init__補足として、特別なことは何もしていないので、Address および User クラスでメソッドを定義する必要はありません。SQLAlchemy はこれを自動的に行うことができます。また、デフォルト値を列定義に直接定義できます。

UPDATE : QTableWidgetItem.setDataの使用例として、ダブルクリックしたときにユーザーを削除したいとしましょう。itemDoubleClickedシグナルを使用します。

# in the __init__ function
self.table.itemDoubleClicked.connect(self.onItemDoubleClick)

# in onItemDoubleClicked function
def onItemDoubleClicked(self, item):
    # Every data has a role, you can specify your own (it's an integer) as long as it's greater than UserRole. Others are used internally, like DisplayRole and some others you can find in the QtCore package.
    # You can use data with other widgets also, not just TableWidgets.
    user = item.data(QtCore.Qt.UserRole+1)
    # you get a session however you want, then delete the user. This object is the same as the one you passed earlier when creating the item, it can be whatever you like.
    session.delete(user)
    session.commit()
于 2012-05-16T09:24:01.703 に答える