QGIS/python/plugins/db_manager/dlg_query_builder.py
2015-05-04 17:19:09 +02:00

382 lines
15 KiB
Python

# -*- coding: utf-8 -*-
"""
/***************************************************************************
Name : DB Manager
Description : Database manager plugin for QGIS
Date : March 2015
copyright : (C) 2015 Hugo Mercier / Oslandia
email : hugo dot mercier at oslandia dot com
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
Query builder dialog, based on the QSpatialite plugin (GPLv2+) by Romain Riviere
"""
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from .ui.ui_DlgQueryBuilder import Ui_DbManagerQueryBuilderDlg as Ui_Dialog
from .db_plugins.plugin import VectorTable
class FocusEventFilter(QObject):
def __init__( self, parent ):
QObject.__init__( self, parent)
self.focus = ''
def eventFilter( self, obj, event ):
if event.type() == QEvent.FocusIn:
self.focus = obj.objectName()
return QObject.eventFilter( self, obj, event )
def insertWithSelection( widget, text ):
if widget.textCursor().hasSelection(): #user has selectedsomething...
selection=widget.textCursor().selectedText()
widget.insertPlainText(text+selection+")")
else:
widget.insertPlainText(text)
def insertWithSelectionOn( parent, objectname, text ):
"""Insert the text in a QTextEdit given by its objectname"""
w = parent.findChild( QTextEdit, objectname )
insertWithSelection( w, text )
class QueryBuilderDlg(QDialog):
# object used to store parameters between invocations
saveParameter = None
def __init__(self, iface, db, parent=None, reset = False):
QDialog.__init__(self, parent)
self.iface = iface
self.db = db
self.query = ''
self.ui = Ui_Dialog()
self.ui.setupUi(self)
self.ui.group.setMaximumHeight(self.ui.tab.sizeHint().height())
self.ui.order.setMaximumHeight(self.ui.tab.sizeHint().height())
self.evt = FocusEventFilter( self )
self.ui.col.installEventFilter( self.evt )
self.ui.where.installEventFilter( self.evt )
self.ui.group.installEventFilter( self.evt )
self.ui.order.installEventFilter( self.evt )
d = self.db.connector.getQueryBuilderDictionary()
#Application default parameters
self.table=None
self.col_col=[]
self.col_where=[]
self.coltables=[]
self.ui.extract.setChecked(True)
#ComboBox default values
self.ui.functions.insertItems(1,d['function'])
self.ui.math.insertItems(1,d['math'])
self.ui.aggregates.insertItems(1,d['aggregate'])
self.ui.operators.insertItems(1,d['operator'])
self.ui.stringfct.insertItems(1,d['string'])
#self.ui.Rtree.insertItems(1,rtreecommand)
# restore last query if needed
if reset:
QueryBuilderDlg.saveParameter = None
if QueryBuilderDlg.saveParameter is not None:
self.restoreLastQuery()
#Show Tables
self.show_tables()
#Signal/slot
QObject.connect(self.ui.aggregates,SIGNAL("currentIndexChanged(const QString&)"),self.add_aggregate)
QObject.connect(self.ui.stringfct,SIGNAL("currentIndexChanged(const QString&)"),self.add_stringfct)
QObject.connect(self.ui.operators,SIGNAL("currentIndexChanged(const QString&)"),self.add_operators)
QObject.connect(self.ui.functions,SIGNAL("currentIndexChanged(const QString&)"),self.add_functions)
QObject.connect(self.ui.math,SIGNAL("currentIndexChanged(const QString&)"),self.add_math)
QObject.connect(self.ui.tables,SIGNAL("currentIndexChanged(const QString&)"),self.add_tables)
QObject.connect(self.ui.tables,SIGNAL("currentIndexChanged(const QString&)"),self.list_cols)
QObject.connect(self.ui.columns,SIGNAL("currentIndexChanged(const QString&)"),self.add_columns)
QObject.connect(self.ui.columns_2,SIGNAL("currentIndexChanged(const QString&)"),self.list_values)
QObject.connect(self.ui.reset,SIGNAL("clicked(bool)"),self.reset)
QObject.connect(self.ui.extract,SIGNAL("stateChanged(int)"),self.list_values)
QObject.connect(self.ui.values,SIGNAL("doubleClicked(const QModelIndex &)"),self.query_item)
QObject.connect(self.ui.buttonBox,SIGNAL("accepted()"),self.validate)
QObject.connect(self.ui.checkBox,SIGNAL("stateChanged(int)"),self.show_tables)
if self.db.explicitSpatialIndex():
self.tablesGeo=[table for table in self.tables if isinstance(table,VectorTable)]
tablesGeo=[ '"%s"."%s"'%(table.name,table.geomColumn) for table in self.tablesGeo]
self.ui.table_target.insertItems(1,tablesGeo)
self.idxTables=[table for table in self.tablesGeo if table.hasSpatialIndex()]
idxTables=['"%s"."%s"'%(table.name,table.geomColumn) for table in self.idxTables]
self.ui.table_idx.insertItems(1,idxTables)
QObject.connect(self.ui.usertree,SIGNAL("clicked(bool)"),self.use_rtree)
else:
self.ui.toolBox.setItemEnabled(2, False )
def update_table_list( self ):
self.tables = []
add_sys_tables = self.ui.checkBox.isChecked()
schemas = self.db.schemas()
if schemas is None:
self.tables = self.db.tables( None, add_sys_tables )
else:
for schema in schemas:
self.tables += self.db.tables(schema, add_sys_tables)
def show_tables(self):
self.update_table_list()
self.ui.tables.clear()
self.ui.tables.insertItems(0,["Tables"])
self.ui.tables.insertItems(1,[t.name for t in self.tables])
def add_aggregate(self):
if self.ui.aggregates.currentIndex() <= 0:
return
ag=self.ui.aggregates.currentText()
insertWithSelection( self.ui.col, ag )
self.ui.aggregates.setCurrentIndex(0)
def add_functions(self):
if self.ui.functions.currentIndex() <= 0:
return
ag=self.ui.functions.currentText()
insertWithSelectionOn( self, self.evt.focus, ag )
self.ui.functions.setCurrentIndex(0)
def add_stringfct(self):
if self.ui.stringFct.currentIndex() <= 0:
return
ag=self.ui.stringfct.currentText()
insertWithSelectionOn( self, self.evt.focus, ag )
self.ui.stringfct.setCurrentIndex(0)
def add_math(self):
if self.ui.math.currentIndex() <= 0:
return
ag=self.ui.math.currentText()
insertWithSelectionOn( self, self.evt.focus, ag )
self.ui.math.setCurrentIndex(0)
def add_operators(self):
if self.ui.operators.currentIndex() <= 0:
return
ag=self.ui.operators.currentText()
if self.evt.focus == "where": # in where section
self.ui.where.insertPlainText(ag)
else:
self.ui.col.insertPlainText(ag)
self.ui.operators.setCurrentIndex(0)
def add_tables(self):
if self.ui.tables.currentIndex() <= 0:
return
ag=self.ui.tables.currentText()
#Retrieve Table Object from txt
tableObj=[table for table in self.tables if table.name.upper()==ag.upper()]
if len(tableObj)!=1:
return #No object with this name
self.table=tableObj[0]
if (ag in self.coltables): #table already use
reponse=QMessageBox.question(self, "Table already used","Do you want to add table %s again ?"%ag, QMessageBox.Yes | QMessageBox.No)
if reponse==QMessageBox.No:
return
ag = self.table.quotedName()
txt=self.ui.tab.text()
if (txt is None) or (txt in (""," ")):
self.ui.tab.setText('%s'%ag)
else:
self.ui.tab.setText('%s, %s'%(txt,ag))
self.ui.tables.setCurrentIndex(0)
def add_columns(self):
if self.ui.columns.currentIndex() <= 0:
return
ag=self.ui.columns.currentText()
if self.evt.focus == "where": # in where section
if ag in self.col_where: # column already called in where section
reponse=QMessageBox.question(self, "Column already used in WHERE clause","Do you want to add column %s again ?"%ag, QMessageBox.Yes | QMessageBox.No)
if reponse==QMessageBox.No:
self.ui.columns.setCurrentIndex(0)
return
self.ui.where.insertPlainText(ag)
self.col_where.append(ag)
elif self.evt.focus == "col" :
if ag in self.col_col: # column already called in col section
reponse=QMessageBox.question(self, "Column already used in COLUMNS section","Do you want to add column %s again ?"%ag, QMessageBox.Yes | QMessageBox.No)
if reponse==QMessageBox.No:
self.ui.columns.setCurrentIndex(0)
return
if len(self.ui.col.toPlainText().strip()) > 0:
self.ui.col.insertPlainText(",\n" + ag)
else:
self.ui.col.insertPlainText(ag)
self.col_col.append(ag)
elif self.evt.focus == "group":
if len(self.ui.group.toPlainText().strip()) > 0:
self.ui.group.insertPlainText( ", " + ag )
else:
self.ui.group.insertPlainText( ag )
elif self.evt.focus == "order":
if len(self.ui.order.toPlainText().strip()) > 0:
self.ui.order.insertPlainText( ", " + ag )
else:
self.ui.order.insertPlainText( ag )
self.ui.columns.setCurrentIndex(0)
def list_cols(self):
table=self.table
if (table is None):
return
if (table.name in self.coltables):
return
columns=['"%s"."%s"'%(table.name,col.name) for col in table.fields()]
#add special '*' column:
columns=['"%s".*'%table.name]+columns
self.coltables.append(table.name) #table columns have been listed
# first and second col combobox
end=self.ui.columns.count()
self.ui.columns.insertItems(end,columns)
self.ui.columns_2.insertItems(end,columns)
end=self.ui.columns.count()
self.ui.columns.insertSeparator(end)
self.ui.columns_2.insertSeparator(end)
def list_values(self):
if self.ui.columns_2.currentIndex() <= 0:
return
item=self.ui.columns_2.currentText()
#recover column and table:
column=item.split(".") # "table".'column'
table=column[0]
if column[1]=='*':
return
table = table[1:-1]
qtable = [t for t in self.tables if t.name.lower() == table.lower()][0].quotedName()
if self.ui.extract.isChecked():
limit = 10
else:
limit = None
model = self.db.columnUniqueValuesModel( item, qtable, limit )
self.ui.values.setModel(model)
def query_item(self, index):
queryWord = index.data()
queryWord=' "%s"' %queryWord
if queryWord != '':
self.ui.where.insertPlainText(queryWord)
self.ui.where.setFocus()
def use_rtree(self):
idx=self.ui.table_idx.currentText()
if idx in (None,""," ","Table (with Spatial Index)"):
return
try:
tab_idx=idx.split(".")[0][1:-1] #remove "
col_idx=idx.split(".")[1][1:-1] #remove '
except:
pop_up_error("All fields are necessary",self)
tgt=self.ui.table_target.currentText()
if tgt in (None,""," ","Table (Target)"):
return
tgt_tab = tgt.split('.')[0][1:-1]
tgt_col = tgt.split('.')[1][1:-1]
sql=""
if self.ui.where.toPlainText() not in (None,""," "):
sql+="\nAND"
sql+=self.db.spatialIndexClause( tab_idx, col_idx, tgt_tab, tgt_col )
self.ui.where.insertPlainText(sql)
def reset(self):
#reset lists:
self.ui.values.setModel(None)
self.ui.columns_2.clear()
self.ui.columns.insertItems(0,["Columns"])
self.ui.columns_2.insertItems(0,["Columns"])
self.coltables=[]
self.col_col=[]
self.col_where=[]
def validate(self):
query_col=unicode(self.ui.col.toPlainText())
query_table=unicode(self.ui.tab.text())
query_where=unicode(self.ui.where.toPlainText())
query_group=unicode(self.ui.group.toPlainText())
query_order=unicode(self.ui.order.toPlainText())
query=""
if query_col.strip()!='':
query+="SELECT %s \nFROM %s"%(query_col,query_table)
if query_where.strip()!='':
query+="\nWHERE %s"%query_where
if query_group.strip()!='':
query+="\nGROUP BY %s"%query_group
if query_order.strip()!='':
query+="\nORDER BY %s"%query_order
if query == '':
return
self.query = query
saveParameter = {}
saveParameter["coltables"]=self.coltables
saveParameter["col_col"]=self.col_col
saveParameter["col_where"]=self.col_where
saveParameter["col"]=query_col
saveParameter["tab"]=query_table
saveParameter["where"]=query_where
saveParameter["group"]=query_group
saveParameter["order"]=query_order
QueryBuilderDlg.saveParameter = saveParameter
def restoreLastQuery(self):
self.update_table_list()
saveParameter = QueryBuilderDlg.saveParameter
self.coltables=saveParameter["coltables"]
self.col_col=saveParameter["col_col"]
self.col_where=saveParameter["col_where"]
self.ui.col.insertPlainText(saveParameter["col"])
self.ui.tab.setText(saveParameter["tab"])
self.ui.where.insertPlainText(saveParameter["where"])
self.ui.order.setPlainText(saveParameter["order"])
self.ui.group.setPlainText(saveParameter["group"])
#list previous colist:
for tablename in self.coltables:
#Retrieve table object from table name:
table=[table for table in self.tables if table.name.upper()==tablename.upper()]
if len(table)!=1:
break
table=table[0]
columns=['"%s"."%s"'%(table.name,col.name) for col in table.fields()]
# first and second col combobox
end=self.ui.columns.count()
self.ui.columns.insertItems(end,columns)
self.ui.columns_2.insertItems(end,columns)
end=self.ui.columns.count()
self.ui.columns.insertSeparator(end)
self.ui.columns_2.insertSeparator(end)