Python PyQt5:将扩展加载到sqlite

Python PyQt5:将扩展加载到sqlite,python,sqlite,pyqt,pyqt5,Python,Sqlite,Pyqt,Pyqt5,我将开始使用PyQt5及其sqlite的Sql类。我想将扩展加载到sqlite中。为此,必须在运行时为sqlite启用扩展加载。在python模块中,这是通过启用的 < C++ > 代码> SQLite 句柄可以从这里得到(取自): 作为旁注,在Pyside2中,handle方法没有公开 看来这样做是不对的。有没有办法通过PyQt5加载sqlite扩展?一种可能的解决方案是创建一个使用ctypes加载的库 在本例中,我展示了ubuntu linux的解决方案,但我认为类似的步骤也可以应用于其他操

我将开始使用PyQt5及其sqlite的Sql类。我想将扩展加载到sqlite中。为此,必须在运行时为sqlite启用扩展加载。在python模块中,这是通过启用的

< C++ > <>代码> SQLite 句柄可以从这里得到(取自):

作为旁注,在Pyside2中,handle方法没有公开


看来这样做是不对的。有没有办法通过PyQt5加载sqlite扩展?

一种可能的解决方案是创建一个使用ctypes加载的库

在本例中,我展示了ubuntu linux的解决方案,但我认为类似的步骤也可以应用于其他操作系统

编译库 qsqlite.pro

QT -= gui
QT += sql
TEMPLATE = lib
DEFINES += QSQLITE_LIBRARY
CONFIG += unversioned_libname unversioned_soname
CONFIG += c++11
SOURCES += \
    qsqlite.cpp

HEADERS += \
    qsqlite_global.h \
    qsqlite.h

LIBS += -lsqlite3
qsqlite_global.h

#ifndef QSQLITE_GLOBAL_H
#define QSQLITE_GLOBAL_H

#if defined(_MSC_VER) || defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
#  define Q_DECL_EXPORT __declspec(dllexport)
#  define Q_DECL_IMPORT __declspec(dllimport)
#else
#  define Q_DECL_EXPORT     __attribute__((visibility("default")))
#  define Q_DECL_IMPORT     __attribute__((visibility("default")))
#endif

#if defined(QSQLITE_LIBRARY)
#  define QSQLITE_EXPORT Q_DECL_EXPORT
#else
#  define QSQLITE_EXPORT Q_DECL_IMPORT
#endif

#endif // QSQLITE_GLOBAL_H
#ifndef QSQLITE_H
#define QSQLITE_H

#include "qsqlite_global.h"

class QSqlDriver;

extern "C" {
    bool QSQLITE_EXPORT enable_extension(QSqlDriver *ptr, bool enabled);
}

#endif // QSQLITE_H
qsqlite.h

#ifndef QSQLITE_GLOBAL_H
#define QSQLITE_GLOBAL_H

#if defined(_MSC_VER) || defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
#  define Q_DECL_EXPORT __declspec(dllexport)
#  define Q_DECL_IMPORT __declspec(dllimport)
#else
#  define Q_DECL_EXPORT     __attribute__((visibility("default")))
#  define Q_DECL_IMPORT     __attribute__((visibility("default")))
#endif

#if defined(QSQLITE_LIBRARY)
#  define QSQLITE_EXPORT Q_DECL_EXPORT
#else
#  define QSQLITE_EXPORT Q_DECL_IMPORT
#endif

#endif // QSQLITE_GLOBAL_H
#ifndef QSQLITE_H
#define QSQLITE_H

#include "qsqlite_global.h"

class QSqlDriver;

extern "C" {
    bool QSQLITE_EXPORT enable_extension(QSqlDriver *ptr, bool enabled);
}

#endif // QSQLITE_H
qsqlite.cpp

#include "qsqlite.h"

#include <sqlite3.h>

#include <QSqlDriver>
#include <QVariant>

bool enable_extension(QSqlDriver *driver, bool enabled)
{
    if(!driver)
        return false;
    QVariant v = driver->handle();
    if (!v.isValid() || !(qstrcmp(v.typeName(), "sqlite3*")==0))
        return false;
    if(sqlite3 *db_handle = *static_cast<sqlite3 **>(v.data())){
        sqlite3_initialize();
        sqlite3_enable_load_extension(db_handle, enabled);
        return true;
    }
    return false;
}
要编译,您必须使用Qt,因此在本例中,我将通过执行以下命令来使用aqtinstall(
python-m pip install aqtinstall
):

python -m aqt install 5.15.0 linux desktop --outputdir qt
qt/5.15.0/gcc_64/bin/qmake qsqlite
make
注意:要编译库,必须有sqlite3的头。为此,您必须使用ubuntu中的
libsqlite3 dev
安装:
sudo apt install-y——没有安装建议使用libsqlite3 dev

这将创建必须复制到脚本旁边的libqsqlite.so库,例如,以下代码加载spatialite模块(
sudo apt install-y——无安装建议使用libsqlite3 mod spatialite

main.py

from ctypes import CDLL, c_void_p
import os

from PyQt5.QtSql import QSqlDatabase, QSqlQuery

import sip

CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))


def load_spatialite():
    queries = (
        "SELECT load_extension('mod_spatialite')",
        "SELECT InitSpatialMetadata(1)",
    )
    q = QSqlQuery()
    for query in queries:
        if not q.exec_(query):
            print(
                f"Error: cannot load the Spatialite extension ({q.lastError().text()})"
            )
            return False
    return True


def main():
    db = QSqlDatabase.addDatabase("QSQLITE")

    db.setDatabaseName("foo.sqlite")
    if not db.open():
        sys.exit(-1)

    lib = CDLL(os.path.join(CURRENT_DIR, "libqsqlite.so"))
    lib.enable_extension(c_void_p(sip.unwrapinstance(db.driver()).__int__()), True)
    load_spatialite()

    query = QSqlQuery()

    query.exec_("CREATE TABLE my_line(id INTEGER PRIMARY KEY)")
    query.exec_(
        """SELECT AddGeometryColumn("my_line","geom" , 4326, "LINESTRING", 2)"""
    )

    polygon_wkt = "POLYGON ((11 50,11 51,12 51,12 50,11 50))"

    XA = 11
    YA = 52
    XB = 12
    YB = 49

    line_wkt = "LINESTRING({0} {1}, {2} {3})".format(XA, YA, XB, YB)

    query.prepare("""INSERT INTO my_line VALUES (?,GeomFromText(?, 4326))""")

    query.addBindValue(1)
    query.addBindValue(line_wkt)
    query.exec_()

    query.prepare(
        """SELECT astext(st_intersection(geom, GeomFromText(?, 4326))) from my_line WHERE st_intersects(geom, GeomFromText(?, 4326))"""
    )
    query.addBindValue(polygon_wkt)
    query.addBindValue(polygon_wkt)
    query.exec_()

    while query.next():
        for i in range(query.record().count()):
            print(query.value(i))


if __name__ == "__main__":
    main()
输出:

LINESTRING(11.333333 51, 11.666667 50)

PySide2可以使用相同的库:

from ctypes import CDLL, c_void_p
import os

from PySide2.QtSql import QSqlDatabase, QSqlQuery

import shiboken2

CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))


def load_spatialite():
    queries = (
        "SELECT load_extension('mod_spatialite')",
        "SELECT InitSpatialMetadata(1)",
    )
    q = QSqlQuery()
    for query in queries:
        if not q.exec_(query):
            print(
                f"Error: cannot load the Spatialite extension ({q.lastError().text()})"
            )
            return False
    return True


def main():
    db = QSqlDatabase.addDatabase("QSQLITE")

    db.setDatabaseName("foo.sqlite")
    if not db.open():
        sys.exit(-1)

    lib = CDLL(os.path.join(CURRENT_DIR, "libqsqlite.so"))
    lib.enable_extension(c_void_p(shiboken2.getCppPointer(db.driver())[0]))
    load_spatialite()

    query = QSqlQuery()

    query.exec_("CREATE TABLE my_line(id INTEGER PRIMARY KEY)")
    query.exec_(
        """SELECT AddGeometryColumn("my_line","geom" , 4326, "LINESTRING", 2)"""
    )

    polygon_wkt = "POLYGON ((11 50,11 51,12 51,12 50,11 50))"

    XA = 11
    YA = 52
    XB = 12
    YB = 49

    line_wkt = "LINESTRING({0} {1}, {2} {3})".format(XA, YA, XB, YB)

    query.prepare("""INSERT INTO my_line VALUES (?,GeomFromText(?, 4326))""")

    query.addBindValue(1)
    query.addBindValue(line_wkt)
    query.exec_()

    query.prepare(
        """SELECT astext(st_intersection(geom, GeomFromText(?, 4326))) from my_line WHERE st_intersects(geom, GeomFromText(?, 4326))"""
    )
    query.addBindValue(polygon_wkt)
    query.addBindValue(polygon_wkt)
    query.exec_()

    while query.next():
        for i in range(query.record().count()):
            print(query.value(i))


if __name__ == "__main__":
    main()
对于测试,我使用了您可以找到的docker

LINESTRING(11.333333 51, 11.666667 50)
from ctypes import CDLL, c_void_p
import os

from PySide2.QtSql import QSqlDatabase, QSqlQuery

import shiboken2

CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))


def load_spatialite():
    queries = (
        "SELECT load_extension('mod_spatialite')",
        "SELECT InitSpatialMetadata(1)",
    )
    q = QSqlQuery()
    for query in queries:
        if not q.exec_(query):
            print(
                f"Error: cannot load the Spatialite extension ({q.lastError().text()})"
            )
            return False
    return True


def main():
    db = QSqlDatabase.addDatabase("QSQLITE")

    db.setDatabaseName("foo.sqlite")
    if not db.open():
        sys.exit(-1)

    lib = CDLL(os.path.join(CURRENT_DIR, "libqsqlite.so"))
    lib.enable_extension(c_void_p(shiboken2.getCppPointer(db.driver())[0]))
    load_spatialite()

    query = QSqlQuery()

    query.exec_("CREATE TABLE my_line(id INTEGER PRIMARY KEY)")
    query.exec_(
        """SELECT AddGeometryColumn("my_line","geom" , 4326, "LINESTRING", 2)"""
    )

    polygon_wkt = "POLYGON ((11 50,11 51,12 51,12 50,11 50))"

    XA = 11
    YA = 52
    XB = 12
    YB = 49

    line_wkt = "LINESTRING({0} {1}, {2} {3})".format(XA, YA, XB, YB)

    query.prepare("""INSERT INTO my_line VALUES (?,GeomFromText(?, 4326))""")

    query.addBindValue(1)
    query.addBindValue(line_wkt)
    query.exec_()

    query.prepare(
        """SELECT astext(st_intersection(geom, GeomFromText(?, 4326))) from my_line WHERE st_intersects(geom, GeomFromText(?, 4326))"""
    )
    query.addBindValue(polygon_wkt)
    query.addBindValue(polygon_wkt)
    query.exec_()

    while query.next():
        for i in range(query.record().count()):
            print(query.value(i))


if __name__ == "__main__":
    main()