eric7/Plugins/VcsPlugins/vcsSubversion/SvnLogBrowserDialog.py

branch
eric7
changeset 8312
800c432b34c8
parent 8222
5994b80b8760
child 8318
962bce857696
equal deleted inserted replaced
8311:4e8b98454baa 8312:800c432b34c8
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2007 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a dialog to browse the log history.
8 """
9
10 import re
11 import os
12
13 from PyQt5.QtCore import pyqtSlot, Qt, QTimer, QDate, QProcess, QPoint
14 from PyQt5.QtWidgets import (
15 QHeaderView, QLineEdit, QWidget, QApplication, QDialogButtonBox,
16 QTreeWidgetItem
17 )
18
19 from E5Gui import E5MessageBox
20 from E5Gui.E5OverrideCursor import E5OverrideCursorProcess
21
22 from .Ui_SvnLogBrowserDialog import Ui_SvnLogBrowserDialog
23
24 import Preferences
25 import UI.PixmapCache
26 from Globals import strToQByteArray
27
28
29 class SvnLogBrowserDialog(QWidget, Ui_SvnLogBrowserDialog):
30 """
31 Class implementing a dialog to browse the log history.
32 """
33 def __init__(self, vcs, parent=None):
34 """
35 Constructor
36
37 @param vcs reference to the vcs object
38 @param parent parent widget (QWidget)
39 """
40 super().__init__(parent)
41 self.setupUi(self)
42
43 self.__position = QPoint()
44
45 self.buttonBox.button(
46 QDialogButtonBox.StandardButton.Close).setEnabled(False)
47 self.buttonBox.button(
48 QDialogButtonBox.StandardButton.Cancel).setDefault(True)
49
50 self.upButton.setIcon(UI.PixmapCache.getIcon("1uparrow"))
51 self.downButton.setIcon(UI.PixmapCache.getIcon("1downarrow"))
52
53 self.filesTree.headerItem().setText(self.filesTree.columnCount(), "")
54 self.filesTree.header().setSortIndicator(
55 0, Qt.SortOrder.AscendingOrder)
56
57 self.vcs = vcs
58
59 self.__initData()
60
61 self.fromDate.setDisplayFormat("yyyy-MM-dd")
62 self.toDate.setDisplayFormat("yyyy-MM-dd")
63 self.__resetUI()
64
65 self.__messageRole = Qt.ItemDataRole.UserRole
66 self.__changesRole = Qt.ItemDataRole.UserRole + 1
67
68 self.__process = E5OverrideCursorProcess()
69 self.__process.finished.connect(self.__procFinished)
70 self.__process.readyReadStandardOutput.connect(self.__readStdout)
71 self.__process.readyReadStandardError.connect(self.__readStderr)
72
73 self.rx_sep1 = re.compile('\\-+\\s*')
74 self.rx_sep2 = re.compile('=+\\s*')
75 self.rx_rev1 = re.compile(
76 r'rev ([0-9]+): ([^|]*) \| ([^|]*) \| ([0-9]+) .*')
77 # "rev" followed by one or more decimals followed by a colon followed
78 # anything up to " | " (twice) followed by one or more decimals
79 # followed by anything
80 self.rx_rev2 = re.compile(
81 r'r([0-9]+) \| ([^|]*) \| ([^|]*) \| ([0-9]+) .*')
82 # "r" followed by one or more decimals followed by " | " followed
83 # anything up to " | " (twice) followed by one or more decimals
84 # followed by anything
85 self.rx_flags1 = re.compile(
86 r""" ([ADM])\s(.*)\s+\(\w+\s+(.*):([0-9]+)\)\s*""")
87 # three blanks followed by A or D or M followed by path followed by
88 # path copied from followed by copied from revision
89 self.rx_flags2 = re.compile(' ([ADM]) (.*)\\s*')
90 # three blanks followed by A or D or M followed by path
91
92 self.flags = {
93 'A': self.tr('Added'),
94 'D': self.tr('Deleted'),
95 'M': self.tr('Modified'),
96 'R': self.tr('Replaced'),
97 }
98 self.intercept = False
99
100 self.__logTreeNormalFont = self.logTree.font()
101 self.__logTreeNormalFont.setBold(False)
102 self.__logTreeBoldFont = self.logTree.font()
103 self.__logTreeBoldFont.setBold(True)
104
105 self.__finishCallbacks = []
106
107 def __addFinishCallback(self, callback):
108 """
109 Private method to add a method to be called once the process finished.
110
111 The callback methods are invoke in a FIFO style and are consumed. If
112 a callback method needs to be called again, it must be added again.
113
114 @param callback callback method
115 @type function
116 """
117 if callback not in self.__finishCallbacks:
118 self.__finishCallbacks.append(callback)
119
120 def __initData(self):
121 """
122 Private method to (re-)initialize some data.
123 """
124 self.__maxDate = QDate()
125 self.__minDate = QDate()
126 self.__filterLogsEnabled = True
127
128 self.buf = [] # buffer for stdout
129 self.diff = None
130 self.__started = False
131 self.__lastRev = 0
132
133 def closeEvent(self, e):
134 """
135 Protected slot implementing a close event handler.
136
137 @param e close event (QCloseEvent)
138 """
139 if (
140 self.__process is not None and
141 self.__process.state() != QProcess.ProcessState.NotRunning
142 ):
143 self.__process.terminate()
144 QTimer.singleShot(2000, self.__process.kill)
145 self.__process.waitForFinished(3000)
146
147 self.__position = self.pos()
148
149 e.accept()
150
151 def show(self):
152 """
153 Public slot to show the dialog.
154 """
155 if not self.__position.isNull():
156 self.move(self.__position)
157 self.__resetUI()
158
159 super().show()
160
161 def __resetUI(self):
162 """
163 Private method to reset the user interface.
164 """
165 self.fromDate.setDate(QDate.currentDate())
166 self.toDate.setDate(QDate.currentDate())
167 self.fieldCombo.setCurrentIndex(self.fieldCombo.findText(
168 self.tr("Message")))
169 self.limitSpinBox.setValue(self.vcs.getPlugin().getPreferences(
170 "LogLimit"))
171 self.stopCheckBox.setChecked(self.vcs.getPlugin().getPreferences(
172 "StopLogOnCopy"))
173
174 self.logTree.clear()
175
176 self.nextButton.setEnabled(True)
177 self.limitSpinBox.setEnabled(True)
178
179 def __resizeColumnsLog(self):
180 """
181 Private method to resize the log tree columns.
182 """
183 self.logTree.header().resizeSections(
184 QHeaderView.ResizeMode.ResizeToContents)
185 self.logTree.header().setStretchLastSection(True)
186
187 def __resortLog(self):
188 """
189 Private method to resort the log tree.
190 """
191 self.logTree.sortItems(
192 self.logTree.sortColumn(),
193 self.logTree.header().sortIndicatorOrder())
194
195 def __resizeColumnsFiles(self):
196 """
197 Private method to resize the changed files tree columns.
198 """
199 self.filesTree.header().resizeSections(
200 QHeaderView.ResizeMode.ResizeToContents)
201 self.filesTree.header().setStretchLastSection(True)
202
203 def __resortFiles(self):
204 """
205 Private method to resort the changed files tree.
206 """
207 sortColumn = self.filesTree.sortColumn()
208 self.filesTree.sortItems(
209 1, self.filesTree.header().sortIndicatorOrder())
210 self.filesTree.sortItems(
211 sortColumn, self.filesTree.header().sortIndicatorOrder())
212
213 def __generateLogItem(self, author, date, message, revision, changedPaths):
214 """
215 Private method to generate a log tree entry.
216
217 @param author author info (string)
218 @param date date info (string)
219 @param message text of the log message (list of strings)
220 @param revision revision info (string)
221 @param changedPaths list of dictionary objects containing
222 info about the changed files/directories
223 @return reference to the generated item (QTreeWidgetItem)
224 """
225 msg = []
226 for line in message:
227 msg.append(line.strip())
228
229 itm = QTreeWidgetItem(self.logTree)
230 itm.setData(0, Qt.ItemDataRole.DisplayRole, int(revision))
231 itm.setData(1, Qt.ItemDataRole.DisplayRole, author)
232 itm.setData(2, Qt.ItemDataRole.DisplayRole, date)
233 itm.setData(3, Qt.ItemDataRole.DisplayRole, " ".join(msg))
234
235 itm.setData(0, self.__messageRole, message)
236 itm.setData(0, self.__changesRole, changedPaths)
237
238 itm.setTextAlignment(0, Qt.AlignmentFlag.AlignRight)
239 itm.setTextAlignment(1, Qt.AlignmentFlag.AlignLeft)
240 itm.setTextAlignment(2, Qt.AlignmentFlag.AlignLeft)
241 itm.setTextAlignment(3, Qt.AlignmentFlag.AlignLeft)
242 itm.setTextAlignment(4, Qt.AlignmentFlag.AlignLeft)
243
244 try:
245 self.__lastRev = int(revision)
246 except ValueError:
247 self.__lastRev = 0
248
249 return itm
250
251 def __generateFileItem(self, action, path, copyFrom, copyRev):
252 """
253 Private method to generate a changed files tree entry.
254
255 @param action indicator for the change action ("A", "D" or "M")
256 @param path path of the file in the repository (string)
257 @param copyFrom path the file was copied from (None, string)
258 @param copyRev revision the file was copied from (None, string)
259 @return reference to the generated item (QTreeWidgetItem)
260 """
261 itm = QTreeWidgetItem(self.filesTree, [
262 self.flags[action],
263 path,
264 copyFrom,
265 copyRev,
266 ])
267
268 itm.setTextAlignment(3, Qt.AlignmentFlag.AlignRight)
269
270 return itm
271
272 def __getLogEntries(self, startRev=None):
273 """
274 Private method to retrieve log entries from the repository.
275
276 @param startRev revision number to start from (integer, string)
277 """
278 self.buttonBox.button(
279 QDialogButtonBox.StandardButton.Close).setEnabled(False)
280 self.buttonBox.button(
281 QDialogButtonBox.StandardButton.Cancel).setEnabled(True)
282 self.buttonBox.button(
283 QDialogButtonBox.StandardButton.Cancel).setDefault(True)
284 QApplication.processEvents()
285
286 self.intercept = False
287 self.__process.kill()
288
289 self.buf = []
290 self.cancelled = False
291 self.errors.clear()
292
293 args = []
294 args.append('log')
295 self.vcs.addArguments(args, self.vcs.options['global'])
296 self.vcs.addArguments(args, self.vcs.options['log'])
297 args.append('--verbose')
298 args.append('--limit')
299 args.append('{0:d}'.format(self.limitSpinBox.value()))
300 if startRev is not None:
301 args.append('--revision')
302 args.append('{0}:0'.format(startRev))
303 if self.stopCheckBox.isChecked():
304 args.append('--stop-on-copy')
305 args.append(self.fname)
306
307 self.__process.setWorkingDirectory(self.dname)
308
309 self.inputGroup.setEnabled(True)
310 self.inputGroup.show()
311
312 self.__process.start('svn', args)
313 procStarted = self.__process.waitForStarted(5000)
314 if not procStarted:
315 self.inputGroup.setEnabled(False)
316 self.inputGroup.hide()
317 E5MessageBox.critical(
318 self,
319 self.tr('Process Generation Error'),
320 self.tr(
321 'The process {0} could not be started. '
322 'Ensure, that it is in the search path.'
323 ).format('svn'))
324
325 def start(self, fn, isFile=False):
326 """
327 Public slot to start the svn log command.
328
329 @param fn filename to show the log for (string)
330 @param isFile flag indicating log for a file is to be shown
331 (boolean)
332 """
333 self.sbsCheckBox.setEnabled(isFile)
334 self.sbsCheckBox.setVisible(isFile)
335
336 self.errorGroup.hide()
337 QApplication.processEvents()
338
339 self.__initData()
340
341 self.filename = fn
342 self.dname, self.fname = self.vcs.splitPath(fn)
343
344 self.activateWindow()
345 self.raise_()
346
347 self.logTree.clear()
348 self.__started = True
349 self.__getLogEntries()
350
351 def __procFinished(self, exitCode, exitStatus):
352 """
353 Private slot connected to the finished signal.
354
355 @param exitCode exit code of the process (integer)
356 @param exitStatus exit status of the process (QProcess.ExitStatus)
357 """
358 self.__processBuffer()
359 self.__finish()
360
361 def __finish(self):
362 """
363 Private slot called when the process finished or the user pressed the
364 button.
365 """
366 if (
367 self.__process is not None and
368 self.__process.state() != QProcess.ProcessState.NotRunning
369 ):
370 self.__process.terminate()
371 QTimer.singleShot(2000, self.__process.kill)
372 self.__process.waitForFinished(3000)
373
374 self.buttonBox.button(
375 QDialogButtonBox.StandardButton.Close).setEnabled(True)
376 self.buttonBox.button(
377 QDialogButtonBox.StandardButton.Cancel).setEnabled(False)
378 self.buttonBox.button(
379 QDialogButtonBox.StandardButton.Close).setDefault(True)
380
381 self.inputGroup.setEnabled(False)
382 self.inputGroup.hide()
383
384 while self.__finishCallbacks:
385 self.__finishCallbacks.pop(0)()
386
387 def __processBuffer(self):
388 """
389 Private method to process the buffered output of the svn log command.
390 """
391 noEntries = 0
392 log = {"message": []}
393 changedPaths = []
394 for s in self.buf:
395 match = (
396 self.rx_rev1.fullmatch(s) or
397 self.rx_rev2.fullmatch(s) or
398 self.rx_flags1.fullmatch(s) or
399 self.rx_flags2.fullmatch(s) or
400 self.rx_sep1.fullmatch(s) or
401 self.rx_sep2.fullmatch(s)
402 )
403 if match is None:
404 if s.strip().endswith(":") or not s.strip():
405 continue
406 else:
407 log["message"].append(s)
408 elif match.re is self.rx_rev1:
409 log["revision"] = match.group(1)
410 log["author"] = match.group(2)
411 log["date"] = match.group(3)
412 # number of lines is ignored
413 elif match.re is self.rx_rev2:
414 log["revision"] = match.group(1)
415 log["author"] = match.group(2)
416 log["date"] = " ".join(match.group(3).split()[:2])
417 # number of lines is ignored
418 elif match.re is self.rx_flags1:
419 changedPaths.append({
420 "action": match.group(1).strip(),
421 "path": match.group(2).strip(),
422 "copyfrom_path": match.group(3).strip(),
423 "copyfrom_revision": match.group(4).strip(),
424 })
425 elif match.re is self.rx_flags2:
426 changedPaths.append({
427 "action": match.group(1).strip(),
428 "path": match.group(2).strip(),
429 "copyfrom_path": "",
430 "copyfrom_revision": "",
431 })
432 elif (
433 (match.re is self.rx_sep1 or match.re is self.rx_sep2) and
434 len(log) > 1
435 ):
436 self.__generateLogItem(
437 log["author"], log["date"], log["message"],
438 log["revision"], changedPaths)
439 dt = QDate.fromString(log["date"], Qt.DateFormat.ISODate)
440 if (
441 not self.__maxDate.isValid() and
442 not self.__minDate.isValid()
443 ):
444 self.__maxDate = dt
445 self.__minDate = dt
446 else:
447 if self.__maxDate < dt:
448 self.__maxDate = dt
449 if self.__minDate > dt:
450 self.__minDate = dt
451 noEntries += 1
452 log = {"message": []}
453 changedPaths = []
454
455 self.__resizeColumnsLog()
456 self.__resortLog()
457
458 if self.__started:
459 self.logTree.setCurrentItem(self.logTree.topLevelItem(0))
460 self.__started = False
461
462 if noEntries < self.limitSpinBox.value() and not self.cancelled:
463 self.nextButton.setEnabled(False)
464 self.limitSpinBox.setEnabled(False)
465
466 self.__filterLogsEnabled = False
467 self.fromDate.setMinimumDate(self.__minDate)
468 self.fromDate.setMaximumDate(self.__maxDate)
469 self.fromDate.setDate(self.__minDate)
470 self.toDate.setMinimumDate(self.__minDate)
471 self.toDate.setMaximumDate(self.__maxDate)
472 self.toDate.setDate(self.__maxDate)
473 self.__filterLogsEnabled = True
474 self.__filterLogs()
475
476 def __readStdout(self):
477 """
478 Private slot to handle the readyReadStandardOutput signal.
479
480 It reads the output of the process and inserts it into a buffer.
481 """
482 self.__process.setReadChannel(QProcess.ProcessChannel.StandardOutput)
483
484 while self.__process.canReadLine():
485 line = str(self.__process.readLine(),
486 Preferences.getSystem("IOEncoding"),
487 'replace')
488 self.buf.append(line)
489
490 def __readStderr(self):
491 """
492 Private slot to handle the readyReadStandardError signal.
493
494 It reads the error output of the process and inserts it into the
495 error pane.
496 """
497 if self.__process is not None:
498 self.errorGroup.show()
499 s = str(self.__process.readAllStandardError(),
500 Preferences.getSystem("IOEncoding"),
501 'replace')
502 self.errors.insertPlainText(s)
503 self.errors.ensureCursorVisible()
504
505 def __diffRevisions(self, rev1, rev2):
506 """
507 Private method to do a diff of two revisions.
508
509 @param rev1 first revision number (integer)
510 @param rev2 second revision number (integer)
511 """
512 if self.sbsCheckBox.isEnabled() and self.sbsCheckBox.isChecked():
513 self.vcs.svnSbsDiff(self.filename,
514 revisions=(str(rev1), str(rev2)))
515 else:
516 if self.diff is None:
517 from .SvnDiffDialog import SvnDiffDialog
518 self.diff = SvnDiffDialog(self.vcs)
519 self.diff.show()
520 self.diff.raise_()
521 self.diff.start(self.filename, [rev1, rev2])
522
523 def on_buttonBox_clicked(self, button):
524 """
525 Private slot called by a button of the button box clicked.
526
527 @param button button that was clicked (QAbstractButton)
528 """
529 if button == self.buttonBox.button(
530 QDialogButtonBox.StandardButton.Close
531 ):
532 self.close()
533 elif button == self.buttonBox.button(
534 QDialogButtonBox.StandardButton.Cancel
535 ):
536 self.cancelled = True
537 self.__finish()
538
539 @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem)
540 def on_logTree_currentItemChanged(self, current, previous):
541 """
542 Private slot called, when the current item of the log tree changes.
543
544 @param current reference to the new current item (QTreeWidgetItem)
545 @param previous reference to the old current item (QTreeWidgetItem)
546 """
547 if current is not None:
548 self.messageEdit.clear()
549 for line in current.data(0, self.__messageRole):
550 self.messageEdit.append(line.strip())
551
552 self.filesTree.clear()
553 changes = current.data(0, self.__changesRole)
554 if len(changes) > 0:
555 for change in changes:
556 self.__generateFileItem(
557 change["action"], change["path"],
558 change["copyfrom_path"],
559 change["copyfrom_revision"])
560 self.__resizeColumnsFiles()
561 self.__resortFiles()
562
563 self.diffPreviousButton.setEnabled(
564 current != self.logTree.topLevelItem(
565 self.logTree.topLevelItemCount() - 1))
566
567 # Highlight the current entry using a bold font
568 for col in range(self.logTree.columnCount()):
569 current and current.setFont(col, self.__logTreeBoldFont)
570 previous and previous.setFont(col, self.__logTreeNormalFont)
571
572 # set the state of the up and down buttons
573 self.upButton.setEnabled(
574 current is not None and
575 self.logTree.indexOfTopLevelItem(current) > 0)
576 self.downButton.setEnabled(
577 current is not None and
578 int(current.text(0)) > 1)
579
580 @pyqtSlot()
581 def on_logTree_itemSelectionChanged(self):
582 """
583 Private slot called, when the selection has changed.
584 """
585 self.diffRevisionsButton.setEnabled(
586 len(self.logTree.selectedItems()) == 2)
587
588 @pyqtSlot()
589 def on_nextButton_clicked(self):
590 """
591 Private slot to handle the Next button.
592 """
593 if self.__lastRev > 1:
594 self.__getLogEntries(self.__lastRev - 1)
595
596 @pyqtSlot()
597 def on_diffPreviousButton_clicked(self):
598 """
599 Private slot to handle the Diff to Previous button.
600 """
601 itm = self.logTree.currentItem()
602 if itm is None:
603 self.diffPreviousButton.setEnabled(False)
604 return
605 rev2 = int(itm.text(0))
606
607 itm = self.logTree.topLevelItem(
608 self.logTree.indexOfTopLevelItem(itm) + 1)
609 if itm is None:
610 self.diffPreviousButton.setEnabled(False)
611 return
612 rev1 = int(itm.text(0))
613
614 self.__diffRevisions(rev1, rev2)
615
616 @pyqtSlot()
617 def on_diffRevisionsButton_clicked(self):
618 """
619 Private slot to handle the Compare Revisions button.
620 """
621 items = self.logTree.selectedItems()
622 if len(items) != 2:
623 self.diffRevisionsButton.setEnabled(False)
624 return
625
626 rev2 = int(items[0].text(0))
627 rev1 = int(items[1].text(0))
628
629 self.__diffRevisions(min(rev1, rev2), max(rev1, rev2))
630
631 @pyqtSlot(QDate)
632 def on_fromDate_dateChanged(self, date):
633 """
634 Private slot called, when the from date changes.
635
636 @param date new date (QDate)
637 """
638 self.__filterLogs()
639
640 @pyqtSlot(QDate)
641 def on_toDate_dateChanged(self, date):
642 """
643 Private slot called, when the from date changes.
644
645 @param date new date (QDate)
646 """
647 self.__filterLogs()
648
649 @pyqtSlot(int)
650 def on_fieldCombo_activated(self, index):
651 """
652 Private slot called, when a new filter field is selected.
653
654 @param index index of the selected entry
655 @type int
656 """
657 self.__filterLogs()
658
659 @pyqtSlot(str)
660 def on_rxEdit_textChanged(self, txt):
661 """
662 Private slot called, when a filter expression is entered.
663
664 @param txt filter expression (string)
665 """
666 self.__filterLogs()
667
668 def __filterLogs(self):
669 """
670 Private method to filter the log entries.
671 """
672 if self.__filterLogsEnabled:
673 from_ = self.fromDate.date().toString("yyyy-MM-dd")
674 to_ = self.toDate.date().addDays(1).toString("yyyy-MM-dd")
675 txt = self.fieldCombo.currentText()
676 if txt == self.tr("Author"):
677 fieldIndex = 1
678 searchRx = re.compile(self.rxEdit.text(), re.IGNORECASE)
679 elif txt == self.tr("Revision"):
680 fieldIndex = 0
681 txt = self.rxEdit.text()
682 if txt.startswith("^"):
683 searchRx = re.compile(
684 r"^\s*{0}".format(txt[1:]), re.IGNORECASE)
685 else:
686 searchRx = re.compile(txt, re.IGNORECASE)
687 else:
688 fieldIndex = 3
689 searchRx = re.compile(self.rxEdit.text(), re.IGNORECASE)
690
691 currentItem = self.logTree.currentItem()
692 for topIndex in range(self.logTree.topLevelItemCount()):
693 topItem = self.logTree.topLevelItem(topIndex)
694 if (
695 topItem.text(2) <= to_ and
696 topItem.text(2) >= from_ and
697 searchRx.match(topItem.text(fieldIndex)) is not None
698 ):
699 topItem.setHidden(False)
700 if topItem is currentItem:
701 self.on_logTree_currentItemChanged(topItem, None)
702 else:
703 topItem.setHidden(True)
704 if topItem is currentItem:
705 self.messageEdit.clear()
706 self.filesTree.clear()
707
708 @pyqtSlot(bool)
709 def on_stopCheckBox_clicked(self, checked):
710 """
711 Private slot called, when the stop on copy/move checkbox is clicked.
712
713 @param checked flag indicating the checked state (boolean)
714 """
715 self.vcs.getPlugin().setPreferences("StopLogOnCopy",
716 self.stopCheckBox.isChecked())
717 self.nextButton.setEnabled(True)
718 self.limitSpinBox.setEnabled(True)
719
720 @pyqtSlot()
721 def on_upButton_clicked(self):
722 """
723 Private slot to move the current item up one entry.
724 """
725 itm = self.logTree.itemAbove(self.logTree.currentItem())
726 if itm:
727 self.logTree.setCurrentItem(itm)
728
729 @pyqtSlot()
730 def on_downButton_clicked(self):
731 """
732 Private slot to move the current item down one entry.
733 """
734 itm = self.logTree.itemBelow(self.logTree.currentItem())
735 if itm:
736 self.logTree.setCurrentItem(itm)
737 else:
738 # load the next bunch and try again
739 self.__addFinishCallback(self.on_downButton_clicked)
740 self.on_nextButton_clicked()
741
742 def on_passwordCheckBox_toggled(self, isOn):
743 """
744 Private slot to handle the password checkbox toggled.
745
746 @param isOn flag indicating the status of the check box (boolean)
747 """
748 if isOn:
749 self.input.setEchoMode(QLineEdit.EchoMode.Password)
750 else:
751 self.input.setEchoMode(QLineEdit.EchoMode.Normal)
752
753 @pyqtSlot()
754 def on_sendButton_clicked(self):
755 """
756 Private slot to send the input to the subversion process.
757 """
758 inputTxt = self.input.text()
759 inputTxt += os.linesep
760
761 if self.passwordCheckBox.isChecked():
762 self.errors.insertPlainText(os.linesep)
763 self.errors.ensureCursorVisible()
764 else:
765 self.errors.insertPlainText(inputTxt)
766 self.errors.ensureCursorVisible()
767 self.errorGroup.show()
768
769 self.__process.write(strToQByteArray(inputTxt))
770
771 self.passwordCheckBox.setChecked(False)
772 self.input.clear()
773
774 def on_input_returnPressed(self):
775 """
776 Private slot to handle the press of the return key in the input field.
777 """
778 self.intercept = True
779 self.on_sendButton_clicked()
780
781 def keyPressEvent(self, evt):
782 """
783 Protected slot to handle a key press event.
784
785 @param evt the key press event (QKeyEvent)
786 """
787 if self.intercept:
788 self.intercept = False
789 evt.accept()
790 return
791 super().keyPressEvent(evt)

eric ide

mercurial