ProjectDjango/DjangoMigrationsListDialog.py

changeset 72
0f95786f7868
child 73
8a4ccc4f1359
equal deleted inserted replaced
71:a250660cc153 72:0f95786f7868
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a dialog show a list of all available migrations.
8 """
9
10 from __future__ import unicode_literals
11 try:
12 str = unicode # __IGNORE_WARNING__
13 except NameError:
14 pass
15
16 from PyQt5.QtCore import pyqtSlot, Qt, QProcess, QTimer
17 from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QAbstractButton, \
18 QHeaderView, QTreeWidgetItem
19
20 from E5Gui import E5MessageBox
21
22 from .Ui_DjangoMigrationsListDialog import Ui_DjangoMigrationsListDialog
23
24 import Preferences
25
26
27 class DjangoMigrationsListDialog(QDialog, Ui_DjangoMigrationsListDialog):
28 """
29 Class implementing a dialog show a list of all available migrations.
30 """
31 MigrationsListMode = "L"
32 MigrationsPlanMode = "P"
33
34 def __init__(self, mode, parent=None):
35 """
36 Constructor
37
38 @param parent reference to the parent widget
39 @type QWidget
40 """
41 super(DjangoMigrationsListDialog, self).__init__(parent)
42 self.setupUi(self)
43
44 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False)
45 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True)
46
47 self.ioEncoding = Preferences.getSystem("IOEncoding")
48
49 self.proc = None
50
51 self.__mode = mode
52 if self.__mode == DjangoMigrationsListDialog.MigrationsListMode:
53 self.setWindowTitle(self.tr("Available Migrations"))
54 self.migrationsList.setHeaderLabels([
55 self.tr("Name"),
56 ])
57 else:
58 self.setWindowTitle(self.tr("Migrations Plan"))
59 self.migrationsList.setHeaderLabels([
60 self.tr("Migration"),
61 self.tr("Dependencies"),
62 ])
63
64 @pyqtSlot(QAbstractButton)
65 def on_buttonBox_clicked(self, button):
66 """
67 Private slot called by a button of the button box clicked.
68
69 @param button button that was clicked
70 @type QAbstractButton
71 """
72 if button == self.buttonBox.button(QDialogButtonBox.Close):
73 self.close()
74 elif button == self.buttonBox.button(QDialogButtonBox.Cancel):
75 self.__finish()
76
77 def __finish(self):
78 """
79 Private slot called when the process finished or the user pressed the
80 button.
81 """
82 if self.proc is not None and \
83 self.proc.state() != QProcess.NotRunning:
84 self.proc.terminate()
85 QTimer.singleShot(2000, self.proc.kill)
86 self.proc.waitForFinished(3000)
87
88 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True)
89 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False)
90 self.buttonBox.button(QDialogButtonBox.Close).setDefault(True)
91
92 self.proc = None
93
94 self.__resizeColumns()
95
96 def __procFinished(self, exitCode, exitStatus):
97 """
98 Private slot connected to the finished signal.
99
100 @param exitCode exit code of the process (integer)
101 @param exitStatus exit status of the process (QProcess.ExitStatus)
102 """
103 self.__finish()
104
105 def __resizeColumns(self):
106 """
107 Private method to resize the list columns.
108 """
109 self.migrationsList.header().resizeSections(
110 QHeaderView.ResizeToContents)
111 if self.__mode == DjangoMigrationsListDialog.MigrationsListMode:
112 self.migrationsList.header().setStretchLastSection(True)
113
114 def start(self, pythonExecutable, sitePath):
115 """
116 Public slot used to start the process.
117
118 @param pythonExecutable Python executable to be used
119 @type str
120 @param sitePath path of the site
121 @type str
122 @return flag indicating a successful start of the process (boolean)
123 """
124 self.errorGroup.hide()
125
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
131 self.__lastTopItem = None
132
133 if sitePath:
134 self.proc.setWorkingDirectory(sitePath)
135
136 args = []
137 args.append("manage.py")
138 args.append("showmigrations")
139 if self.__mode == DjangoMigrationsListDialog.MigrationsListMode:
140 args.append("--list")
141 else:
142 args.append("--plan")
143 args.append("--verbosity")
144 args.append("2")
145
146 self.proc.start(pythonExecutable, args)
147 procStarted = self.proc.waitForStarted()
148 if not procStarted:
149 self.buttonBox.setFocus()
150 E5MessageBox.critical(
151 self,
152 self.tr('Process Generation Error'),
153 self.tr(
154 'The process {0} could not be started. '
155 'Ensure, that it is in the search path.'
156 ).format(pythonExecutable))
157 return procStarted
158
159 def __readStdout(self):
160 """
161 Private slot to handle the readyReadStdout signal.
162
163 It reads the output of the process, formats it and inserts it into
164 the contents pane.
165 """
166 while self.proc.canReadLine():
167 s = str(self.proc.readLine(), self.ioEncoding, 'replace').rstrip()
168 if self.__mode == DjangoMigrationsListDialog.MigrationsListMode:
169 self.__createListItem(s)
170 else:
171 self.__createPlanItem(s)
172
173 def __createListItem(self, line):
174 """
175 Private method to create an item for list mode.
176
177 @param line line of text
178 @type str
179 """
180 if not line.startswith(" "):
181 # application name
182 self.__lastTopItem = QTreeWidgetItem(
183 self.migrationsList, [line.strip()])
184 self.__lastTopItem.setExpanded(True)
185 else:
186 # migration name
187 line = line.strip()
188 applied = line[:3]
189 name = line[3:].strip()
190 if self.__lastTopItem:
191 itm = QTreeWidgetItem(self.__lastTopItem, [name])
192 else:
193 itm = QTreeWidgetItem(self.migrationsList, [name])
194 if applied[1] != " ":
195 itm.setCheckState(0, Qt.Checked)
196
197 def __createPlanItem(self, line):
198 """
199 Private method to create an item for plan mode.
200
201 @param line line of text
202 @type str
203 """
204 line = line.strip()
205 applied = line[:3]
206 parts = line[3:].strip().split(None, 2)
207 if len(parts) == 3:
208 dependencies = "\n".join([
209 d.strip() for d in parts[2].strip()[1:-1].split(",")
210 ])
211 itm = QTreeWidgetItem(self.migrationsList, [
212 parts[0].strip(),
213 dependencies,
214 ])
215 else:
216 itm = QTreeWidgetItem(self.migrationsList, [
217 parts[0].strip(),
218 "",
219 ])
220 if applied[1] != " ":
221 itm.setCheckState(0, Qt.Checked)
222
223 def __readStderr(self):
224 """
225 Private slot to handle the readyReadStderr signal.
226
227 It reads the error output of the process and inserts it into the
228 error pane.
229 """
230 if self.proc is not None:
231 self.errorGroup.show()
232 s = str(self.proc.readAllStandardError(), self.ioEncoding,
233 'replace')
234 self.errors.insertPlainText(s)
235 self.errors.ensureCursorVisible()

eric ide

mercurial