3 votos

Creación de QgsVectorLayer en Qthread (pyQt)

Nuestro proyecto QGIS se genera dinámicamente en el inicio basado en los roles de usuario. Hay cerca de 120 QgsVectorLayers que necesitan ser generados. Si se crean secuencialmente, el proceso de inicio de sesión tarda hasta 2 minutos, lo que es realmente molesto.

Así que pensé en usar un QThreadPool y crear las capas allí. Así que los hilos básicamente sólo llaman a este tipo de código:

class LayerLoaderWorker(QRunnable):

    def run(self):
        db_connect = DatabaseConnection()
        uri = QgsDataSourceUri()
        uri.setConnection(db_connect.host, str(db_connect.port), db_connect.dbname, db_connect.user,
                          db_connect.password)
        uri.setDataSource('public', self.viewname, 'geometry', '', 'id')

        locker = QMutexLocker(mutex)
        self.layer = QgsVectorLayer(uri.uri(False), self.layerSettings.displayname, "postgres")
        self.finished = True
        self.loader.workerFinished()
        locker.unlock()

Sólo las capas se crean en los hilos, después de la creación se añaden a la QgsProject en el hilo principal (UI). A veces parece funcionar, pero la mayoría de las veces las capas no se cargan correctamente y la columna de geometría está vacía.

¿Es la creación del QgsVectorLayer objeto thread safe? Me parece que este no es el caso. Leí en alguna parte que las cosas relacionadas con la interfaz de usuario deben ser manejados en el UI-Thread, pero pensé que la creación del objeto (que parece conectarse a la base de datos que lleva algún tiempo) es una tarea no-ui.

¿Es posible cargar capas en paralelo de alguna manera?

0voto

Julien Ricard Puntos 11

Vale me rindo, he probado todo lo que se me ha ocurrido. He borrado el objeto, he enviado el deleteLater al metaObjeto de la tarea y así sucesivamente. Nada parece funcionar.

He utilizado QThreads, QgsTasks como clases personalizadas y ahora he intentado utilizar QgsTask.fromFunction Nada funciona. Tan pronto como se devuelve la capa, la conexión db nunca se cierra.

Este es el código de ejemplo que estoy utilizando ahora mismo:

@dataclass(frozen=True)
class LookupLoaderData:
    businessTypeId: int
    layer: QgsVectorLayer
    public_table: str

def loadLookupTableLayer(task, businessTypeId):
    display_name = BusinessModel().getObjectDisplayName(businessTypeId=businessTypeId)

    QgsMessageLog.logMessage('loading lookup layer name "{}"'.format(display_name),
                             MESSAGE_CATEGORY, Qgis.Info)

    uri = QgsDataSourceUri()
    uri.setConnection(db_connect.host, str(db_connect.port), db_connect.dbname,
                      db_connect.user, db_connect.password)
    uri.setDataSource(schema, public_table, None, None, None)
    uri.setWkbType(QgsWkbTypes.NoGeometry)
    layer = QgsVectorLayer(uri.uri(), display_name, "postgres")
    return LookupLoaderData(businessTypeId, layer, public_table)

def loadLookupTableLayerCompleted(exception, result: Optional[LookupLoaderData] = None):
    if not result or not result.layer:
        return
    oldName = result.layer.name()
    result.layer.setName(f'DEBUG ({result.businessTypeId}) {oldName}')
    result.layer.setReadOnly(True)
    layerTree = iface.layerTreeCanvasBridge().rootGroup()
    layer_group = layerTree.findGroup("Lookups")
    if not layer_group:
        layer_group = layerTree.insertGroup(0, "Lookups")
    layer_group.setItemVisibilityChecked(False)
    layer_group.setExpanded(False)
    QgsProject.instance().addMapLayer(result.layer, False)
    inserted_layer = layer_group.insertLayer(0, result.layer)
    inserted_layer.setItemVisibilityChecked(False)
    inserted_layer.setExpanded(False)

Las conexiones permanecen abiertas y la base de datos acabará rechazando nuevas conexiones. Si devuelvo algo como esto desde la función worker:

return LookupLoaderData(businessTypeId, None, public_table)

Las conexiones se cierran porque se mata el objeto, al menos es lo que pienso :-) Según el tiempo que tarda en cargar todas estas capas, las tareas están realmente haciendo algo, pero desgraciadamente no soy capaz de saberlo porque ahora no tengo ningún objeto capa que se pueda añadir al proyecto y el objeto layerTree .

Parece imposible cargar capas vectoriales en segundo plano, al menos con un proveedor de datos postgres.

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X