ProjectDjango/DjangoMigrationsListDialog.py

changeset 73
8a4ccc4f1359
parent 72
0f95786f7868
child 75
0165ce437462
equal deleted inserted replaced
72:0f95786f7868 73:8a4ccc4f1359
11 try: 11 try:
12 str = unicode # __IGNORE_WARNING__ 12 str = unicode # __IGNORE_WARNING__
13 except NameError: 13 except NameError:
14 pass 14 pass
15 15
16 from PyQt5.QtCore import pyqtSlot, Qt, QProcess, QTimer 16 from PyQt5.QtCore import pyqtSlot, Qt, QProcess, QTimer, QPoint
17 from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QAbstractButton, \ 17 from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QAbstractButton, \
18 QHeaderView, QTreeWidgetItem 18 QHeaderView, QTreeWidgetItem, QMenu, QAbstractItemView, QInputDialog, \
19 QLineEdit
19 20
20 from E5Gui import E5MessageBox 21 from E5Gui import E5MessageBox
21 22
22 from .Ui_DjangoMigrationsListDialog import Ui_DjangoMigrationsListDialog 23 from .Ui_DjangoMigrationsListDialog import Ui_DjangoMigrationsListDialog
23 24
29 Class implementing a dialog show a list of all available migrations. 30 Class implementing a dialog show a list of all available migrations.
30 """ 31 """
31 MigrationsListMode = "L" 32 MigrationsListMode = "L"
32 MigrationsPlanMode = "P" 33 MigrationsPlanMode = "P"
33 34
34 def __init__(self, mode, parent=None): 35 def __init__(self, mode, django, parent=None):
35 """ 36 """
36 Constructor 37 Constructor
37 38
38 @param parent reference to the parent widget 39 @param parent reference to the parent widget
39 @type QWidget 40 @type QWidget
41 @param django reference to the Django project object
42 @type Project
40 """ 43 """
41 super(DjangoMigrationsListDialog, self).__init__(parent) 44 super(DjangoMigrationsListDialog, self).__init__(parent)
42 self.setupUi(self) 45 self.setupUi(self)
43 46
44 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) 47 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False)
45 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) 48 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True)
46 49
47 self.ioEncoding = Preferences.getSystem("IOEncoding") 50 self.ioEncoding = Preferences.getSystem("IOEncoding")
48 51
49 self.proc = None 52 self.proc = QProcess()
53 self.proc.finished.connect(self.__procFinished)
54 self.proc.readyReadStandardOutput.connect(self.__readStdout)
55 self.proc.readyReadStandardError.connect(self.__readStderr)
56
57 self.__pyExe = ""
58 self.__sitePath = ""
59
60 self.__django = django
50 61
51 self.__mode = mode 62 self.__mode = mode
52 if self.__mode == DjangoMigrationsListDialog.MigrationsListMode: 63 if self.__mode == DjangoMigrationsListDialog.MigrationsListMode:
53 self.setWindowTitle(self.tr("Available Migrations")) 64 self.setWindowTitle(self.tr("Available Migrations"))
54 self.migrationsList.setHeaderLabels([ 65 self.migrationsList.setHeaderLabels([
55 self.tr("Name"), 66 self.tr("Name"),
56 ]) 67 ])
68 self.migrationsList.setSelectionMode(
69 QAbstractItemView.ExtendedSelection)
57 else: 70 else:
58 self.setWindowTitle(self.tr("Migrations Plan")) 71 self.setWindowTitle(self.tr("Migrations Plan"))
59 self.migrationsList.setHeaderLabels([ 72 self.migrationsList.setHeaderLabels([
60 self.tr("Migration"), 73 self.tr("Migration"),
61 self.tr("Dependencies"), 74 self.tr("Dependencies"),
62 ]) 75 ])
76 self.migrationsList.setSelectionMode(
77 QAbstractItemView.SingleSelection)
78
79 self.refreshButton = self.buttonBox.addButton(
80 self.tr("&Refresh"), QDialogButtonBox.ActionRole)
81 self.refreshButton.setToolTip(
82 self.tr("Press to refresh the list"))
83 self.refreshButton.setEnabled(False)
63 84
64 @pyqtSlot(QAbstractButton) 85 @pyqtSlot(QAbstractButton)
65 def on_buttonBox_clicked(self, button): 86 def on_buttonBox_clicked(self, button):
66 """ 87 """
67 Private slot called by a button of the button box clicked. 88 Private slot called by a button of the button box clicked.
71 """ 92 """
72 if button == self.buttonBox.button(QDialogButtonBox.Close): 93 if button == self.buttonBox.button(QDialogButtonBox.Close):
73 self.close() 94 self.close()
74 elif button == self.buttonBox.button(QDialogButtonBox.Cancel): 95 elif button == self.buttonBox.button(QDialogButtonBox.Cancel):
75 self.__finish() 96 self.__finish()
97 elif button == self.refreshButton:
98 self.on_refreshButton_clicked()
76 99
77 def __finish(self): 100 def __finish(self):
78 """ 101 """
79 Private slot called when the process finished or the user pressed the 102 Private slot called when the process finished or the user pressed the
80 button. 103 button.
87 110
88 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True) 111 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True)
89 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False) 112 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False)
90 self.buttonBox.button(QDialogButtonBox.Close).setDefault(True) 113 self.buttonBox.button(QDialogButtonBox.Close).setDefault(True)
91 114
92 self.proc = None 115 self.refreshButton.setEnabled(True)
93 116
94 self.__resizeColumns() 117 self.__resizeColumns()
95 118
96 def __procFinished(self, exitCode, exitStatus): 119 def __procFinished(self, exitCode, exitStatus):
97 """ 120 """
119 @type str 142 @type str
120 @param sitePath path of the site 143 @param sitePath path of the site
121 @type str 144 @type str
122 @return flag indicating a successful start of the process (boolean) 145 @return flag indicating a successful start of the process (boolean)
123 """ 146 """
147 self.__pyExe = pythonExecutable
148 self.__sitePath = sitePath
149
124 self.errorGroup.hide() 150 self.errorGroup.hide()
125 151 self.migrationsList.clear()
126 self.proc = QProcess()
127 self.proc.finished.connect(self.__procFinished)
128 self.proc.readyReadStandardOutput.connect(self.__readStdout)
129 self.proc.readyReadStandardError.connect(self.__readStderr)
130 152
131 self.__lastTopItem = None 153 self.__lastTopItem = None
132 154
133 if sitePath: 155 if sitePath:
134 self.proc.setWorkingDirectory(sitePath) 156 self.proc.setWorkingDirectory(sitePath)
183 self.migrationsList, [line.strip()]) 205 self.migrationsList, [line.strip()])
184 self.__lastTopItem.setExpanded(True) 206 self.__lastTopItem.setExpanded(True)
185 else: 207 else:
186 # migration name 208 # migration name
187 line = line.strip() 209 line = line.strip()
188 applied = line[:3] 210 if line.startswith("["):
189 name = line[3:].strip() 211 applied = line[:3]
190 if self.__lastTopItem: 212 name = line[3:].strip()
191 itm = QTreeWidgetItem(self.__lastTopItem, [name]) 213 if self.__lastTopItem:
192 else: 214 itm = QTreeWidgetItem(self.__lastTopItem, [name])
193 itm = QTreeWidgetItem(self.migrationsList, [name]) 215 else:
194 if applied[1] != " ": 216 itm = QTreeWidgetItem(self.migrationsList, [name])
195 itm.setCheckState(0, Qt.Checked) 217 if applied[1] == " ":
218 itm.setCheckState(0, Qt.Unchecked)
219 else:
220 itm.setCheckState(0, Qt.Checked)
196 221
197 def __createPlanItem(self, line): 222 def __createPlanItem(self, line):
198 """ 223 """
199 Private method to create an item for plan mode. 224 Private method to create an item for plan mode.
200 225
231 self.errorGroup.show() 256 self.errorGroup.show()
232 s = str(self.proc.readAllStandardError(), self.ioEncoding, 257 s = str(self.proc.readAllStandardError(), self.ioEncoding,
233 'replace') 258 'replace')
234 self.errors.insertPlainText(s) 259 self.errors.insertPlainText(s)
235 self.errors.ensureCursorVisible() 260 self.errors.ensureCursorVisible()
261
262 @pyqtSlot()
263 def on_refreshButton_clicked(self):
264 """
265 Private slot to refresh the log.
266 """
267 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False)
268 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(True)
269 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True)
270
271 self.refreshButton.setEnabled(False)
272
273 self.start(self.__pyExe, self.__sitePath)
274
275 @pyqtSlot(QPoint)
276 def on_migrationsList_customContextMenuRequested(self, pos):
277 """
278 Private slot to show the context menu.
279
280 @param pos position the context menu was requested at
281 @type QPoint
282 """
283 menu = QMenu(self.migrationsList)
284 menu.addAction(self.tr("Apply All Migrations"),
285 self.__applyAllMigrations)
286
287 if self.__mode == DjangoMigrationsListDialog.MigrationsListMode:
288 selItems = self.migrationsList.selectedItems()
289 if len(selItems) > 0:
290 selApps = []
291 for itm in selItems:
292 if itm.parent() is None:
293 selApps.append(itm)
294
295 menu.addAction(
296 self.tr("Apply Selected Migrations"),
297 self.__applyMigration).setEnabled(len(selItems) == 1)
298 menu.addAction(
299 self.tr("Unapply Migrations"),
300 self.__unapplyMigration).setEnabled(len(selApps) == 1)
301 menu.addSeparator()
302 menu.addAction(
303 self.tr("Make Migrations"),
304 self.__makeMigrations).setEnabled(len(selApps) > 0)
305 menu.addAction(
306 self.tr("Make Migrations (dry-run)"),
307 lambda: self.__makeMigrations(dryRun=True))\
308 .setEnabled(len(selApps) > 0)
309 else:
310 menu.addAction(self.tr("Apply Selected Migrations"),
311 self.__applyMigration)
312
313 menu.popup(self.migrationsList.mapToGlobal(pos))
314
315 def __applyAllMigrations(self):
316 """
317 Private slot to apply all migrations.
318 """
319 self.__django.applyMigrations()
320 self.on_refreshButton_clicked()
321
322 def __applyMigration(self):
323 """
324 Private slot to apply the selected migrations.
325 """
326 itm = self.migrationsList.selectedItems()[0]
327 if self.__mode == DjangoMigrationsListDialog.MigrationsListMode:
328 if itm.parent() is None:
329 # app item
330 app = itm.text(0)
331 migration = None
332 else:
333 migration = itm.text(0)
334 app = itm.parent().text(0)
335 else:
336 app, migration = itm.text(0).split(".", 1)
337
338 self.__django.applyMigrations(app=app, migration=migration)
339
340 self.on_refreshButton_clicked()
341
342 def __unapplyMigration(self):
343 """
344 Private slot to unapply the selected migrations.
345 """
346 if self.__mode == DjangoMigrationsListDialog.MigrationsListMode:
347 itm = self.migrationsList.selectedItems()[0]
348 if itm.parent() is None:
349 # only valid for app entries
350 app = itm.text(0)
351 self.__django.applyMigrations(app=app, migration="zero")
352
353 self.on_refreshButton_clicked()
354
355 def __makeMigrations(self, dryRun=False):
356 """
357 Private slot to make migrations for the selected apps.
358
359 @param dryRun dlag indicating a dry-run
360 @type bool
361 """
362 apps = []
363 for itm in self.migrationsList.selectedItems():
364 if itm.parent() is None:
365 apps.append(itm.text(0))
366
367 if apps:
368 migration, ok = QInputDialog.getText(
369 self,
370 self.tr("Make Migrations"),
371 self.tr("Enter a name for the migrations (leave empty to"
372 " use system supplied name):"),
373 QLineEdit.Normal)
374 if ok:
375 self.__django.makeMigrations(apps, migration, dryRun)
376
377 self.on_refreshButton_clicked()

eric ide

mercurial