QGIS/python/plugins/db_manager/dlg_create_table.py
Nyall Dawson b369f1e1b0 Port dbmanager away from compiled resources
These are not recommended, and removed in PyQt6
2024-02-28 11:23:47 +10:00

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."))