|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2009 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing the SQL Browser widget. |
|
8 """ |
|
9 |
|
10 from PyQt4.QtCore import * |
|
11 from PyQt4.QtGui import * |
|
12 from PyQt4.QtSql import QSqlDatabase, QSqlError, QSqlTableModel, QSqlQueryModel, QSqlQuery |
|
13 |
|
14 from SqlConnectionDialog import SqlConnectionDialog |
|
15 |
|
16 from Ui_SqlBrowserWidget import Ui_SqlBrowserWidget |
|
17 |
|
18 class SqlBrowserWidget(QWidget, Ui_SqlBrowserWidget): |
|
19 """ |
|
20 Class implementing the SQL Browser widget. |
|
21 |
|
22 @signal statusMessage(string) emitted to show a status message |
|
23 """ |
|
24 cCount = 0 |
|
25 |
|
26 def __init__(self, parent = None): |
|
27 """ |
|
28 Constructor |
|
29 |
|
30 @param parent reference to the parent widget (QWidget) |
|
31 """ |
|
32 QWidget.__init__(self, parent) |
|
33 self.setupUi(self) |
|
34 |
|
35 self.table.addAction(self.insertRowAction) |
|
36 self.table.addAction(self.deleteRowAction) |
|
37 |
|
38 if len(QSqlDatabase.drivers()) == 0: |
|
39 QMessageBox.information(None, |
|
40 self.trUtf8("No database drivers found"), |
|
41 self.trUtf8("""This tool requires at least one Qt database driver. """ |
|
42 """Please check the Qt documentation how to build the """ |
|
43 """Qt SQL plugins.""")) |
|
44 |
|
45 self.connect(self.connections, SIGNAL("tableActivated(QString)"), |
|
46 self.on_connections_tableActivated) |
|
47 self.connect(self.connections, SIGNAL("schemaRequested(QString)"), |
|
48 self.on_connections_schemaRequested) |
|
49 self.connect(self.connections, SIGNAL("cleared()"), |
|
50 self.on_connections_cleared) |
|
51 |
|
52 self.emit(SIGNAL("statusMessage(QString)"), self.trUtf8("Ready")) |
|
53 |
|
54 @pyqtSlot() |
|
55 def on_clearButton_clicked(self): |
|
56 """ |
|
57 Private slot to clear the SQL entry widget. |
|
58 """ |
|
59 self.sqlEdit.clear() |
|
60 self.sqlEdit.setFocus() |
|
61 |
|
62 @pyqtSlot() |
|
63 def on_executeButton_clicked(self): |
|
64 """ |
|
65 Private slot to execute the entered SQL query. |
|
66 """ |
|
67 self.executeQuery() |
|
68 self.sqlEdit.setFocus() |
|
69 |
|
70 @pyqtSlot() |
|
71 def on_insertRowAction_triggered(self): |
|
72 """ |
|
73 Private slot handling the action to insert a new row. |
|
74 """ |
|
75 self.__insertRow() |
|
76 |
|
77 @pyqtSlot() |
|
78 def on_deleteRowAction_triggered(self): |
|
79 """ |
|
80 Private slot handling the action to delete a row. |
|
81 """ |
|
82 self.__deleteRow() |
|
83 |
|
84 @pyqtSlot(str) |
|
85 def on_connections_tableActivated(self, table): |
|
86 """ |
|
87 Private slot to show the contents of a table. |
|
88 |
|
89 @param table name of the table for which to show the contents (string) |
|
90 """ |
|
91 self.showTable(table) |
|
92 |
|
93 @pyqtSlot(str) |
|
94 def on_connections_schemaRequested(self, table): |
|
95 """ |
|
96 Private slot to show the schema of a table. |
|
97 |
|
98 @param table name of the table for which to show the schema (string) |
|
99 """ |
|
100 self.showSchema(table) |
|
101 |
|
102 @pyqtSlot() |
|
103 def on_connections_cleared(self): |
|
104 """ |
|
105 Private slot to clear the table. |
|
106 """ |
|
107 model = QStandardItemModel(self.table) |
|
108 self.table.setModel(model) |
|
109 self.table.setEditTriggers(QAbstractItemView.NoEditTriggers) |
|
110 |
|
111 self.updateActions() |
|
112 |
|
113 def addConnection(self, driver, dbName, user, password, host, port): |
|
114 """ |
|
115 Public method to add a database connection. |
|
116 |
|
117 @param driver name of the Qt database driver (string) |
|
118 @param dbName name of the database (string) |
|
119 @param user user name (string) |
|
120 @param password password (string) |
|
121 @param host host name (string) |
|
122 @param port port number (integer) |
|
123 """ |
|
124 err = QSqlError() |
|
125 |
|
126 self.__class__.cCount += 1 |
|
127 db = QSqlDatabase.addDatabase(driver.upper(), |
|
128 "Browser%d" % self.__class__.cCount) |
|
129 db.setDatabaseName(dbName) |
|
130 db.setHostName(host) |
|
131 db.setPort(port) |
|
132 if not db.open(user, password): |
|
133 err = db.lastError() |
|
134 db = QSqlDatabase() |
|
135 QSqlDatabase.removeDatabase("Browser%d" % self.__class__.cCount) |
|
136 |
|
137 self.connections.refresh() |
|
138 |
|
139 return err |
|
140 |
|
141 def addConnectionByDialog(self): |
|
142 """ |
|
143 Public slot to add a database connection via an input dialog. |
|
144 """ |
|
145 dlg = SqlConnectionDialog(self) |
|
146 if dlg.exec_() == QDialog.Accepted: |
|
147 driver, dbName, user, password, host, port = dlg.getData() |
|
148 err = self.addConnection(driver, dbName, user, password, host, port) |
|
149 |
|
150 if err.type() != QSqlError.NoError: |
|
151 QMessageBox.warning(self, |
|
152 self.trUtf8("Unable to open database"), |
|
153 self.trUtf8("""An error occurred while opening the connection.""")) |
|
154 |
|
155 def showTable(self, table): |
|
156 """ |
|
157 Public slot to show the contents of a table. |
|
158 |
|
159 @param table name of the table to be shown (string) |
|
160 """ |
|
161 model = QSqlTableModel(self.table, self.connections.currentDatabase()) |
|
162 model.setEditStrategy(QSqlTableModel.OnRowChange) |
|
163 model.setTable(table) |
|
164 model.select() |
|
165 if model.lastError().type() != QSqlError.NoError: |
|
166 self.emit(SIGNAL("statusMessage(QString)"), model.lastError().text()) |
|
167 self.table.setModel(model) |
|
168 self.table.setEditTriggers( |
|
169 QAbstractItemView.DoubleClicked | QAbstractItemView.EditKeyPressed) |
|
170 |
|
171 self.table.resizeColumnsToContents() |
|
172 |
|
173 self.connect(self.table.selectionModel(), |
|
174 SIGNAL("currentRowChanged(QModelIndex, QModelIndex)"), |
|
175 self.updateActions) |
|
176 |
|
177 self.updateActions() |
|
178 |
|
179 def showSchema(self, table): |
|
180 """ |
|
181 Public slot to show the schema of a table. |
|
182 |
|
183 @param table name of the table to be shown (string) |
|
184 """ |
|
185 rec = self.connections.currentDatabase().record(table) |
|
186 model = QStandardItemModel(self.table) |
|
187 |
|
188 model.insertRows(0, rec.count()) |
|
189 model.insertColumns(0, 7) |
|
190 |
|
191 model.setHeaderData(0, Qt.Horizontal, QVariant("Fieldname")) |
|
192 model.setHeaderData(1, Qt.Horizontal, QVariant("Type")) |
|
193 model.setHeaderData(2, Qt.Horizontal, QVariant("Length")) |
|
194 model.setHeaderData(3, Qt.Horizontal, QVariant("Precision")) |
|
195 model.setHeaderData(4, Qt.Horizontal, QVariant("Required")) |
|
196 model.setHeaderData(5, Qt.Horizontal, QVariant("Auto Value")) |
|
197 model.setHeaderData(6, Qt.Horizontal, QVariant("Default Value")) |
|
198 |
|
199 for i in range(rec.count()): |
|
200 fld = rec.field(i) |
|
201 model.setData(model.index(i, 0), QVariant(fld.name())) |
|
202 if fld.typeID() == -1: |
|
203 model.setData(model.index(i, 1), |
|
204 QVariant(QVariant.typeToName(fld.type()))) |
|
205 else: |
|
206 model.setData(model.index(i, 1), QVariant("%s1 (%s)" % (\ |
|
207 QVariant.typeToName(fld.type()), |
|
208 fld.typeID()))) |
|
209 if fld.length() < 0: |
|
210 model.setData(model.index(i, 2), QVariant("?")) |
|
211 else: |
|
212 model.setData(model.index(i, 2), QVariant(fld.length())) |
|
213 if fld.precision() < 0: |
|
214 model.setData(model.index(i, 3), QVariant("?")) |
|
215 else: |
|
216 model.setData(model.index(i, 3), QVariant(fld.precision())) |
|
217 if fld.requiredStatus() == -1: |
|
218 model.setData(model.index(i, 4), QVariant("?")) |
|
219 else: |
|
220 model.setData(model.index(i, 4), QVariant(bool(fld.requiredStatus()))) |
|
221 model.setData(model.index(i, 5), QVariant(fld.isAutoValue())) |
|
222 model.setData(model.index(i, 6), QVariant(fld.defaultValue())) |
|
223 |
|
224 self.table.setModel(model) |
|
225 self.table.setEditTriggers(QAbstractItemView.NoEditTriggers) |
|
226 |
|
227 self.table.resizeColumnsToContents() |
|
228 |
|
229 self.updateActions() |
|
230 |
|
231 def updateActions(self): |
|
232 """ |
|
233 Public slot to update the actions. |
|
234 """ |
|
235 enableIns = isinstance(self.table.model(), QSqlTableModel) |
|
236 enableDel = enableIns & self.table.currentIndex().isValid() |
|
237 |
|
238 self.insertRowAction.setEnabled(enableIns) |
|
239 self.deleteRowAction.setEnabled(enableDel) |
|
240 |
|
241 def __insertRow(self): |
|
242 """ |
|
243 Privat slot to insert a row into the database table. |
|
244 """ |
|
245 model = self.table.model() |
|
246 if not isinstance(model, QSqlTableModel): |
|
247 return |
|
248 |
|
249 insertIndex = self.table.currentIndex() |
|
250 if insertIndex.row() == -1: |
|
251 row = 0 |
|
252 else: |
|
253 row = insertIndex.row() |
|
254 model.insertRow(row) |
|
255 insertIndex = model.index(row, 0) |
|
256 self.table.setCurrentIndex(insertIndex) |
|
257 self.table.edit(insertIndex) |
|
258 |
|
259 def __deleteRow(self): |
|
260 """ |
|
261 Privat slot to delete a row from the database table. |
|
262 """ |
|
263 model = self.table.model() |
|
264 if not isinstance(model, QSqlTableModel): |
|
265 return |
|
266 |
|
267 model.setEditStrategy(QSqlTableModel.OnManualSubmit) |
|
268 |
|
269 currentSelection = self.table.selectionModel().selectedIndexes() |
|
270 for selectedIndex in currentSelection: |
|
271 if selectedIndex.column() != 0: |
|
272 continue |
|
273 model.removeRow(selectedIndex.row()) |
|
274 |
|
275 model.submitAll() |
|
276 model.setEditStrategy(QSqlTableModel.OnRowChange) |
|
277 |
|
278 self.updateActions() |
|
279 |
|
280 def executeQuery(self): |
|
281 """ |
|
282 Public slot to execute the entered query. |
|
283 """ |
|
284 model = QSqlQueryModel(self.table) |
|
285 model.setQuery( |
|
286 QSqlQuery(self.sqlEdit.toPlainText(), self.connections.currentDatabase())) |
|
287 self.table.setModel(model) |
|
288 |
|
289 if model.lastError().type() != QSqlError.NoError: |
|
290 self.emit(SIGNAL("statusMessage(QString)"), model.lastError().text()) |
|
291 elif model.query().isSelect(): |
|
292 self.emit(SIGNAL("statusMessage(QString)"), self.trUtf8("Query OK.")) |
|
293 else: |
|
294 self.emit(SIGNAL("statusMessage(QString)"), |
|
295 self.trUtf8("Query OK, number of affected rows: {0}")\ |
|
296 .format(model.query().numRowsAffected())) |
|
297 |
|
298 self.table.resizeColumnsToContents() |
|
299 |
|
300 self.updateActions() |