src/eric7/Plugins/VcsPlugins/vcsSubversion/SvnBlameDialog.py

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

eric ide

mercurial