mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-24 00:47:57 -05:00
325 lines
12 KiB
Python
325 lines
12 KiB
Python
"""
|
|
/***************************************************************************
|
|
Name : DB Manager
|
|
Description : Database manager plugin for QGIS
|
|
Date : Oct 13, 2011
|
|
copyright : (C) 2011 by Giuseppe Sucameli
|
|
email : brush.tyler@gmail.com
|
|
|
|
The content of this file is based on
|
|
- PG_Manager by Martin Dobias (GPLv2 license)
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
***************************************************************************/
|
|
"""
|
|
|
|
from qgis.PyQt import uic
|
|
from qgis.PyQt.QtCore import Qt, QModelIndex
|
|
from qgis.PyQt.QtWidgets import QItemDelegate, QComboBox, QDialog, QPushButton, QDialogButtonBox, QMessageBox, QApplication
|
|
from qgis.PyQt.QtCore import QItemSelectionModel, pyqtSignal
|
|
|
|
from qgis.utils import OverrideCursor
|
|
|
|
from .db_plugins.data_model import TableFieldsModel
|
|
from .db_plugins.plugin import DbError, ConnectionError
|
|
from .dlg_db_error import DlgDbError
|
|
from .gui_utils import GuiUtils
|
|
|
|
Ui_Dialog, _ = uic.loadUiType(GuiUtils.get_ui_file_path('DlgCreateTable.ui'))
|
|
|
|
|
|
class TableFieldsDelegate(QItemDelegate):
|
|
""" delegate with some special item editors """
|
|
|
|
columnNameChanged = pyqtSignal()
|
|
|
|
def __init__(self, field_types, parent=None):
|
|
QItemDelegate.__init__(self, parent)
|
|
self.fieldTypes = field_types
|
|
|
|
def createEditor(self, parent, option, index):
|
|
# special combobox for field type
|
|
if index.column() == 1:
|
|
cbo = QComboBox(parent)
|
|
cbo.setEditable(True)
|
|
cbo.setFrame(False)
|
|
for item in self.fieldTypes:
|
|
cbo.addItem(item)
|
|
return cbo
|
|
return QItemDelegate.createEditor(self, parent, option, index)
|
|
|
|
def setEditorData(self, editor, index):
|
|
""" load data from model to editor """
|
|
m = index.model()
|
|
if index.column() == 1:
|
|
txt = m.data(index, Qt.ItemDataRole.DisplayRole)
|
|
editor.setEditText(txt)
|
|
else:
|
|
# use default
|
|
QItemDelegate.setEditorData(self, editor, index)
|
|
|
|
def setModelData(self, editor, model, index):
|
|
""" save data from editor back to model """
|
|
if index.column() == 1:
|
|
model.setData(index, editor.currentText())
|
|
else:
|
|
# use default
|
|
QItemDelegate.setModelData(self, editor, model, index)
|
|
if index.column() == 0:
|
|
self.columnNameChanged.emit()
|
|
|
|
|
|
class DlgCreateTable(QDialog, Ui_Dialog):
|
|
GEOM_TYPES = ["POINT", "LINESTRING", "POLYGON", "MULTIPOINT", "MULTILINESTRING", "MULTIPOLYGON",
|
|
"GEOMETRYCOLLECTION"]
|
|
|
|
def __init__(self, item, parent=None):
|
|
QDialog.__init__(self, parent)
|
|
self.item = item
|
|
self.setupUi(self)
|
|
|
|
self.db = self.item.database()
|
|
self.schemas = self.db.schemas()
|
|
self.hasSchemas = self.schemas is not None
|
|
self.fieldTypes = self.db.connector.fieldTypes()
|
|
|
|
m = TableFieldsModel(self, True) # it's editable
|
|
self.fields.setModel(m)
|
|
self.fields.setColumnHidden(3, True) # hide Default column
|
|
|
|
d = TableFieldsDelegate(self.fieldTypes, self)
|
|
self.fields.setItemDelegate(d)
|
|
|
|
self.fields.setColumnWidth(0, 140)
|
|
self.fields.setColumnWidth(1, 140)
|
|
self.fields.setColumnWidth(2, 50)
|
|
|
|
b = QPushButton(self.tr("&Create"))
|
|
self.buttonBox.addButton(b, QDialogButtonBox.ButtonRole.ActionRole)
|
|
|
|
self.btnAddField.clicked.connect(self.addField)
|
|
self.btnDeleteField.clicked.connect(self.deleteField)
|
|
self.btnFieldUp.clicked.connect(self.fieldUp)
|
|
self.btnFieldDown.clicked.connect(self.fieldDown)
|
|
b.clicked.connect(self.createTable)
|
|
|
|
self.chkGeomColumn.clicked.connect(self.updateUi)
|
|
|
|
self.fields.selectionModel().selectionChanged.connect(self.updateUiFields)
|
|
d.columnNameChanged.connect(self.updatePkeyCombo)
|
|
|
|
self.populateSchemas()
|
|
self.updateUi()
|
|
self.updateUiFields()
|
|
|
|
def populateSchemas(self):
|
|
self.cboSchema.clear()
|
|
if not self.hasSchemas:
|
|
self.hideSchemas()
|
|
return
|
|
|
|
index = -1
|
|
for schema in self.schemas:
|
|
self.cboSchema.addItem(schema.name)
|
|
if hasattr(self.item, 'schema') and schema.name == self.item.schema().name:
|
|
index = self.cboSchema.count() - 1
|
|
self.cboSchema.setCurrentIndex(index)
|
|
|
|
def hideSchemas(self):
|
|
self.cboSchema.setEnabled(False)
|
|
|
|
def updateUi(self):
|
|
useGeom = self.chkGeomColumn.isChecked()
|
|
self.cboGeomType.setEnabled(useGeom)
|
|
self.editGeomColumn.setEnabled(useGeom)
|
|
self.spinGeomDim.setEnabled(useGeom)
|
|
self.editGeomSrid.setEnabled(useGeom)
|
|
self.chkSpatialIndex.setEnabled(useGeom)
|
|
|
|
def updateUiFields(self):
|
|
fld = self.selectedField()
|
|
if fld is not None:
|
|
up_enabled = (fld != 0)
|
|
down_enabled = (fld != self.fields.model().rowCount() - 1)
|
|
del_enabled = True
|
|
else:
|
|
up_enabled, down_enabled, del_enabled = False, False, False
|
|
self.btnFieldUp.setEnabled(up_enabled)
|
|
self.btnFieldDown.setEnabled(down_enabled)
|
|
self.btnDeleteField.setEnabled(del_enabled)
|
|
|
|
def updatePkeyCombo(self, selRow=None):
|
|
""" called when list of columns changes. if 'sel' is None, it keeps current index """
|
|
|
|
if selRow is None:
|
|
selRow = self.cboPrimaryKey.currentIndex()
|
|
|
|
self.cboPrimaryKey.clear()
|
|
|
|
m = self.fields.model()
|
|
for row in range(m.rowCount()):
|
|
name = m.data(m.index(row, 0))
|
|
self.cboPrimaryKey.addItem(name)
|
|
|
|
self.cboPrimaryKey.setCurrentIndex(selRow)
|
|
|
|
def addField(self):
|
|
"""Adds new field to the end of field table """
|
|
m = self.fields.model()
|
|
newRow = m.rowCount()
|
|
m.insertRows(newRow, 1)
|
|
|
|
indexName = m.index(newRow, 0, QModelIndex())
|
|
indexType = m.index(newRow, 1, QModelIndex())
|
|
indexNull = m.index(newRow, 2, QModelIndex())
|
|
|
|
m.setData(indexName, "new_field")
|
|
colType = self.fieldTypes[0]
|
|
if newRow == 0:
|
|
# adding the first row, use auto-incrementing column type if any
|
|
if "serial" in self.fieldTypes: # PostgreSQL
|
|
colType = "serial"
|
|
m.setData(indexType, colType)
|
|
m.setData(indexNull, None, Qt.ItemDataRole.DisplayRole)
|
|
m.setData(indexNull, Qt.CheckState.Unchecked, Qt.ItemDataRole.CheckStateRole)
|
|
|
|
# selects the new row
|
|
sel = self.fields.selectionModel()
|
|
sel.select(indexName, QItemSelectionModel.SelectionFlag.Rows | QItemSelectionModel.SelectionFlag.ClearAndSelect)
|
|
|
|
# starts editing
|
|
self.fields.edit(indexName)
|
|
|
|
self.updatePkeyCombo(0 if newRow == 0 else None)
|
|
|
|
def selectedField(self):
|
|
sel = self.fields.selectedIndexes()
|
|
if len(sel) < 1:
|
|
return None
|
|
return sel[0].row()
|
|
|
|
def deleteField(self):
|
|
"""Deletes selected field """
|
|
row = self.selectedField()
|
|
if row is None:
|
|
QMessageBox.information(self, self.tr("DB Manager"), self.tr("No field selected."))
|
|
else:
|
|
self.fields.model().removeRows(row, 1)
|
|
|
|
self.updatePkeyCombo()
|
|
|
|
def fieldUp(self):
|
|
""" move selected field up """
|
|
row = self.selectedField()
|
|
if row is None:
|
|
QMessageBox.information(self, self.tr("DB Manager"), self.tr("No field selected."))
|
|
return
|
|
if row == 0:
|
|
QMessageBox.information(self, self.tr("DB Manager"), self.tr("Field is already at the top."))
|
|
return
|
|
|
|
# take row and reinsert it
|
|
rowdata = self.fields.model().takeRow(row)
|
|
self.fields.model().insertRow(row - 1, rowdata)
|
|
|
|
# set selection again
|
|
index = self.fields.model().index(row - 1, 0, QModelIndex())
|
|
self.fields.selectionModel().select(index, QItemSelectionModel.SelectionFlag.Rows | QItemSelectionModel.SelectionFlag.ClearAndSelect)
|
|
|
|
self.updatePkeyCombo()
|
|
|
|
def fieldDown(self):
|
|
""" move selected field down """
|
|
row = self.selectedField()
|
|
if row is None:
|
|
QMessageBox.information(self, self.tr("DB Manager"), self.tr("No field selected."))
|
|
return
|
|
if row == self.fields.model().rowCount() - 1:
|
|
QMessageBox.information(self, self.tr("DB Manager"), self.tr("Field is already at the bottom."))
|
|
return
|
|
|
|
# take row and reinsert it
|
|
rowdata = self.fields.model().takeRow(row)
|
|
self.fields.model().insertRow(row + 1, rowdata)
|
|
|
|
# set selection again
|
|
index = self.fields.model().index(row + 1, 0, QModelIndex())
|
|
self.fields.selectionModel().select(index, QItemSelectionModel.SelectionFlag.Rows | QItemSelectionModel.SelectionFlag.ClearAndSelect)
|
|
|
|
self.updatePkeyCombo()
|
|
|
|
def createTable(self):
|
|
"""Creates table with chosen fields, optionally add a geometry column """
|
|
if not self.hasSchemas:
|
|
schema = None
|
|
else:
|
|
schema = str(self.cboSchema.currentText())
|
|
if len(schema) == 0:
|
|
QMessageBox.information(self, self.tr("DB Manager"), self.tr("A valid schema must be selected first."))
|
|
return
|
|
|
|
table = str(self.editName.text())
|
|
if len(table) == 0:
|
|
QMessageBox.information(self, self.tr("DB Manager"), self.tr("A valid table name is required."))
|
|
return
|
|
|
|
m = self.fields.model()
|
|
if m.rowCount() == 0:
|
|
QMessageBox.information(self, self.tr("DB Manager"), self.tr("At least one field is required."))
|
|
return
|
|
|
|
useGeomColumn = self.chkGeomColumn.isChecked()
|
|
if useGeomColumn:
|
|
geomColumn = str(self.editGeomColumn.text())
|
|
if len(geomColumn) == 0:
|
|
QMessageBox.information(self, self.tr("DB Manager"), self.tr("A name is required for the geometry column."))
|
|
return
|
|
|
|
geomType = self.GEOM_TYPES[self.cboGeomType.currentIndex()]
|
|
geomDim = self.spinGeomDim.value()
|
|
try:
|
|
geomSrid = int(self.editGeomSrid.text())
|
|
except ValueError:
|
|
geomSrid = 0
|
|
useSpatialIndex = self.chkSpatialIndex.isChecked()
|
|
|
|
flds = m.getFields()
|
|
pk_index = self.cboPrimaryKey.currentIndex()
|
|
if pk_index >= 0:
|
|
flds[pk_index].primaryKey = True
|
|
|
|
# commit to DB
|
|
with OverrideCursor(Qt.CursorShape.WaitCursor):
|
|
try:
|
|
if not useGeomColumn:
|
|
self.db.createTable(table, flds, schema)
|
|
else:
|
|
geom = geomColumn, geomType, geomSrid, geomDim, useSpatialIndex
|
|
self.db.createVectorTable(table, flds, geom, schema)
|
|
|
|
except (ConnectionError, DbError) as e:
|
|
DlgDbError.showError(e, self)
|
|
|
|
# clear UI
|
|
self.editName.clear()
|
|
self.fields.model().removeRows(0, self.fields.model().rowCount())
|
|
self.cboPrimaryKey.clear()
|
|
self.chkGeomColumn.setChecked(False)
|
|
self.chkSpatialIndex.setChecked(False)
|
|
self.editGeomSrid.clear()
|
|
|
|
self.cboGeomType.setEnabled(False)
|
|
self.editGeomColumn.setEnabled(False)
|
|
self.spinGeomDim.setEnabled(False)
|
|
self.editGeomSrid.setEnabled(False)
|
|
self.chkSpatialIndex.setEnabled(False)
|
|
|
|
QMessageBox.information(self, self.tr("DB Manager"), self.tr("Table created successfully."))
|