多线程SQLAlchemy并将结果返回给ObjectListView

时间:2013-11-11 17:34:17

标签: multithreading sqlite sqlalchemy wxpython objectlistview

我遇到了正在处理的程序的另一个问题。基本上我的程序所做的是它最多需要4个输入文件,处理它们并将我从它们收集的信息存储在我计算机上的SQLite3数据库中。这使我可以随时查看数据,而无需再次运行输入文件。该程序使用一个主脚本,它基本上只是一个导入输入脚本的AUI笔记本,并输出脚本用作面板。

要将数据添加到数据库,我可以使用线程,因为我没有将结果直接返回到输出屏幕。但是,当我需要查看主表中的所有内容时,我最终会加载25,000条记录。当这些加载时,我的GUI被锁定,几乎总是显示:“程序没有响应”。

我想使用线程/多处理从数据库中获取25k记录并将它们加载到我的ObjectListView小部件中,以便我的GUI在此过程中仍然可用。当我尝试使用用于将数据添加到数据库的类似线程类时,我得不到任何返回。当我说我什么也得不到时,我并不夸张。

所以这是我的一个大问题,是否有一种方法可以在不使用全局变量的情况下对查询进行线程化并返回结果?我无法找到一个我能理解的例子的解决方案,但我可能使用了错误的搜索词。

以下是与手头问题有关的代码片段:

这是我用来确保数据为ObjectListView小部件做好准备的。

class OlvMainDisplay(object):
    def __init__(self, id, name, col01, col02, col03, col04, col05,
                 col06, col07, col08, col09, col10, col11,
                 col12, col13, col14, col15):
        self.id    = id
        self.name  = name
        self.col01 = col01
        self.col02 = col02
        self.col03 = col03
        self.col04 = col04
        self.col05 = col05
        self.col06 = col06
        self.col07 = col07
        self.col08 = col08
        self.col09 = col09
        self.col10 = col10
        self.col11 = col11
        self.col12 = col12
        self.col13 = col13
        self.col14 = col14
        self.col15 = col15

我从中提取数据的两个表:

class TableMeta(base):
    __tablename__ = 'meta_extra'
    id = Column(String(20), ForeignKey('main_data.id'), primary_key=True)
    col06 = Column(String)
    col08 = Column(String)
    col02 = Column(String)
    col03 = Column(String)
    col04 = Column(String)
    col09 = Column(String)
    col10 = Column(String)
    col11 = Column(String)
    col12 = Column(String)
    col13 = Column(String)
    col14 = Column(String)
    col15 = Column(String)


class TableMain(base):
    __tablename__ = 'main_data'
    id    = Column(String(20), primary_key=True)
    name  = Column(String)
    col01 = Column(String)
    col05 = Column(String)
    col07 = Column(String)

    extra_data = relation(
        TableMeta, uselist=False, backref=backref('main_data', order_by=id))

我使用2个查询从这两个表中收集,一个获取所有记录,而另一个是函数定义的一部分,该函数定义采用多个字典并根据字典内容应用过滤器。这两个查询都是我的每个笔记本电脑面板导入的主“工作”脚本的一部分。

以下是应用过滤器的函数:

def multiFilter(theFilters, table, anOutput, qType):
    session = Session()
    anOutput = session.query(table)
    try:
        for x in theFilters:
            for attr, value in x.items():
                anOutput = anOutput.filter(getattr(table, attr).in_(value))
    except AttributeError:
        for attr, value in theFilters.items():
            anOutput = anOutput.filter(getattr(table, attr).in_(value))

    anOutput = convertResults(anOutput.all())
    return anOutput
    session.close()

theFilters可以是单个字典,也可以是字典列表,因此是“Try:”语句。一旦函数应用了过滤器,它就会通过另一个函数运行返回的结果,该函数将通过OlvMainDisplay类返回的每个结果放入一个列表中,然后传递给OLV Widget。

同样重要的问题是,有没有办法在不使用全局变量的情况下对查询(或查询)进行线程化并返回结果?或者可能一次抓取大约200条记录并将“以块为单位”的数据添加到OLV小部件中?

提前谢谢你 -MikeS

- UPDATE -
我已经审核了"how to get the return value from a thread in python",并且接受的答案没有返回任何内容或仍然锁定了GUI(不确定是什么导致了差异)。我想将创建的线程数限制在最多5个。

- 新更新 - 我对过滤器功能进行了一些修正。

2 个答案:

答案 0 :(得分:1)

您可能不希望立即将整个数据库加载到内存中。这通常是一个坏主意。因为ObjectListView是ListCtrl的包装器,所以我建议使用底层小部件的虚拟版本。标志是wx.LC_VIRTUAL。看一下wxPython演示的示例,但基本上您可以通过虚拟方法OnGetItemText(),OnGetItemImage()和OnGetItemAttr()按需加载数据。请注意,这是指ListVtrl方法......在OLV土地中可能有所不同。无论如何,我知道OLV版本被称为VirtualObjectListView并且工作方式大致相同。我很确定源代码下载中有一个例子。

答案 1 :(得分:0)

好的,我终于设法让查询在一个线程中运行,并能够在标准的ObjectListView中显示结果。我使用了答案 HERE 进行了一些修改。

我将代码添加到我的主工作人员脚本中,该脚本以 EW 的形式导入我的输出面板。
由于我没有将参数传递给我的查询,因此这些行已更改:

    def start(self, params):

    self.thread = threading.Thread(target=self.func, args=params)

    def start(self):

    self.thread = threading.Thread(target=self.func)

在我的输出面板中,我更改了如何调用默认查询,即返回25,000条记录的查询。在我的输出面板的init中,我添加了 self.worker =()作为占位符,并在运行默认查询的函数中添加:

def defaultView(self, evt):
    self.worker = EW.ThreadWorker(EW.defaultQuery)
    self.worker.start()
    pub.sendMessage('update.statusbar', msg='Full query started.')

我还补充说:

def threadUpdateOLV(self):
    time.sleep(10)
    anOutput = self.worker.get_results()
    self.dataOLV.SetObjects(anOutput)

pub.subscribe(self.threadUpdateOLV, 'thread.completed')

time.sleep(10)在试用后添加了错误以获得完整的25,000个结果,我发现10秒的延迟工作正常。

最后,在我的默认查询结束时,我在输出返回之前添加了PubSub发送:

wx.CallAfter(pub.sendMessage, 'thread.completed')
return anOutput
session.close()

说实话,我确信有更好的方法可以实现这一目标,但截至目前,它正在满足所需的目的。我会努力寻找更好的解决方案。

感谢
-Mike S