From 5ade26a3460b13e9ca8bce118e3d124a74d0a1b1 Mon Sep 17 00:00:00 2001 From: qpskcn1 Date: Fri, 19 Oct 2018 21:36:26 -0400 Subject: [PATCH 1/3] Add searching feature in Shotgun Desktop Console --- python/tk_desktop/console.py | 46 +++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/python/tk_desktop/console.py b/python/tk_desktop/console.py index 0cf1d1cc..f2e44c4d 100644 --- a/python/tk_desktop/console.py +++ b/python/tk_desktop/console.py @@ -10,6 +10,7 @@ import sgtk import logging +import re from sgtk.platform.qt import QtGui from sgtk.platform.qt import QtCore @@ -66,8 +67,10 @@ def __init__(self, parent=None): self.setWindowIcon(QtGui.QIcon(":/tk-desktop/default_systray_icon.png")) self.__logs = QtGui.QPlainTextEdit() - layout = QtGui.QHBoxLayout() + self.__find = QtGui.QLineEdit() + layout = QtGui.QVBoxLayout() layout.addWidget(self.__logs) + layout.addWidget(self.__find) self.setLayout(layout) # configure the text widget @@ -77,6 +80,13 @@ def __init__(self, parent=None): self.__logs.customContextMenuRequested.connect(self.on_logs_context_menu_request) self.__logs.setStyleSheet("QPlainTextEdit:focus { border: none; }") + # configure the find lineEdit + self.__find.setPlaceholderText("Find") + self.__find.returnPressed.connect(self.find) + self.__last_start = 0 + # set shortcut + find_shortcut = QtGui.QShortcut(QtGui.QKeySequence("Ctrl+F"), self) + find_shortcut.activated.connect(self.focus_find) # load up previous size self._settings_manager = settings.UserSettings(sgtk.platform.current_bundle()) pos = self._settings_manager.retrieve("console.pos", self.pos(), self._settings_manager.SCOPE_GLOBAL) @@ -95,6 +105,9 @@ def on_logs_context_menu_request(self, point): clear_action.triggered.connect(self.clear) close_action = menu.addAction("Close") close_action.triggered.connect(self.close) + find_action = menu.addAction("Find") + find_action.setShortcut("Ctrl+F") + find_action.triggered.connect(self.focus_find) menu.exec_(self.__logs.mapToGlobal(point)) @@ -112,6 +125,37 @@ def append_text(self, text, force_show=False): def clear(self): self.__logs.setPlainText("") + def focus_find(self): + self.__find.setFocus() + self.__find.selectAll() + + def find(self): + query = self.__find.text() + logs = self.__logs.toPlainText() + pattern = re.compile(query, re.IGNORECASE) + match = pattern.search(logs, self.__last_start + 1) + if match: + self.__last_start = match.start() + self.highlight(self.__last_start, match.end()) + else: + self.__last_start = 0 + # Set the cursor to the end if the search was unsuccessful + self.__logs.moveCursor(QtGui.QTextCursor.End) + + def highlight(self, start, end): + cursor = self.__logs.textCursor() + # set the position to the beginning of the last match + cursor.setPosition(start) + # select match + cursor.movePosition( + QtGui.QTextCursor.Right, + QtGui.QTextCursor.KeepAnchor, + end - start + ) + + # set this new cursor as logs' cursor + self.__logs.setTextCursor(cursor) + def show_and_raise(self): self.show() self.raise_() From f3b0253a916a493b986839dd88953a5b478b75e9 Mon Sep 17 00:00:00 2001 From: qpskcn1 Date: Sat, 20 Oct 2018 12:18:59 -0400 Subject: [PATCH 2/3] highlight all matches on first search --- python/tk_desktop/console.py | 73 +++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 14 deletions(-) diff --git a/python/tk_desktop/console.py b/python/tk_desktop/console.py index f2e44c4d..2f3a9540 100644 --- a/python/tk_desktop/console.py +++ b/python/tk_desktop/console.py @@ -10,7 +10,6 @@ import sgtk import logging -import re from sgtk.platform.qt import QtGui from sgtk.platform.qt import QtCore @@ -83,7 +82,9 @@ def __init__(self, parent=None): # configure the find lineEdit self.__find.setPlaceholderText("Find") self.__find.returnPressed.connect(self.find) - self.__last_start = 0 + self.__pattern = "" + self.__match_index = 0 + self.__matches = [] # set shortcut find_shortcut = QtGui.QShortcut(QtGui.QKeySequence("Ctrl+F"), self) find_shortcut.activated.connect(self.focus_find) @@ -130,32 +131,76 @@ def focus_find(self): self.__find.selectAll() def find(self): - query = self.__find.text() + pattern = self.__find.text() logs = self.__logs.toPlainText() - pattern = re.compile(query, re.IGNORECASE) - match = pattern.search(logs, self.__last_start + 1) - if match: - self.__last_start = match.start() - self.highlight(self.__last_start, match.end()) + cursor = self.__logs.textCursor() + regex = QtCore.QRegExp(pattern, QtCore.Qt.CaseInsensitive) + if pattern != self.__pattern: + self.clear_highlight() + self.__match_index = 0 + self.__matches = self.find_all(pattern, regex, logs, cursor) + self.__pattern = pattern + # total number of matches + match_count = len(self.__matches) + # highlight pattern forward + if self.__match_index < len(self.__matches): + start, matched_length = self.__matches[self.__match_index] + print "{} of {}".format(self.__match_index + 1, match_count) + self.highlight_one( + start, + matched_length + ) + self.__match_index += 1 else: - self.__last_start = 0 - # Set the cursor to the end if the search was unsuccessful + # search reaches end, reset match index and cursor + self.__match_index = 0 self.__logs.moveCursor(QtGui.QTextCursor.End) + self.find() - def highlight(self, start, end): + def find_all(self, pattern, regex, logs, cursor): + highlight_format = QtGui.QTextCharFormat() + highlight_format.setBackground( + QtGui.QBrush(QtGui.QColor(80, 80, 80)) + ) + matches = [] + pos = 0 + index = regex.indexIn(logs, pos) + count = 0 + while (index != -1): + count += 1 + matched_length = regex.matchedLength() + # append start index and length of last matched string + # length could be different + matches.append((index, matched_length)) + # select the matched text and apply the desired format + self.highlight_one(index, matched_length, highlight_format) + # Move to the next match + pos = index + matched_length + index = regex.indexIn(logs, pos) + return matches + + def highlight_one(self, start, length, highlight_format=None): cursor = self.__logs.textCursor() # set the position to the beginning of the last match cursor.setPosition(start) - # select match + # select one match cursor.movePosition( QtGui.QTextCursor.Right, QtGui.QTextCursor.KeepAnchor, - end - start + length ) - + if highlight_format: + cursor.mergeCharFormat(highlight_format) # set this new cursor as logs' cursor self.__logs.setTextCursor(cursor) + def clear_highlight(self): + cursor = self.__logs.textCursor() + cursor.select(QtGui.QTextCursor.Document) + cursor.setCharFormat(QtGui.QTextCharFormat()) + cursor.clearSelection() + self.__logs.setTextCursor(cursor) + def show_and_raise(self): self.show() self.raise_() From cd1bfa4691d1a8fb92a774ff9a4e3c1c7f4fba0a Mon Sep 17 00:00:00 2001 From: qpskcn1 Date: Mon, 22 Oct 2018 10:42:47 -0400 Subject: [PATCH 3/3] Adjust layout, show more details of matches --- python/tk_desktop/console.py | 47 +++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/python/tk_desktop/console.py b/python/tk_desktop/console.py index 2f3a9540..c05b4dee 100644 --- a/python/tk_desktop/console.py +++ b/python/tk_desktop/console.py @@ -67,9 +67,14 @@ def __init__(self, parent=None): self.__logs = QtGui.QPlainTextEdit() self.__find = QtGui.QLineEdit() + self.__find_label = QtGui.QLabel() layout = QtGui.QVBoxLayout() + find_layout = QtGui.QHBoxLayout() layout.addWidget(self.__logs) - layout.addWidget(self.__find) + layout.addLayout(find_layout) + find_layout.addStretch() + find_layout.addWidget(self.__find) + find_layout.addWidget(self.__find_label) self.setLayout(layout) # configure the text widget @@ -78,9 +83,10 @@ def __init__(self, parent=None): self.__logs.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse) self.__logs.customContextMenuRequested.connect(self.on_logs_context_menu_request) self.__logs.setStyleSheet("QPlainTextEdit:focus { border: none; }") - - # configure the find lineEdit + # configure the find area self.__find.setPlaceholderText("Find") + self.__find_label.setText("No Results") + self.__find_label.setFixedWidth(60) self.__find.returnPressed.connect(self.find) self.__pattern = "" self.__match_index = 0 @@ -119,6 +125,8 @@ def append_text(self, text, force_show=False): cursor.movePosition(cursor.StartOfLine) self.__logs.setTextCursor(cursor) self.__logs.ensureCursorVisible() + # reset search after logs changed + self.__pattern = "" if force_show: self.show_and_raise() @@ -132,20 +140,31 @@ def focus_find(self): def find(self): pattern = self.__find.text() + # do nothing if pattern is empty + if not pattern: + self.__find_label.setText("No Results") + return logs = self.__logs.toPlainText() - cursor = self.__logs.textCursor() - regex = QtCore.QRegExp(pattern, QtCore.Qt.CaseInsensitive) + # try to find all matches again if pattern changed if pattern != self.__pattern: self.clear_highlight() self.__match_index = 0 - self.__matches = self.find_all(pattern, regex, logs, cursor) + self.__matches = self.find_all(pattern, logs) self.__pattern = pattern + self.find_in_matches(self.__matches) + + def find_in_matches(self, matches): # total number of matches - match_count = len(self.__matches) + match_count = len(matches) + if match_count == 0: + self.__find_label.setText("No Results") + return # highlight pattern forward if self.__match_index < len(self.__matches): start, matched_length = self.__matches[self.__match_index] - print "{} of {}".format(self.__match_index + 1, match_count) + self.__find_label.setText( + "{} of {}".format(self.__match_index + 1, match_count) + ) self.highlight_one( start, matched_length @@ -153,15 +172,17 @@ def find(self): self.__match_index += 1 else: # search reaches end, reset match index and cursor + # and search again self.__match_index = 0 self.__logs.moveCursor(QtGui.QTextCursor.End) - self.find() + self.find_in_matches(matches) - def find_all(self, pattern, regex, logs, cursor): + def find_all(self, pattern, logs): highlight_format = QtGui.QTextCharFormat() highlight_format.setBackground( QtGui.QBrush(QtGui.QColor(80, 80, 80)) ) + regex = QtCore.QRegExp(pattern, QtCore.Qt.CaseInsensitive) matches = [] pos = 0 index = regex.indexIn(logs, pos) @@ -195,9 +216,13 @@ def highlight_one(self, start, length, highlight_format=None): self.__logs.setTextCursor(cursor) def clear_highlight(self): + original_format = QtGui.QTextCharFormat() + original_format.setBackground( + QtGui.QBrush(QtGui.QColor(0, 0, 0), QtCore.Qt.NoBrush) + ) cursor = self.__logs.textCursor() cursor.select(QtGui.QTextCursor.Document) - cursor.setCharFormat(QtGui.QTextCharFormat()) + cursor.mergeCharFormat(original_format) cursor.clearSelection() self.__logs.setTextCursor(cursor)