--- a/ProjectDjango/DjangoMigrationsListDialog.py Sat Dec 17 19:07:02 2016 +0100 +++ b/ProjectDjango/DjangoMigrationsListDialog.py Mon Dec 19 18:44:07 2016 +0100 @@ -13,9 +13,10 @@ except NameError: pass -from PyQt5.QtCore import pyqtSlot, Qt, QProcess, QTimer +from PyQt5.QtCore import pyqtSlot, Qt, QProcess, QTimer, QPoint from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QAbstractButton, \ - QHeaderView, QTreeWidgetItem + QHeaderView, QTreeWidgetItem, QMenu, QAbstractItemView, QInputDialog, \ + QLineEdit from E5Gui import E5MessageBox @@ -31,12 +32,14 @@ MigrationsListMode = "L" MigrationsPlanMode = "P" - def __init__(self, mode, parent=None): + def __init__(self, mode, django, parent=None): """ Constructor @param parent reference to the parent widget @type QWidget + @param django reference to the Django project object + @type Project """ super(DjangoMigrationsListDialog, self).__init__(parent) self.setupUi(self) @@ -46,7 +49,15 @@ self.ioEncoding = Preferences.getSystem("IOEncoding") - self.proc = None + self.proc = QProcess() + self.proc.finished.connect(self.__procFinished) + self.proc.readyReadStandardOutput.connect(self.__readStdout) + self.proc.readyReadStandardError.connect(self.__readStderr) + + self.__pyExe = "" + self.__sitePath = "" + + self.__django = django self.__mode = mode if self.__mode == DjangoMigrationsListDialog.MigrationsListMode: @@ -54,12 +65,22 @@ self.migrationsList.setHeaderLabels([ self.tr("Name"), ]) + self.migrationsList.setSelectionMode( + QAbstractItemView.ExtendedSelection) else: self.setWindowTitle(self.tr("Migrations Plan")) self.migrationsList.setHeaderLabels([ self.tr("Migration"), self.tr("Dependencies"), ]) + self.migrationsList.setSelectionMode( + QAbstractItemView.SingleSelection) + + self.refreshButton = self.buttonBox.addButton( + self.tr("&Refresh"), QDialogButtonBox.ActionRole) + self.refreshButton.setToolTip( + self.tr("Press to refresh the list")) + self.refreshButton.setEnabled(False) @pyqtSlot(QAbstractButton) def on_buttonBox_clicked(self, button): @@ -73,6 +94,8 @@ self.close() elif button == self.buttonBox.button(QDialogButtonBox.Cancel): self.__finish() + elif button == self.refreshButton: + self.on_refreshButton_clicked() def __finish(self): """ @@ -89,7 +112,7 @@ self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False) self.buttonBox.button(QDialogButtonBox.Close).setDefault(True) - self.proc = None + self.refreshButton.setEnabled(True) self.__resizeColumns() @@ -121,12 +144,11 @@ @type str @return flag indicating a successful start of the process (boolean) """ - self.errorGroup.hide() + self.__pyExe = pythonExecutable + self.__sitePath = sitePath - self.proc = QProcess() - self.proc.finished.connect(self.__procFinished) - self.proc.readyReadStandardOutput.connect(self.__readStdout) - self.proc.readyReadStandardError.connect(self.__readStderr) + self.errorGroup.hide() + self.migrationsList.clear() self.__lastTopItem = None @@ -185,14 +207,17 @@ else: # migration name line = line.strip() - applied = line[:3] - name = line[3:].strip() - if self.__lastTopItem: - itm = QTreeWidgetItem(self.__lastTopItem, [name]) - else: - itm = QTreeWidgetItem(self.migrationsList, [name]) - if applied[1] != " ": - itm.setCheckState(0, Qt.Checked) + if line.startswith("["): + applied = line[:3] + name = line[3:].strip() + if self.__lastTopItem: + itm = QTreeWidgetItem(self.__lastTopItem, [name]) + else: + itm = QTreeWidgetItem(self.migrationsList, [name]) + if applied[1] == " ": + itm.setCheckState(0, Qt.Unchecked) + else: + itm.setCheckState(0, Qt.Checked) def __createPlanItem(self, line): """ @@ -233,3 +258,120 @@ 'replace') self.errors.insertPlainText(s) self.errors.ensureCursorVisible() + + @pyqtSlot() + def on_refreshButton_clicked(self): + """ + Private slot to refresh the log. + """ + self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) + self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(True) + self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) + + self.refreshButton.setEnabled(False) + + self.start(self.__pyExe, self.__sitePath) + + @pyqtSlot(QPoint) + def on_migrationsList_customContextMenuRequested(self, pos): + """ + Private slot to show the context menu. + + @param pos position the context menu was requested at + @type QPoint + """ + menu = QMenu(self.migrationsList) + menu.addAction(self.tr("Apply All Migrations"), + self.__applyAllMigrations) + + if self.__mode == DjangoMigrationsListDialog.MigrationsListMode: + selItems = self.migrationsList.selectedItems() + if len(selItems) > 0: + selApps = [] + for itm in selItems: + if itm.parent() is None: + selApps.append(itm) + + menu.addAction( + self.tr("Apply Selected Migrations"), + self.__applyMigration).setEnabled(len(selItems) == 1) + menu.addAction( + self.tr("Unapply Migrations"), + self.__unapplyMigration).setEnabled(len(selApps) == 1) + menu.addSeparator() + menu.addAction( + self.tr("Make Migrations"), + self.__makeMigrations).setEnabled(len(selApps) > 0) + menu.addAction( + self.tr("Make Migrations (dry-run)"), + lambda: self.__makeMigrations(dryRun=True))\ + .setEnabled(len(selApps) > 0) + else: + menu.addAction(self.tr("Apply Selected Migrations"), + self.__applyMigration) + + menu.popup(self.migrationsList.mapToGlobal(pos)) + + def __applyAllMigrations(self): + """ + Private slot to apply all migrations. + """ + self.__django.applyMigrations() + self.on_refreshButton_clicked() + + def __applyMigration(self): + """ + Private slot to apply the selected migrations. + """ + itm = self.migrationsList.selectedItems()[0] + if self.__mode == DjangoMigrationsListDialog.MigrationsListMode: + if itm.parent() is None: + # app item + app = itm.text(0) + migration = None + else: + migration = itm.text(0) + app = itm.parent().text(0) + else: + app, migration = itm.text(0).split(".", 1) + + self.__django.applyMigrations(app=app, migration=migration) + + self.on_refreshButton_clicked() + + def __unapplyMigration(self): + """ + Private slot to unapply the selected migrations. + """ + if self.__mode == DjangoMigrationsListDialog.MigrationsListMode: + itm = self.migrationsList.selectedItems()[0] + if itm.parent() is None: + # only valid for app entries + app = itm.text(0) + self.__django.applyMigrations(app=app, migration="zero") + + self.on_refreshButton_clicked() + + def __makeMigrations(self, dryRun=False): + """ + Private slot to make migrations for the selected apps. + + @param dryRun dlag indicating a dry-run + @type bool + """ + apps = [] + for itm in self.migrationsList.selectedItems(): + if itm.parent() is None: + apps.append(itm.text(0)) + + if apps: + migration, ok = QInputDialog.getText( + self, + self.tr("Make Migrations"), + self.tr("Enter a name for the migrations (leave empty to" + " use system supplied name):"), + QLineEdit.Normal) + if ok: + self.__django.makeMigrations(apps, migration, dryRun) + + self.on_refreshButton_clicked()