From a06a2d01f3b2ff88e9e3f6e02a780af6fffc9b38 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Sat, 17 Dec 2022 10:06:15 +0100 Subject: [PATCH] intermediate state after fixing the logfilemodel test --- src/plugins/logfile/filterwidget.py | 2 +- src/ui/ScaledScrollBar.py | 32 ++++++++++++++++++----- src/ui/bigtext/bigtext.py | 37 ++++++++++++++++++++++---- src/ui/bigtext/logFileModel.py | 14 ++++++---- src/ui/bigtext/testlogfilemodel.py | 40 ++++++++++++++--------------- 5 files changed, 87 insertions(+), 38 deletions(-) diff --git a/src/plugins/logfile/filterwidget.py b/src/plugins/logfile/filterwidget.py index 6491bd2..5c0a05d 100644 --- a/src/plugins/logfile/filterwidget.py +++ b/src/plugins/logfile/filterwidget.py @@ -180,7 +180,7 @@ class FilterWidget(QWidget): (handle, self.tmp_filename) = tempfile.mkstemp() os.close(handle) self.filter_model = LogFileModel(self.tmp_filename, self.source_model.settings) - self.hits_view = BigText(self.filter_model) + self.hits_view = BigText(self.filter_model, show_range_slider=False) self.layout.addWidget(filter_bar) self.layout.addWidget(self.hits_view) diff --git a/src/ui/ScaledScrollBar.py b/src/ui/ScaledScrollBar.py index 44aaaba..48cddff 100644 --- a/src/ui/ScaledScrollBar.py +++ b/src/ui/ScaledScrollBar.py @@ -10,6 +10,9 @@ log = logging.getLogger("scaledScrollBar") class ScaledScrollBar(QScrollBar): is_huge = False + _range_start = 0; + _range_end = -1 + scaledValueChanged = Signal(str) """Signal emitted when the scroll bar value changes. **Note**: The value is a string and must be parsed into an int. @@ -26,12 +29,32 @@ class ScaledScrollBar(QScrollBar): self.real_maximum = self.maximum() super().valueChanged.connect(self._valueChanged) + def set_range(self, range_start: int, range_end: int): + old_range_start = self._range_start + self._range_start = range_start + self._range_end = range_end + new_max = range_end - range_start + old_value = self._real_value() + new_value = old_value - range_start + self.setMaximum(new_max) + self.setValue(new_value) + print(f"old_value: {old_value} new_value {new_value}") + def setValue(self, value: int) -> None: if self.is_huge: real_position = value / self.real_maximum super().setValue(round(self.maximum() * real_position)) else: - super().setValue(value) + super().setValue(value - self._range_start) + + def _real_value(self): + value = self.value() + if self.is_huge: + real_value = self._range_start + (value / self.maximum()) * self.real_maximum + # print("scaled value changed: %d -> %d (%f)" % (value, real_value, value / self.maximum())) + return int(real_value) + else: + return int(self._range_start + value) def setMaximum(self, maximum: int) -> None: if maximum > 2 ** 31: @@ -50,9 +73,4 @@ class ScaledScrollBar(QScrollBar): def _valueChanged(self, value: int): self.scrolled_to_end.emit(value == self.maximum() and value > 0) - if self.is_huge: - real_value = (value / self.maximum()) * self.real_maximum - # print("scaled value changed: %d -> %d (%f)" % (value, real_value, value / self.maximum())) - self.scaledValueChanged.emit(str(int(real_value))) - else: - self.scaledValueChanged.emit(str(int(value))) + self.scaledValueChanged.emit(str(self._real_value())) diff --git a/src/ui/bigtext/bigtext.py b/src/ui/bigtext/bigtext.py index bf0427f..1973670 100644 --- a/src/ui/bigtext/bigtext.py +++ b/src/ui/bigtext/bigtext.py @@ -17,6 +17,7 @@ from src.ui.bigtext.highlightingdialog import HighlightingDialog from src.ui.bigtext.line import Line from src.ui.bigtext.logFileModel import LogFileModel from src.ui.icon import Icon +from src.ui.rangeslider import RangeSlider from src.util.conversion import humanbytes from src.pluginregistry import PluginRegistry @@ -61,7 +62,7 @@ class FileWatchdogThread(QRunnable): class BigText(QWidget): trigger_update = Signal() - def __init__(self, model: LogFileModel): + def __init__(self, model: LogFileModel, show_range_slider=True): super(BigText, self).__init__() self.model = model @@ -85,14 +86,27 @@ class BigText(QWidget): self.v_scroll_bar.scaledValueChanged.connect(self.big_text.v_scroll_event) self.v_scroll_bar.scrolled_to_end.connect(self.big_text.v_scroll_update_follow_tail) - self.grid.addWidget(self.big_text, 0, 0) - self.grid.addWidget(self.h_scroll_bar, 1, 0) - self.grid.addWidget(self.v_scroll_bar, 0, 1) + self.range_limit = RangeSlider() + self.range_limit.value_changed.connect(self._range_limit_event) + + if show_range_slider: + self.grid.addWidget(self.range_limit, 0, 0) + self.grid.addWidget(self.big_text, 0, 1) + self.grid.addWidget(self.h_scroll_bar, 1, 1) + self.grid.addWidget(self.v_scroll_bar, 0, 2) self.watchdog = FileWatchdogThread(self, model.get_file()) QThreadPool.globalInstance().start(self.watchdog) self.trigger_update.connect(self.big_text._file_changed) + def _range_limit_event(self, lower_value: float, upper_value: float): + byte_count = self.model.byte_count() + range_start = int(byte_count * (lower_value / 100.0)) + range_end = int(byte_count * (upper_value / 100.0)) + print(f"-> {lower_value}, {upper_value}, {range_start}, {range_end}") + self.v_scroll_bar.set_range(range_start, range_end) + self.big_text.set_range(range_start, range_end) + def get_file(self): return self.model.get_file() @@ -121,6 +135,9 @@ class InnerBigText(QWidget): scroll_lines = 0 longest_line = 0 + _range_start = 0 + _range_end = -1 + def __init__(self, parent: BigText, model: LogFileModel): super(InnerBigText, self).__init__() self.char_height = None @@ -148,6 +165,15 @@ class InnerBigText(QWidget): self.line_click_listeners: [Callable[[int], None]] = [] + def set_range(self, range_start: int, range_end: int): + self._range_start = range_start + self._range_end = range_end + self._set_byte_offset(self._byte_offset) + + def _set_byte_offset(self, byte_offset: int): + self._byte_offset = min(max(byte_offset, self._range_start), self._range_end) + self.update() + def clear_selection_highlight(self): self.selection_highlight.start_byte = 0 self.selection_highlight.end_byte = 0 @@ -432,7 +458,8 @@ class InnerBigText(QWidget): lines_to_show = math.ceil(self.lines_shown()) # print("%s / %s = %s" %(self.height(), float(self.char_height), lines_to_show)) - self.lines = self.model.data(self._byte_offset, self.scroll_lines, lines_to_show) + self.lines = self.model.data(self._byte_offset, self.scroll_lines, lines_to_show, self._range_start, + self._range_end) # print("lines_to_show: %d returned: %d" % (lines_to_show, len(self.lines))) self.scroll_lines = 0 self._byte_offset = self.lines[0].byte_offset() if len(self.lines) > 0 else 0 diff --git a/src/ui/bigtext/logFileModel.py b/src/ui/bigtext/logFileModel.py index 1cdf762..6d06b98 100644 --- a/src/ui/bigtext/logFileModel.py +++ b/src/ui/bigtext/logFileModel.py @@ -73,7 +73,7 @@ class LogFileModel: offset = new_offset def read_word_at(self, byte_offset: int) -> (str, int, int): - lines = self.data(byte_offset, 0, 1) + lines = self.data(byte_offset, 0, 1, 0, -1) if len(lines) == 0: return "", -1, -1 line: Line = lines[0] @@ -99,7 +99,7 @@ class LogFileModel: def _is_word_char(self, char: str) -> bool: return re.match(r"\w", char) is not None - def data(self, byte_offset: int, scroll_lines: int, lines: int) -> List[Line]: + def data(self, byte_offset: int, scroll_lines: int, lines: int, range_start: int, range_end: int) -> List[Line]: # print("data(%s, %s, %s)" % (byte_offset, scroll_lines, lines)) lines_before_offset: List[Line] = [] lines_after_offset: List[Line] = [] @@ -107,19 +107,23 @@ class LogFileModel: lines_to_return = math.ceil(lines) # TODO handle lines longer than 4096 bytes - # TODO abort file open after a few secons: https://docs.python.org/3/library/signal.html#example + # TODO abort file open after a few seconds: https://docs.python.org/3/library/signal.html#example with open(self._file, 'rb') as f: offset = min(byte_offset, self.byte_count()) # print("offset: %s byte_count: %d" % (offset, self.byte_count())) - offset = max(0, offset - self.settings.max_line_length()) + offset = max(0, + max(range_start - self.settings.max_line_length(), offset - self.settings.max_line_length())) f.seek(offset) while l := f.readline(): new_offset = f.tell() + if 0 <= range_end < new_offset: + break line = Line(offset, new_offset, l.decode("utf8", errors="ignore")) if line.byte_end() <= byte_offset: # line.byte_end() returns the end byte +1 - lines_before_offset.append(line) + if line.byte_offset() >= range_start: # only add if in range + lines_before_offset.append(line) else: lines_after_offset.append(line) offset = f.tell() diff --git a/src/ui/bigtext/testlogfilemodel.py b/src/ui/bigtext/testlogfilemodel.py index e6556ff..c2276c8 100644 --- a/src/ui/bigtext/testlogfilemodel.py +++ b/src/ui/bigtext/testlogfilemodel.py @@ -23,27 +23,27 @@ class TestLogFileModel(unittest.TestCase): def test_load_from_beginning(self): self.write_str("1\n2\n3\n4\n5\n6\n7\n") - expected_lines = ["1", "2", "3", "4", "5"] + expected_lines = ["1\n", "2\n", "3\n", "4\n", "5\n"] - lines = self.model.data(0, 0, 5) + lines = self.model.data(0, 0, 5, 0, -1) line_str = [l.line() for l in lines] self.assertEqual(expected_lines, line_str) def test_load_from_middle_of_first_line(self): self.write_str("abc\ndef\nghi\njkl") - expected_lines = ["abc", "def", "ghi"] + expected_lines = ["abc\n", "def\n", "ghi\n"] - lines = self.model.data(1, 0, 3) + lines = self.model.data(1, 0, 3, 0, -1) line_str = [l.line() for l in lines] self.assertEqual(expected_lines, line_str) def test_read_from_newline_character(self): self.write_str("abc\ndef\nghi\njkl") - expected_lines = ["abc", "def"] + expected_lines = ["abc\n", "def\n"] - lines = self.model.data(3, 0, 2) + lines = self.model.data(3, 0, 2, 0, -1) line_str = [l.line() for l in lines] self.assertEqual(expected_lines, line_str) @@ -51,18 +51,18 @@ class TestLogFileModel(unittest.TestCase): def test_negative_byte_offset(self): self.write_str("abc\ndef\nghi\njkl") - expected_lines = ["abc","def"] + expected_lines = ["abc\n", "def\n"] - lines = self.model.data(-1, 0, 2) + lines = self.model.data(-1, 0, 2, 0, -1) line_str = [l.line() for l in lines] self.assertEqual(expected_lines, line_str) def test_empty_last_line_is_ignored(self): self.write_str("1\n") - expected_lines = ["1"] + expected_lines = ["1\n"] - lines = self.model.data(0, 0, 5) + lines = self.model.data(0, 0, 5, 0, -1) line_str = [l.line() for l in lines] self.assertEqual(expected_lines, line_str) @@ -73,9 +73,9 @@ class TestLogFileModel(unittest.TestCase): Returns all three lines. """ self.write_str("abc\ndef\nghi") - expected_lines = ["abc", "def", "ghi"] + expected_lines = ["abc\n", "def\n", "ghi"] - lines = self.model.data(0, 0, 4) + lines = self.model.data(0, 0, 4, 0, -1) line_str = [l.line() for l in lines] self.assertEqual(expected_lines, line_str) @@ -88,9 +88,9 @@ class TestLogFileModel(unittest.TestCase): """ text = "abc\ndef\nghi" self.write_str(text) - expected_lines = ["abc", "def", "ghi"] + expected_lines = ["abc\n", "def\n", "ghi"] - lines = self.model.data(text.index("e"), 0, 4) + lines = self.model.data(text.index("e"), 0, 4, 0, -1) line_str = [l.line() for l in lines] self.assertEqual(expected_lines, line_str) @@ -101,9 +101,9 @@ class TestLogFileModel(unittest.TestCase): """ text = "0___\n1___\n2___\n3___\n4___\n5___" self.write_str(text) - expected_lines = ["3___", "4___"] + expected_lines = ["3___\n", "4___\n"] - lines = self.model.data(text.index("1___"), 2, 2) + lines = self.model.data(text.index("1___"), 2, 2, 0, -1) line_str = [l.line() for l in lines] self.assertEqual(expected_lines, line_str) @@ -116,9 +116,9 @@ class TestLogFileModel(unittest.TestCase): """ text = "0___\n1___\n2___\n3___\n4___\n5___" self.write_str(text) - expected_lines = ["3___","4___", "5___"] + expected_lines = ["3___\n", "4___\n", "5___"] - lines = self.model.data(text.index("1___"), 2, 2.1) + lines = self.model.data(text.index("1___"), 2, 2.1, 0, -1) line_str = [l.line() for l in lines] self.assertEqual(expected_lines, line_str) @@ -131,9 +131,9 @@ class TestLogFileModel(unittest.TestCase): """ text = "0___\n1___\n2___\n3___\n4___\n5___" self.write_str(text) - expected_lines = ["0___", "1___"] + expected_lines = ["0___\n", "1___\n"] - lines = self.model.data(5, -2, 2) + lines = self.model.data(5, -2, 2, 0, -1) line_str = [l.line() for l in lines] self.assertEqual(expected_lines, line_str)