eric6/Plugins/VcsPlugins/vcsSubversion/SvnBlameDialog.py

changeset 6942
2602857055c5
parent 6645
ad476851d7e0
child 7192
a22eee00b052
equal deleted inserted replaced
6941:f99d60d6b59b 6942:2602857055c5
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2003 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a dialog to show the output of the svn blame command.
8 """
9
10 from __future__ import unicode_literals
11 try:
12 str = unicode
13 except NameError:
14 pass
15
16 import os
17
18 from PyQt5.QtCore import QTimer, QProcess, Qt, pyqtSlot
19 from PyQt5.QtWidgets import QHeaderView, QLineEdit, QDialog, \
20 QDialogButtonBox, QTreeWidgetItem
21
22 from E5Gui import E5MessageBox
23
24 from .Ui_SvnBlameDialog import Ui_SvnBlameDialog
25
26 import Preferences
27 from Globals import strToQByteArray
28
29
30 class SvnBlameDialog(QDialog, Ui_SvnBlameDialog):
31 """
32 Class implementing a dialog to show the output of the svn blame command.
33 """
34 def __init__(self, vcs, parent=None):
35 """
36 Constructor
37
38 @param vcs reference to the vcs object
39 @param parent parent widget (QWidget)
40 """
41 super(SvnBlameDialog, self).__init__(parent)
42 self.setupUi(self)
43 self.setWindowFlags(Qt.Window)
44
45 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False)
46 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True)
47
48 self.vcs = vcs
49
50 self.blameList.headerItem().setText(self.blameList.columnCount(), "")
51 font = Preferences.getEditorOtherFonts("MonospacedFont")
52 self.blameList.setFont(font)
53
54 self.__ioEncoding = Preferences.getSystem("IOEncoding")
55
56 self.process = QProcess()
57 self.process.finished.connect(self.__procFinished)
58 self.process.readyReadStandardOutput.connect(self.__readStdout)
59 self.process.readyReadStandardError.connect(self.__readStderr)
60
61 def closeEvent(self, e):
62 """
63 Protected slot implementing a close event handler.
64
65 @param e close event (QCloseEvent)
66 """
67 if self.process is not None and \
68 self.process.state() != QProcess.NotRunning:
69 self.process.terminate()
70 QTimer.singleShot(2000, self.process.kill)
71 self.process.waitForFinished(3000)
72
73 e.accept()
74
75 def start(self, fn):
76 """
77 Public slot to start the svn blame command.
78
79 @param fn filename to show the blame for (string)
80 """
81 self.blameList.clear()
82 self.errorGroup.hide()
83 self.intercept = False
84 self.activateWindow()
85 self.lineno = 1
86
87 dname, fname = self.vcs.splitPath(fn)
88
89 self.process.kill()
90
91 args = []
92 args.append('blame')
93 self.vcs.addArguments(args, self.vcs.options['global'])
94 args.append(fname)
95
96 self.process.setWorkingDirectory(dname)
97
98 self.process.start('svn', args)
99 procStarted = self.process.waitForStarted(5000)
100 if not procStarted:
101 self.inputGroup.setEnabled(False)
102 self.inputGroup.hide()
103 E5MessageBox.critical(
104 self,
105 self.tr('Process Generation Error'),
106 self.tr(
107 'The process {0} could not be started. '
108 'Ensure, that it is in the search path.'
109 ).format('svn'))
110 else:
111 self.inputGroup.setEnabled(True)
112 self.inputGroup.show()
113
114 def __finish(self):
115 """
116 Private slot called when the process finished or the user pressed the
117 button.
118 """
119 if self.process is not None and \
120 self.process.state() != QProcess.NotRunning:
121 self.process.terminate()
122 QTimer.singleShot(2000, self.process.kill)
123 self.process.waitForFinished(3000)
124
125 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True)
126 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False)
127 self.buttonBox.button(QDialogButtonBox.Close).setDefault(True)
128 self.buttonBox.button(QDialogButtonBox.Close).setFocus(
129 Qt.OtherFocusReason)
130
131 self.inputGroup.setEnabled(False)
132 self.inputGroup.hide()
133
134 self.__resizeColumns()
135
136 def on_buttonBox_clicked(self, button):
137 """
138 Private slot called by a button of the button box clicked.
139
140 @param button button that was clicked (QAbstractButton)
141 """
142 if button == self.buttonBox.button(QDialogButtonBox.Close):
143 self.close()
144 elif button == self.buttonBox.button(QDialogButtonBox.Cancel):
145 self.__finish()
146
147 def __procFinished(self, exitCode, exitStatus):
148 """
149 Private slot connected to the finished signal.
150
151 @param exitCode exit code of the process (integer)
152 @param exitStatus exit status of the process (QProcess.ExitStatus)
153 """
154 self.__finish()
155
156 def __resizeColumns(self):
157 """
158 Private method to resize the list columns.
159 """
160 self.blameList.header().resizeSections(QHeaderView.ResizeToContents)
161
162 def __generateItem(self, revision, author, text):
163 """
164 Private method to generate a blame item in the blame list.
165
166 @param revision revision string (string)
167 @param author author of the change (string)
168 @param text line of text from the annotated file (string)
169 """
170 itm = QTreeWidgetItem(
171 self.blameList,
172 [revision, author, "{0:d}".format(self.lineno), text])
173 self.lineno += 1
174 itm.setTextAlignment(0, Qt.AlignRight)
175 itm.setTextAlignment(2, Qt.AlignRight)
176
177 def __readStdout(self):
178 """
179 Private slot to handle the readyReadStdout signal.
180
181 It reads the output of the process, formats it and inserts it into
182 the contents pane.
183 """
184 self.process.setReadChannel(QProcess.StandardOutput)
185
186 while self.process.canReadLine():
187 s = str(self.process.readLine(), self.__ioEncoding, 'replace')\
188 .strip()
189 rev, s = s.split(None, 1)
190 try:
191 author, text = s.split(' ', 1)
192 except ValueError:
193 author = s.strip()
194 text = ""
195 self.__generateItem(rev, author, text)
196
197 def __readStderr(self):
198 """
199 Private slot to handle the readyReadStderr signal.
200
201 It reads the error output of the process and inserts it into the
202 error pane.
203 """
204 if self.process is not None:
205 self.errorGroup.show()
206 s = str(self.process.readAllStandardError(),
207 Preferences.getSystem("IOEncoding"),
208 'replace')
209 self.errors.insertPlainText(s)
210 self.errors.ensureCursorVisible()
211
212 def on_passwordCheckBox_toggled(self, isOn):
213 """
214 Private slot to handle the password checkbox toggled.
215
216 @param isOn flag indicating the status of the check box (boolean)
217 """
218 if isOn:
219 self.input.setEchoMode(QLineEdit.Password)
220 else:
221 self.input.setEchoMode(QLineEdit.Normal)
222
223 @pyqtSlot()
224 def on_sendButton_clicked(self):
225 """
226 Private slot to send the input to the subversion process.
227 """
228 inputTxt = self.input.text()
229 inputTxt += os.linesep
230
231 if self.passwordCheckBox.isChecked():
232 self.errors.insertPlainText(os.linesep)
233 self.errors.ensureCursorVisible()
234 else:
235 self.errors.insertPlainText(inputTxt)
236 self.errors.ensureCursorVisible()
237
238 self.process.write(strToQByteArray(inputTxt))
239
240 self.passwordCheckBox.setChecked(False)
241 self.input.clear()
242
243 def on_input_returnPressed(self):
244 """
245 Private slot to handle the press of the return key in the input field.
246 """
247 self.intercept = True
248 self.on_sendButton_clicked()
249
250 def keyPressEvent(self, evt):
251 """
252 Protected slot to handle a key press event.
253
254 @param evt the key press event (QKeyEvent)
255 """
256 if self.intercept:
257 self.intercept = False
258 evt.accept()
259 return
260 super(SvnBlameDialog, self).keyPressEvent(evt)

eric ide

mercurial