Plugins/VcsPlugins/vcsGit/GitRemoteRepositoriesDialog.py

changeset 6020
baf6da1ae288
child 6048
82ad8ec9548c
equal deleted inserted replaced
6019:58ecdaf0b789 6020:baf6da1ae288
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2014 - 2017 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a dialog to show available remote repositories.
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 pyqtSlot, Qt, QProcess, QTimer
19 from PyQt5.QtWidgets import QWidget, QHeaderView, QTreeWidgetItem, \
20 QDialogButtonBox, QLineEdit
21
22 from E5Gui import E5MessageBox
23
24 from .Ui_GitRemoteRepositoriesDialog import Ui_GitRemoteRepositoriesDialog
25
26 from .GitUtilities import strToQByteArray
27
28 import Preferences
29
30
31 class GitRemoteRepositoriesDialog(QWidget, Ui_GitRemoteRepositoriesDialog):
32 """
33 Class implementing a dialog to show available remote repositories.
34 """
35 def __init__(self, vcs, parent=None):
36 """
37 Constructor
38
39 @param vcs reference to the vcs object
40 @param parent parent widget (QWidget)
41 """
42 super(GitRemoteRepositoriesDialog, self).__init__(parent)
43 self.setupUi(self)
44
45 self.vcs = vcs
46 self.process = QProcess()
47 self.process.finished.connect(self.__procFinished)
48 self.process.readyReadStandardOutput.connect(self.__readStdout)
49 self.process.readyReadStandardError.connect(self.__readStderr)
50
51 self.refreshButton = self.buttonBox.addButton(
52 self.tr("Refresh"), QDialogButtonBox.ActionRole)
53 self.refreshButton.setToolTip(
54 self.tr("Press to refresh the repositories display"))
55 self.refreshButton.setEnabled(False)
56 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False)
57 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True)
58
59 self.__lastColumn = self.repolist.columnCount()
60
61 self.repolist.headerItem().setText(self.__lastColumn, "")
62 self.repolist.header().setSortIndicator(0, Qt.AscendingOrder)
63
64 self.__ioEncoding = Preferences.getSystem("IOEncoding")
65
66 def __resort(self):
67 """
68 Private method to resort the list.
69 """
70 self.repolist.sortItems(
71 self.repolist.sortColumn(),
72 self.repolist.header().sortIndicatorOrder())
73
74 def __resizeColumns(self):
75 """
76 Private method to resize the list columns.
77 """
78 self.repolist.header().resizeSections(QHeaderView.ResizeToContents)
79 self.repolist.header().setStretchLastSection(True)
80
81 def __generateItem(self, name, url, oper):
82 """
83 Private method to generate a status item in the status list.
84
85 @param name name of the remote repository (string)
86 @param url URL of the remote repository (string)
87 @param oper operation the remote repository may be used for (string)
88 """
89 foundItems = self.repolist.findItems(name, Qt.MatchExactly, 0)
90 if foundItems:
91 # modify the operations column only
92 foundItems[0].setText(
93 2, "{0} + {1}".format(foundItems[0].text(2), oper))
94 else:
95 QTreeWidgetItem(self.repolist, [name, url, oper])
96
97 def closeEvent(self, e):
98 """
99 Protected slot implementing a close event handler.
100
101 @param e close event (QCloseEvent)
102 """
103 if self.process is not None and \
104 self.process.state() != QProcess.NotRunning:
105 self.process.terminate()
106 QTimer.singleShot(2000, self.process.kill)
107 self.process.waitForFinished(3000)
108
109 e.accept()
110
111 def start(self, projectDir):
112 """
113 Public slot to start the git remote command.
114
115 @param projectDir name of the project directory (string)
116 """
117 self.repolist.clear()
118
119 self.errorGroup.hide()
120 self.intercept = False
121 self.projectDir = projectDir
122
123 self.__ioEncoding = Preferences.getSystem("IOEncoding")
124
125 self.removeButton.setEnabled(False)
126 self.renameButton.setEnabled(False)
127 self.pruneButton.setEnabled(False)
128 self.showInfoButton.setEnabled(False)
129
130 args = self.vcs.initCommand("remote")
131 args.append('--verbose')
132
133 # find the root of the repo
134 repodir = self.projectDir
135 while not os.path.isdir(os.path.join(repodir, self.vcs.adminDir)):
136 repodir = os.path.dirname(repodir)
137 if os.path.splitdrive(repodir)[1] == os.sep:
138 return
139
140 self.process.kill()
141 self.process.setWorkingDirectory(repodir)
142
143 self.process.start('git', args)
144 procStarted = self.process.waitForStarted(5000)
145 if not procStarted:
146 self.inputGroup.setEnabled(False)
147 self.inputGroup.hide()
148 E5MessageBox.critical(
149 self,
150 self.tr('Process Generation Error'),
151 self.tr(
152 'The process {0} could not be started. '
153 'Ensure, that it is in the search path.'
154 ).format('git'))
155 else:
156 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False)
157 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(True)
158 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True)
159
160 self.inputGroup.setEnabled(True)
161 self.inputGroup.show()
162 self.refreshButton.setEnabled(False)
163
164 def __finish(self):
165 """
166 Private slot called when the process finished or the user pressed
167 the button.
168 """
169 if self.process is not None and \
170 self.process.state() != QProcess.NotRunning:
171 self.process.terminate()
172 QTimer.singleShot(2000, self.process.kill)
173 self.process.waitForFinished(3000)
174
175 self.inputGroup.setEnabled(False)
176 self.inputGroup.hide()
177 self.refreshButton.setEnabled(True)
178
179 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True)
180 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False)
181 self.buttonBox.button(QDialogButtonBox.Close).setDefault(True)
182 self.buttonBox.button(QDialogButtonBox.Close).setFocus(
183 Qt.OtherFocusReason)
184
185 self.__resort()
186 self.__resizeColumns()
187
188 self.__updateButtons()
189
190 def on_buttonBox_clicked(self, button):
191 """
192 Private slot called by a button of the button box clicked.
193
194 @param button button that was clicked (QAbstractButton)
195 """
196 if button == self.buttonBox.button(QDialogButtonBox.Close):
197 self.close()
198 elif button == self.buttonBox.button(QDialogButtonBox.Cancel):
199 self.__finish()
200 elif button == self.refreshButton:
201 self.on_refreshButton_clicked()
202
203 def __procFinished(self, exitCode, exitStatus):
204 """
205 Private slot connected to the finished signal.
206
207 @param exitCode exit code of the process (integer)
208 @param exitStatus exit status of the process (QProcess.ExitStatus)
209 """
210 self.__finish()
211
212 def __readStdout(self):
213 """
214 Private slot to handle the readyReadStandardOutput signal.
215
216 It reads the output of the process, formats it and inserts it into
217 the contents pane.
218 """
219 if self.process is not None:
220 self.process.setReadChannel(QProcess.StandardOutput)
221
222 while self.process.canReadLine():
223 line = str(self.process.readLine(), self.__ioEncoding,
224 'replace').strip()
225
226 name, line = line.split(None, 1)
227 url, oper = line.rsplit(None, 1)
228 oper = oper[1:-1] # it is enclosed in ()
229 self.__generateItem(name, url, oper)
230
231 def __readStderr(self):
232 """
233 Private slot to handle the readyReadStandardError signal.
234
235 It reads the error output of the process and inserts it into the
236 error pane.
237 """
238 if self.process is not None:
239 s = str(self.process.readAllStandardError(),
240 self.__ioEncoding, 'replace')
241 self.errorGroup.show()
242 self.errors.insertPlainText(s)
243 self.errors.ensureCursorVisible()
244
245 def on_passwordCheckBox_toggled(self, isOn):
246 """
247 Private slot to handle the password checkbox toggled.
248
249 @param isOn flag indicating the status of the check box (boolean)
250 """
251 if isOn:
252 self.input.setEchoMode(QLineEdit.Password)
253 else:
254 self.input.setEchoMode(QLineEdit.Normal)
255
256 @pyqtSlot()
257 def on_sendButton_clicked(self):
258 """
259 Private slot to send the input to the git process.
260 """
261 inputTxt = self.input.text()
262 inputTxt += os.linesep
263
264 if self.passwordCheckBox.isChecked():
265 self.errors.insertPlainText(os.linesep)
266 self.errors.ensureCursorVisible()
267 else:
268 self.errors.insertPlainText(inputTxt)
269 self.errors.ensureCursorVisible()
270
271 self.process.write(strToQByteArray(inputTxt))
272
273 self.passwordCheckBox.setChecked(False)
274 self.input.clear()
275
276 def on_input_returnPressed(self):
277 """
278 Private slot to handle the press of the return key in the input field.
279 """
280 self.intercept = True
281 self.on_sendButton_clicked()
282
283 def keyPressEvent(self, evt):
284 """
285 Protected slot to handle a key press event.
286
287 @param evt the key press event (QKeyEvent)
288 """
289 if self.intercept:
290 self.intercept = False
291 evt.accept()
292 return
293 super(GitRemoteRepositoriesDialog, self).keyPressEvent(evt)
294
295 @pyqtSlot()
296 def on_refreshButton_clicked(self):
297 """
298 Private slot to refresh the status display.
299 """
300 self.start(self.projectDir)
301
302 def __updateButtons(self):
303 """
304 Private method to update the buttons status.
305 """
306 enable = len(self.repolist.selectedItems()) == 1
307
308 self.removeButton.setEnabled(enable)
309 self.renameButton.setEnabled(enable)
310 self.pruneButton.setEnabled(enable)
311 self.showInfoButton.setEnabled(enable)
312
313 @pyqtSlot()
314 def on_repolist_itemSelectionChanged(self):
315 """
316 Private slot to act upon changes of selected items.
317 """
318 self.__updateButtons()
319
320 @pyqtSlot()
321 def on_addButton_clicked(self):
322 """
323 Private slot to add a remote repository.
324 """
325 self.vcs.gitAddRemote(self.projectDir)
326 self.on_refreshButton_clicked()
327
328 @pyqtSlot()
329 def on_renameButton_clicked(self):
330 """
331 Private slot to rename a remote repository.
332 """
333 remoteName = self.repolist.selectedItems()[0].text(0)
334 self.vcs.gitRenameRemote(self.projectDir, remoteName)
335 self.on_refreshButton_clicked()
336
337 @pyqtSlot()
338 def on_removeButton_clicked(self):
339 """
340 Private slot to remove a remote repository.
341 """
342 remoteName = self.repolist.selectedItems()[0].text(0)
343 self.vcs.gitRemoveRemote(self.projectDir, remoteName)
344 self.on_refreshButton_clicked()
345
346 @pyqtSlot()
347 def on_showInfoButton_clicked(self):
348 """
349 Private slot to show information about a remote repository.
350 """
351 remoteName = self.repolist.selectedItems()[0].text(0)
352 self.vcs.gitShowRemote(self.projectDir, remoteName)
353
354 @pyqtSlot()
355 def on_pruneButton_clicked(self):
356 """
357 Private slot to prune all stale remote-tracking branches.
358 """
359 remoteName = self.repolist.selectedItems()[0].text(0)
360 self.vcs.gitPruneRemote(self.projectDir, remoteName)

eric ide

mercurial