0

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

要将数据添加到数据库中,我可以使用线程,因为我不会将结果直接返回到我的输出屏幕。但是,当我需要查看主表中的全部内容时,我最终会加载 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

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

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 个查询从这 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 小部件?

先感谢您。
-迈克

--UPDATE--
我已经查看了“如何从 python 中的线程获取返回值”并且接受的答案没有返回任何内容或仍然锁定 GUI(不确定是什么导致了差异)。我想将创建的线程数限制在最多 5 个左右。

--新更新-- 我对过滤器功能做了一些更正。

4

2 回答 2

1

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

于 2013-11-11T19:30:18.593 回答
0

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

我将代码添加到我的主工作脚本中,该脚本作为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 多条记录的查询。在我的输出面板的初始化中,我添加了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')

在尝试错误以获得完整的 25,000 多个结果后添加了 time.sleep(10),我发现 10 秒的延迟效果很好

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

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

老实说,我确信有更好的方法来实现这一点,但就目前而言,它正在服务于所需的目的。不过,我会努力寻找更好的解决方案。

谢谢
-迈克小号

于 2013-11-26T20:03:29.190 回答