diff --git a/.editorconfig b/.editorconfig index 797a73e..cc24cc1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -15,6 +15,6 @@ charset = utf-8 # 4 space indentation [*.py] -indent_style = tab +indent_style = space indent_size = 4 trim_trailing_whitespace=true diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index eadc693..f224d06 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -18,9 +18,9 @@ jobs: os: [ubuntu-latest, macos-latest, windows-latest] python-version: [3.8] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install Qt diff --git a/.pylintrc b/.pylintrc index caf88a7..6949bec 100644 --- a/.pylintrc +++ b/.pylintrc @@ -281,7 +281,7 @@ indent-after-paren=4 # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 # tab). -indent-string=\t +indent-string=" " # Maximum number of characters on a single line. max-line-length=160 diff --git a/example.py b/example.py index 84a53c7..41b5699 100644 --- a/example.py +++ b/example.py @@ -9,80 +9,106 @@ from qt_range_slider import QtRangeSlider + def _size(size): - print(f'size: {size}') - if size < 0: - print('size is negative, resetting to 0') - size = 0 - return humanize.naturalsize(FileSize(size)) + print(f"size: {size}") + if size < 0: + print("size is negative, resetting to 0") + size = 0 + return humanize.naturalsize(FileSize(size)) + # pylint: disable=too-many-arguments -def _render_only_slider(layout, min_value = 0, max_value = 10): - inner_layout = QHBoxLayout() - slider = QtRangeSlider(layout.parent(), min_value, max_value) - slider.setMinimumWidth(500) - inner_layout.addWidget(slider) - - layout.addLayout(inner_layout) - -def _render_slider_with_labels(layout, min_value = 0, max_value = 10, \ - left_thumb_value=0, right_thumb_value=None, size_value=False): - inner_layout = QHBoxLayout() - - label_min = QLabel(_size(left_thumb_value) if size_value else str(left_thumb_value)) - label_min.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter) - label_min.setFixedWidth(70) - _right_thumb_value = right_thumb_value if right_thumb_value else max_value - label_max = QLabel(_size(_right_thumb_value) if size_value else str(_right_thumb_value)) - label_max.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter) - label_max.setFixedWidth(70) - slider = QtRangeSlider(layout.parent(), min_value, max_value, left_thumb_value, right_thumb_value) - slider.setMinimumWidth(500) - inner_layout.addWidget(label_min) - inner_layout.addWidget(slider) - inner_layout.addWidget(label_max) - layout.addLayout(inner_layout) - - # connect signal-slots - if not size_value: - slider.left_thumb_value_changed.connect(label_min.setNum) - slider.right_thumb_value_changed.connect(label_max.setNum) - return slider - - slider.left_thumb_value_changed.connect(lambda x: label_min.setText(_size(x))) - slider.right_thumb_value_changed.connect(lambda x: label_max.setText(_size(x))) - return slider +def _render_only_slider(layout, min_value=0, max_value=10): + inner_layout = QHBoxLayout() + slider = QtRangeSlider(layout.parent(), min_value, max_value) + slider.setMinimumWidth(500) + inner_layout.addWidget(slider) + + layout.addLayout(inner_layout) + + +def _render_slider_with_labels( + layout, + min_value=0, + max_value=10, + left_thumb_value=0, + right_thumb_value=None, + size_value=False, +): + inner_layout = QHBoxLayout() + + label_min = QLabel(_size(left_thumb_value) if size_value else str(left_thumb_value)) + label_min.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter) + label_min.setFixedWidth(70) + _right_thumb_value = right_thumb_value if right_thumb_value else max_value + label_max = QLabel( + _size(_right_thumb_value) if size_value else str(_right_thumb_value) + ) + label_max.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter) + label_max.setFixedWidth(70) + slider = QtRangeSlider( + layout.parent(), min_value, max_value, left_thumb_value, right_thumb_value + ) + slider.setMinimumWidth(500) + inner_layout.addWidget(label_min) + inner_layout.addWidget(slider) + inner_layout.addWidget(label_max) + layout.addLayout(inner_layout) + + # connect signal-slots + if not size_value: + slider.left_thumb_value_changed.connect(label_min.setNum) + slider.right_thumb_value_changed.connect(label_max.setNum) + return slider + + slider.left_thumb_value_changed.connect(lambda x: label_min.setText(_size(x))) + slider.right_thumb_value_changed.connect(lambda x: label_max.setText(_size(x))) + return slider def main(): - app = QApplication(sys.argv) - - logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) - - main_window = QWidget() - main_window.setMinimumSize(640, 480) - layout = QVBoxLayout(main_window) - - min_value = 0 - max_value = 10 - _render_only_slider(layout, min_value, max_value) - _render_slider_with_labels(layout, min_value, max_value) - _render_slider_with_labels(layout, min_value, max_value, min_value + 1, \ - max_value - 1) - min_value = 0 - max_value = 10*1024*1024*1024 - _render_slider_with_labels(layout, min_value, max_value, max_value // 3, \ - max_value * 3 // 4, size_value=True) - - slider = _render_slider_with_labels(layout, min_value, max_value, max_value // 3, \ - max_value * 3 // 4, size_value=True) - slider.setMinimumWidth(500) - slider.set_ticks_count(10) - - main_window.show() - - sys.exit(app.exec()) + app = QApplication(sys.argv) + + logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) + + main_window = QWidget() + main_window.setMinimumSize(640, 480) + layout = QVBoxLayout(main_window) + + min_value = 0 + max_value = 10 + _render_only_slider(layout, min_value, max_value) + _render_slider_with_labels(layout, min_value, max_value) + _render_slider_with_labels( + layout, min_value, max_value, min_value + 1, max_value - 1 + ) + min_value = 0 + max_value = 10 * 1024 * 1024 * 1024 + _render_slider_with_labels( + layout, + min_value, + max_value, + max_value // 3, + max_value * 3 // 4, + size_value=True, + ) + + slider = _render_slider_with_labels( + layout, + min_value, + max_value, + max_value // 3, + max_value * 3 // 4, + size_value=True, + ) + slider.setMinimumWidth(500) + slider.set_ticks_count(10) + + main_window.show() + + sys.exit(app.exec()) if __name__ == "__main__": - main() + main() diff --git a/qt_range_slider/__init__.py b/qt_range_slider/__init__.py index 7b8c895..5a84e4f 100644 --- a/qt_range_slider/__init__.py +++ b/qt_range_slider/__init__.py @@ -5,256 +5,291 @@ from dataclasses import dataclass from PyQt6.QtCore import Qt, QRect, QSize, pyqtSignal -from PyQt6.QtWidgets import (QWidget, QSizePolicy) +from PyQt6.QtWidgets import QWidget, QSizePolicy from PyQt6.QtGui import QPainter, QBrush, QColor, QPalette def _left_thumb_adjuster(value, min_value): - value = max(value, min_value) + value = max(value, min_value) + def _right_thumb_adjuster(value, max_value): - value = min(value, max_value) + value = min(value, max_value) + + +def _set_painter_pen_color(painter, pen_color): + pen = painter.pen() + pen.setColor(pen_color) + painter.setPen(pen) + @dataclass class Thumb: - """Thumb class which holds information about a thumb. - """ - value: int - rect: QRect - pressed: bool + """Thumb class which holds information about a thumb.""" + + value: int + rect: QRect + pressed: bool + class QtRangeSlider(QWidget): - """ - QtRangeSlider is a class which implements a slider with 2 thumbs. - - Methods - - * __init__ (self, QWidget parent, left_value, right_value, left_thumb_value=0, right_thumb_value=None) - * set_left_thumb_value (self, int value): - * set_right_thumb_value (self, int value): - * (int) get_left_thumb_value (self): - * (int) get_right_thumb_value (self): - - Signals - - * left_thumb_value_changed (int) - * right_thumb_value_changed (int) - - """ - HEIGHT = 30 - WIDTH = 120 - THUMB_WIDTH = 16 - THUMB_HEIGHT = 16 - TRACK_HEIGHT = 3 - TRACK_COLOR = QColor(0xc7, 0xc7, 0xc7) - TRACK_FILL_COLOR = QColor(0x01, 0x81, 0xff) - TRACK_PADDING = THUMB_WIDTH // 2 + 5 - TICK_PADDING = 5 - - left_thumb_value_changed = pyqtSignal('unsigned long long') - right_thumb_value_changed = pyqtSignal('unsigned long long') - - def __init__(self, parent, left_value, right_value, left_thumb_value=0, right_thumb_value=None): - super().__init__(parent) - - self.setSizePolicy( - QSizePolicy.Policy.Expanding, - QSizePolicy.Policy.Fixed - ) - self.setMinimumWidth(self.WIDTH) - self.setMinimumHeight(self.HEIGHT) - - self._left_value = left_value - self._right_value = right_value - - self._left_thumb = Thumb(left_thumb_value, None, False) - _right_thumb_value = right_thumb_value if right_thumb_value is not None \ - else self._right_value - if _right_thumb_value < left_thumb_value + 1: - raise ValueError("Right thumb value is less or equal left thumb value.") - self._right_thumb = Thumb(_right_thumb_value, None, False) - - self._canvas_width = None - self._canvas_height = None - - self._ticks_count = 0 - - parent_palette = parent.palette() - self._background_color = parent_palette.color(QPalette.ColorRole.Window) - self._base_color = parent_palette.color(QPalette.ColorRole.Base) - self._button_color = parent_palette.color(QPalette.ColorRole.Button) - self._border_color = parent_palette.color(QPalette.ColorRole.Mid) - - def paintEvent(self, unused_e): - logging.debug("paintEvent") - del unused_e - painter = QPainter(self) - painter.setRenderHint(QPainter.RenderHint.Antialiasing) - - self.__draw_track(self._canvas_width, self._canvas_height, painter) - self.__draw_track_fill(self._canvas_width, self._canvas_height, painter) - self.__draw_ticks(self._canvas_width, self._canvas_height, painter, self._ticks_count) - self.__draw_left_thumb(self._canvas_width, self._canvas_height, painter) - self.__draw_right_thumb(self._canvas_width, self._canvas_height, painter) - - painter.end() - - def __get_track_y_position(self): - return self._canvas_height // 2 - self.TRACK_HEIGHT // 2 - - def __draw_track(self, canvas_width, canvas_height, painter): - del canvas_height - brush = QBrush() - brush.setColor(self.TRACK_COLOR) - brush.setStyle(Qt.BrushStyle.SolidPattern) - - rect = QRect(self.TRACK_PADDING, self.__get_track_y_position(), \ - canvas_width - 2 * self.TRACK_PADDING, self.TRACK_HEIGHT) - painter.fillRect(rect, brush) - - def __draw_track_fill(self, canvas_width, canvas_height, painter): - del canvas_height - brush = QBrush() - brush.setColor(self.TRACK_FILL_COLOR) - brush.setStyle(Qt.BrushStyle.SolidPattern) - - available_width = canvas_width - 2 * self.TRACK_PADDING - x1 = self._left_thumb.value / self._right_value * available_width + self.TRACK_PADDING - x2 = self._right_thumb.value / self._right_value * available_width + self.TRACK_PADDING - rect = QRect(round(x1), self.__get_track_y_position(), \ - round(x2) - round(x1), self.TRACK_HEIGHT) - painter.fillRect(rect, brush) - - # pylint: disable=no-self-use - def __set_painter_pen_color(self, painter, pen_color): - pen = painter.pen() - pen.setColor(pen_color) - painter.setPen(pen) - - def __draw_thumb(self, x, y, painter): - brush = QBrush() - brush.setColor(self._base_color) - brush.setStyle(Qt.BrushStyle.SolidPattern) - - self.__set_painter_pen_color(painter, self._border_color) - - painter.setBrush(brush) - - thumb_rect = QRect(round(x) - self.THUMB_WIDTH // 2 + self.TRACK_PADDING, \ - y + self.TRACK_HEIGHT // 2 - self.THUMB_HEIGHT // 2, self.THUMB_WIDTH, self.THUMB_HEIGHT) - painter.drawEllipse(thumb_rect) - return thumb_rect - - def __draw_right_thumb(self, canvas_width, canvas_height, painter): - del canvas_height - available_width = canvas_width - 2 * self.TRACK_PADDING - x = self._right_thumb.value / self._right_value * available_width - y = self.__get_track_y_position() - self._right_thumb.rect = self.__draw_thumb(x, y, painter) - - def __draw_left_thumb(self, canvas_width, canvas_height, painter): - del canvas_height - available_width = canvas_width - 2 * self.TRACK_PADDING - x = round(self._left_thumb.value / self._right_value * available_width) - y = self.__get_track_y_position() - self._left_thumb.rect = self.__draw_thumb(x, y, painter) - - def set_left_thumb_value(self, value): - if value < 0 or value > self._right_thumb.value - 1: - return - if value == self._left_thumb.value: - # nothing to update - return - self._left_thumb.value = value - # pylint: disable=logging-fstring-interpolation - logging.debug(f"value before emit {value}") - self.left_thumb_value_changed.emit(value) - self.repaint() - - def set_right_thumb_value(self, value): - if value > self._right_value or value < self._left_thumb.value + 1: - return - if value == self._right_thumb.value: - # nothing to update - return - self._right_thumb.value = value - # pylint: disable=logging-fstring-interpolation - logging.debug(f"value before emit {value}") - self.right_thumb_value_changed.emit(value) - self.repaint() - - # override Qt event - def mousePressEvent(self, event): - logging.debug("mousePressEvent") - position = event.position() - if self._left_thumb.rect.contains(int(position.x()), int(position.y())): - self._left_thumb.pressed = True - if self._right_thumb.rect.contains(int(position.x()), int(position.y())): - self._right_thumb.pressed = True - super().mousePressEvent(event) - - # override Qt event - def mouseReleaseEvent(self, event): - logging.debug("mouseReleaseEvent") - self._left_thumb.pressed = False - self._right_thumb.pressed = False - super().mouseReleaseEvent(event) - - # pylint: disable=no-self-use - def __get_thumb_value(self, x, canvas_width, right_value): - # pylint: disable=logging-fstring-interpolation - logging.debug(f"x {x} canvas_width {canvas_width} left_value {self._left_thumb.value} right_value {right_value}") - return round(x / canvas_width * right_value) - - # override Qt event - def mouseMoveEvent(self, event): - logging.debug("mouseMoveEvent") - - thumb = self._left_thumb if self._left_thumb.pressed else self._right_thumb - - if thumb.pressed: - if thumb == self._left_thumb: - value_setter = self.set_left_thumb_value - value_adjuster = lambda val: _left_thumb_adjuster(val, 0) - else: - value_setter = self.set_right_thumb_value - value_adjuster = lambda val: _right_thumb_adjuster(val, self._right_value) - - new_val = self.__get_thumb_value(event.position().x(), self._canvas_width, self._right_value) - value_adjuster(new_val) - value_changed = new_val != thumb.value - if value_changed: - value_setter(new_val) - - super().mouseMoveEvent(event) - - def get_left_thumb_value(self): - return self._left_thumb.value - - def get_right_thumb_value(self): - return self._right_thumb.value - - def set_ticks_count(self, count): - if count < 0: - raise ValueError("Invalid ticks count.") - self._ticks_count = count - - def __draw_ticks(self, canvas_width, canvas_height, painter, ticks_count): - del canvas_height - if not self._ticks_count: - return - - self.__set_painter_pen_color(painter, self._border_color) - - tick_step = (canvas_width - 2 * self.TRACK_PADDING) // ticks_count - y1 = self.__get_track_y_position() - self.TICK_PADDING - y2 = y1 - self.THUMB_HEIGHT // 2 - for x in range(0, ticks_count + 1): - x = x * tick_step + self.TRACK_PADDING - painter.drawLine(x, y1, x, y2) - - def resizeEvent(self, event): - logging.debug("resizeEvent") - del event - self._canvas_width = self.width() - self._canvas_height = self.height() + """ + QtRangeSlider is a class which implements a slider with 2 thumbs. + + Methods + + * __init__ (self, QWidget parent, left_value, right_value, left_thumb_value=0, right_thumb_value=None) + * set_left_thumb_value (self, int value): + * set_right_thumb_value (self, int value): + * (int) get_left_thumb_value (self): + * (int) get_right_thumb_value (self): + + Signals + + * left_thumb_value_changed (int) + * right_thumb_value_changed (int) + + """ + + HEIGHT = 30 + WIDTH = 120 + THUMB_WIDTH = 16 + THUMB_HEIGHT = 16 + TRACK_HEIGHT = 3 + TRACK_COLOR = QColor(0xC7, 0xC7, 0xC7) + TRACK_FILL_COLOR = QColor(0x01, 0x81, 0xFF) + TRACK_PADDING = THUMB_WIDTH // 2 + 5 + TICK_PADDING = 5 + + left_thumb_value_changed = pyqtSignal("unsigned long long") + right_thumb_value_changed = pyqtSignal("unsigned long long") + + def __init__( + self, + parent, + left_value, + right_value, + left_thumb_value=0, + right_thumb_value=None, + ): + super().__init__(parent) + + self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed) + self.setMinimumWidth(self.WIDTH) + self.setMinimumHeight(self.HEIGHT) + + self._left_value = left_value + self._right_value = right_value + + self._left_thumb = Thumb(left_thumb_value, None, False) + _right_thumb_value = ( + right_thumb_value if right_thumb_value is not None else self._right_value + ) + if _right_thumb_value < left_thumb_value + 1: + raise ValueError("Right thumb value is less or equal left thumb value.") + self._right_thumb = Thumb(_right_thumb_value, None, False) + + self._canvas_width = None + self._canvas_height = None + + self._ticks_count = 0 + + parent_palette = parent.palette() + self._background_color = parent_palette.color(QPalette.ColorRole.Window) + self._base_color = parent_palette.color(QPalette.ColorRole.Base) + self._button_color = parent_palette.color(QPalette.ColorRole.Button) + self._border_color = parent_palette.color(QPalette.ColorRole.Mid) + + def paintEvent(self, unused_e): + logging.debug("paintEvent") + del unused_e + painter = QPainter(self) + painter.setRenderHint(QPainter.RenderHint.Antialiasing) + + self.__draw_track(self._canvas_width, self._canvas_height, painter) + self.__draw_track_fill(self._canvas_width, self._canvas_height, painter) + self.__draw_ticks( + self._canvas_width, self._canvas_height, painter, self._ticks_count + ) + self.__draw_left_thumb(self._canvas_width, self._canvas_height, painter) + self.__draw_right_thumb(self._canvas_width, self._canvas_height, painter) + + painter.end() + + def __get_track_y_position(self): + return self._canvas_height // 2 - self.TRACK_HEIGHT // 2 + + def __draw_track(self, canvas_width, canvas_height, painter): + del canvas_height + brush = QBrush() + brush.setColor(self.TRACK_COLOR) + brush.setStyle(Qt.BrushStyle.SolidPattern) + + rect = QRect( + self.TRACK_PADDING, + self.__get_track_y_position(), + canvas_width - 2 * self.TRACK_PADDING, + self.TRACK_HEIGHT, + ) + painter.fillRect(rect, brush) + + def __draw_track_fill(self, canvas_width, canvas_height, painter): + del canvas_height + brush = QBrush() + brush.setColor(self.TRACK_FILL_COLOR) + brush.setStyle(Qt.BrushStyle.SolidPattern) + + available_width = canvas_width - 2 * self.TRACK_PADDING + x1 = ( + self._left_thumb.value / self._right_value * available_width + + self.TRACK_PADDING + ) + x2 = ( + self._right_thumb.value / self._right_value * available_width + + self.TRACK_PADDING + ) + rect = QRect( + round(x1), + self.__get_track_y_position(), + round(x2) - round(x1), + self.TRACK_HEIGHT, + ) + painter.fillRect(rect, brush) + + def __draw_thumb(self, x, y, painter): + brush = QBrush() + brush.setColor(self._base_color) + brush.setStyle(Qt.BrushStyle.SolidPattern) + + _set_painter_pen_color(painter, self._border_color) + + painter.setBrush(brush) + + thumb_rect = QRect( + round(x) - self.THUMB_WIDTH // 2 + self.TRACK_PADDING, + y + self.TRACK_HEIGHT // 2 - self.THUMB_HEIGHT // 2, + self.THUMB_WIDTH, + self.THUMB_HEIGHT, + ) + painter.drawEllipse(thumb_rect) + return thumb_rect + + def __draw_right_thumb(self, canvas_width, canvas_height, painter): + del canvas_height + available_width = canvas_width - 2 * self.TRACK_PADDING + x = self._right_thumb.value / self._right_value * available_width + y = self.__get_track_y_position() + self._right_thumb.rect = self.__draw_thumb(x, y, painter) + + def __draw_left_thumb(self, canvas_width, canvas_height, painter): + del canvas_height + available_width = canvas_width - 2 * self.TRACK_PADDING + x = round(self._left_thumb.value / self._right_value * available_width) + y = self.__get_track_y_position() + self._left_thumb.rect = self.__draw_thumb(x, y, painter) + + def set_left_thumb_value(self, value): + if value < 0 or value > self._right_thumb.value - 1: + return + if value == self._left_thumb.value: + # nothing to update + return + self._left_thumb.value = value + # pylint: disable=logging-fstring-interpolation + logging.debug(f"value before emit {value}") + self.left_thumb_value_changed.emit(value) + self.repaint() + + def set_right_thumb_value(self, value): + if value > self._right_value or value < self._left_thumb.value + 1: + return + if value == self._right_thumb.value: + # nothing to update + return + self._right_thumb.value = value + # pylint: disable=logging-fstring-interpolation + logging.debug(f"value before emit {value}") + self.right_thumb_value_changed.emit(value) + self.repaint() + + # override Qt event + def mousePressEvent(self, event): + logging.debug("mousePressEvent") + position = event.position() + if self._left_thumb.rect.contains(int(position.x()), int(position.y())): + self._left_thumb.pressed = True + if self._right_thumb.rect.contains(int(position.x()), int(position.y())): + self._right_thumb.pressed = True + super().mousePressEvent(event) + + # override Qt event + def mouseReleaseEvent(self, event): + logging.debug("mouseReleaseEvent") + self._left_thumb.pressed = False + self._right_thumb.pressed = False + super().mouseReleaseEvent(event) + + # pylint: disable=no-self-use + def __get_thumb_value(self, x, canvas_width, right_value): + # pylint: disable=logging-fstring-interpolation + logging.debug( + f"x {x} canvas_width {canvas_width} left_value {self._left_thumb.value} right_value {right_value}" + ) + return round(x / canvas_width * right_value) + + # override Qt event + def mouseMoveEvent(self, event): + logging.debug("mouseMoveEvent") + + thumb = self._left_thumb if self._left_thumb.pressed else self._right_thumb + + if thumb.pressed: + if thumb == self._left_thumb: + value_setter = self.set_left_thumb_value + value_adjuster = lambda val: _left_thumb_adjuster(val, 0) + else: + value_setter = self.set_right_thumb_value + value_adjuster = lambda val: _right_thumb_adjuster( + val, self._right_value + ) + + new_val = self.__get_thumb_value( + event.position().x(), self._canvas_width, self._right_value + ) + value_adjuster(new_val) + value_changed = new_val != thumb.value + if value_changed: + value_setter(new_val) + + super().mouseMoveEvent(event) + + def get_left_thumb_value(self): + return self._left_thumb.value + + def get_right_thumb_value(self): + return self._right_thumb.value + + def set_ticks_count(self, count): + if count < 0: + raise ValueError("Invalid ticks count.") + self._ticks_count = count + + def __draw_ticks(self, canvas_width, canvas_height, painter, ticks_count): + del canvas_height + if not self._ticks_count: + return + + _set_painter_pen_color(painter, self._border_color) + + tick_step = (canvas_width - 2 * self.TRACK_PADDING) // ticks_count + y1 = self.__get_track_y_position() - self.TICK_PADDING + y2 = y1 - self.THUMB_HEIGHT // 2 + for x in range(0, ticks_count + 1): + x = x * tick_step + self.TRACK_PADDING + painter.drawLine(x, y1, x, y2) + + def resizeEvent(self, event): + logging.debug("resizeEvent") + del event + self._canvas_width = self.width() + self._canvas_height = self.height() diff --git a/tests/test_slider.py b/tests/test_slider.py index f3a76ab..ff76a5b 100644 --- a/tests/test_slider.py +++ b/tests/test_slider.py @@ -10,147 +10,169 @@ def _mouse_move(widget: QWidget, new_position: QPoint): - print(f"move mouse to ({new_position.x()}, {new_position.y()})") - event = QMouseEvent(QEvent.Type.MouseMove, QPointF(new_position), \ - Qt.MouseButton.LeftButton, Qt.MouseButton.NoButton, Qt.KeyboardModifier.NoModifier) - QApplication.sendEvent(widget, event) + print(f"move mouse to ({new_position.x()}, {new_position.y()})") + event = QMouseEvent( + QEvent.Type.MouseMove, + QPointF(new_position), + Qt.MouseButton.LeftButton, + Qt.MouseButton.NoButton, + Qt.KeyboardModifier.NoModifier, + ) + QApplication.sendEvent(widget, event) + def _draw_widget(widget: QWidget): - unused_event = QPaintEvent(QRect(0, 0, 1, 1)) - widget.resizeEvent(unused_event) - widget.paintEvent(unused_event) + unused_event = QPaintEvent(QRect(0, 0, 1, 1)) + widget.resizeEvent(unused_event) + widget.paintEvent(unused_event) + def _gb_to_bytes(gb_count): - return gb_count * 1024 ** 3 + return gb_count * 1024**3 class QtRangeSliderTest(unittest.TestCase): - """Tests for qt_range_slider - """ - @classmethod - def setUpClass(cls): - cls._app = QApplication(sys.argv) - cls._form = QMainWindow() - cls._initial_size = QSize(500, 100) - cls._form.setFixedWidth(cls._initial_size.width()) - cls._form.setFixedHeight(cls._initial_size.height()) - - def test_init(self): - slider = QtRangeSlider(QtRangeSliderTest._form, 0, 10, 3, 5) - self.assertIsNotNone(slider) - with self.assertRaises(ValueError): - QtRangeSlider(QtRangeSliderTest._form, 0, 10, 5, 3) - - # pylint: disable=no-self-use - def test_paint_event(self): - slider = QtRangeSlider(QtRangeSliderTest._form, 0, 10, 3, 5) - _draw_widget(slider) - - def test_ticks(self): - slider = QtRangeSlider(QtRangeSliderTest._form, 0, 10, 3, 5) - slider.set_ticks_count(5) - # pylint: disable=protected-access - self.assertEqual(slider._ticks_count, 5) - _draw_widget(slider) - - - def test_thumb_values(self): - slider = QtRangeSlider(QtRangeSliderTest._form, 0, 10) - slider.set_left_thumb_value(3) - # pylint: disable=protected-access - self.assertEqual(slider._left_thumb.value, 3) - slider.set_left_thumb_value(5) - # pylint: disable=protected-access - self.assertEqual(slider._left_thumb.value, 5) - - def test_change_size_while_dragging(self): - slider = QtRangeSlider(QtRangeSliderTest._form, 0, _gb_to_bytes(10), \ - _gb_to_bytes(3), _gb_to_bytes(5)) - slider.setMouseTracking(True) - _draw_widget(slider) - # pylint: disable=protected-access - left_thumb_position = slider._left_thumb.rect.center() - - _mouse_move(slider, left_thumb_position) - QTest.mousePress(slider, Qt.MouseButton.LeftButton, pos=left_thumb_position) - - new_position = left_thumb_position - new_position.setX(new_position.x() - 10) - _mouse_move(slider, new_position) - - QTest.mouseRelease(slider, Qt.MouseButton.LeftButton) - self.assertEqual(slider.get_left_thumb_value(), 2684354560) - - left_thumb_position = slider._left_thumb.rect.center() - new_width = QtRangeSliderTest._initial_size.width() - 50 - QTest.mouseMove(slider, pos=left_thumb_position) - QTest.mousePress(slider, Qt.MouseButton.LeftButton, pos=left_thumb_position) - slider.setMaximumSize(QSize(new_width, QtRangeSliderTest._initial_size.height())) - _draw_widget(slider) - new_position = left_thumb_position - new_x = new_position.x() - 30 - new_position.setX(new_x) - _mouse_move(slider, new_position) - - QTest.mouseRelease(slider, Qt.MouseButton.LeftButton) - self.assertEqual(slider.get_left_thumb_value(), 894784853) - - def test_dragging_right_thumb(self): - slider = QtRangeSlider(QtRangeSliderTest._form, 0, _gb_to_bytes(10), \ - _gb_to_bytes(3), _gb_to_bytes(5)) - slider.setMouseTracking(True) - _draw_widget(slider) - # pylint: disable=protected-access - right_thumb_center = slider._right_thumb.rect.center() - - _mouse_move(slider, right_thumb_center) - QTest.mousePress(slider, Qt.MouseButton.RightButton, pos=right_thumb_center) - - right_thumb_center.setX(right_thumb_center.x() - 10) - _mouse_move(slider, right_thumb_center) - - QTest.mouseRelease(slider, Qt.MouseButton.RightButton) - self.assertEqual(slider.get_right_thumb_value(), 4384445781) - - def test_invalid_ticks_count(self): - slider = QtRangeSlider(QtRangeSliderTest._form, 0, 10) - with self.assertRaises(ValueError): - slider.set_ticks_count(-10) - - def test_set_incorrect_right_thumb_value(self): - max_value = 10 - slider = QtRangeSlider(QtRangeSliderTest._form, 0, max_value) - slider.set_right_thumb_value(max_value + 2) - self.assertEqual(slider.get_right_thumb_value(), max_value) - - def test_set_incorrect_left_thumb_value(self): - min_value = 0 - slider = QtRangeSlider(QtRangeSliderTest._form, min_value, 10) - slider.set_left_thumb_value(min_value - 2) - self.assertEqual(slider.get_left_thumb_value(), min_value) - - def test_set_left_thumb_value_greater_than_right(self): - left_thumb_value = 3 - right_thumb_value = 5 - slider = QtRangeSlider(QtRangeSliderTest._form, 0, 10, left_thumb_value, right_thumb_value) - slider.set_left_thumb_value(right_thumb_value + 2) - self.assertEqual(slider.get_left_thumb_value(), left_thumb_value) - - def test_set_right_thumb_value_less_than_left(self): - left_thumb_value = 3 - right_thumb_value = 5 - slider = QtRangeSlider(QtRangeSliderTest._form, 0, 10, left_thumb_value, right_thumb_value) - slider.set_right_thumb_value(left_thumb_value - 2) - self.assertEqual(slider.get_right_thumb_value(), right_thumb_value) - - def test_set_same_left_thumb_value(self): - left_thumb_value = 3 - slider = QtRangeSlider(QtRangeSliderTest._form, 0, 10, left_thumb_value, 5) - slider.set_left_thumb_value(left_thumb_value) - self.assertEqual(slider.get_left_thumb_value(), left_thumb_value) - - def test_set_same_right_thumb_value(self): - right_thumb_value = 5 - slider = QtRangeSlider(QtRangeSliderTest._form, 0, 10, 3, right_thumb_value) - slider.set_right_thumb_value(right_thumb_value) - self.assertEqual(slider.get_right_thumb_value(), right_thumb_value) + """Tests for qt_range_slider""" + + @classmethod + def setUpClass(cls): + cls._app = QApplication(sys.argv) + cls._form = QMainWindow() + cls._initial_size = QSize(500, 100) + cls._form.setFixedWidth(cls._initial_size.width()) + cls._form.setFixedHeight(cls._initial_size.height()) + + def test_init(self): + slider = QtRangeSlider(QtRangeSliderTest._form, 0, 10, 3, 5) + self.assertIsNotNone(slider) + with self.assertRaises(ValueError): + QtRangeSlider(QtRangeSliderTest._form, 0, 10, 5, 3) + + # pylint: disable=no-self-use + def test_paint_event(self): + slider = QtRangeSlider(QtRangeSliderTest._form, 0, 10, 3, 5) + _draw_widget(slider) + + def test_ticks(self): + slider = QtRangeSlider(QtRangeSliderTest._form, 0, 10, 3, 5) + slider.set_ticks_count(5) + # pylint: disable=protected-access + self.assertEqual(slider._ticks_count, 5) + _draw_widget(slider) + + def test_thumb_values(self): + slider = QtRangeSlider(QtRangeSliderTest._form, 0, 10) + slider.set_left_thumb_value(3) + # pylint: disable=protected-access + self.assertEqual(slider._left_thumb.value, 3) + slider.set_left_thumb_value(5) + # pylint: disable=protected-access + self.assertEqual(slider._left_thumb.value, 5) + + def test_change_size_while_dragging(self): + slider = QtRangeSlider( + QtRangeSliderTest._form, + 0, + _gb_to_bytes(10), + _gb_to_bytes(3), + _gb_to_bytes(5), + ) + slider.setMouseTracking(True) + _draw_widget(slider) + # pylint: disable=protected-access + left_thumb_position = slider._left_thumb.rect.center() + + _mouse_move(slider, left_thumb_position) + QTest.mousePress(slider, Qt.MouseButton.LeftButton, pos=left_thumb_position) + + new_position = left_thumb_position + new_position.setX(new_position.x() - 10) + _mouse_move(slider, new_position) + + QTest.mouseRelease(slider, Qt.MouseButton.LeftButton) + self.assertEqual(slider.get_left_thumb_value(), 2684354560) + + left_thumb_position = slider._left_thumb.rect.center() + new_width = QtRangeSliderTest._initial_size.width() - 50 + QTest.mouseMove(slider, pos=left_thumb_position) + QTest.mousePress(slider, Qt.MouseButton.LeftButton, pos=left_thumb_position) + slider.setMaximumSize( + QSize(new_width, QtRangeSliderTest._initial_size.height()) + ) + _draw_widget(slider) + new_position = left_thumb_position + new_x = new_position.x() - 30 + new_position.setX(new_x) + _mouse_move(slider, new_position) + + QTest.mouseRelease(slider, Qt.MouseButton.LeftButton) + self.assertEqual(slider.get_left_thumb_value(), 894784853) + + def test_dragging_right_thumb(self): + slider = QtRangeSlider( + QtRangeSliderTest._form, + 0, + _gb_to_bytes(10), + _gb_to_bytes(3), + _gb_to_bytes(5), + ) + slider.setMouseTracking(True) + _draw_widget(slider) + # pylint: disable=protected-access + right_thumb_center = slider._right_thumb.rect.center() + + _mouse_move(slider, right_thumb_center) + QTest.mousePress(slider, Qt.MouseButton.RightButton, pos=right_thumb_center) + + right_thumb_center.setX(right_thumb_center.x() - 10) + _mouse_move(slider, right_thumb_center) + + QTest.mouseRelease(slider, Qt.MouseButton.RightButton) + self.assertEqual(slider.get_right_thumb_value(), 4384445781) + + def test_invalid_ticks_count(self): + slider = QtRangeSlider(QtRangeSliderTest._form, 0, 10) + with self.assertRaises(ValueError): + slider.set_ticks_count(-10) + + def test_set_incorrect_right_thumb_value(self): + max_value = 10 + slider = QtRangeSlider(QtRangeSliderTest._form, 0, max_value) + slider.set_right_thumb_value(max_value + 2) + self.assertEqual(slider.get_right_thumb_value(), max_value) + + def test_set_incorrect_left_thumb_value(self): + min_value = 0 + slider = QtRangeSlider(QtRangeSliderTest._form, min_value, 10) + slider.set_left_thumb_value(min_value - 2) + self.assertEqual(slider.get_left_thumb_value(), min_value) + + def test_set_left_thumb_value_greater_than_right(self): + left_thumb_value = 3 + right_thumb_value = 5 + slider = QtRangeSlider( + QtRangeSliderTest._form, 0, 10, left_thumb_value, right_thumb_value + ) + slider.set_left_thumb_value(right_thumb_value + 2) + self.assertEqual(slider.get_left_thumb_value(), left_thumb_value) + + def test_set_right_thumb_value_less_than_left(self): + left_thumb_value = 3 + right_thumb_value = 5 + slider = QtRangeSlider( + QtRangeSliderTest._form, 0, 10, left_thumb_value, right_thumb_value + ) + slider.set_right_thumb_value(left_thumb_value - 2) + self.assertEqual(slider.get_right_thumb_value(), right_thumb_value) + + def test_set_same_left_thumb_value(self): + left_thumb_value = 3 + slider = QtRangeSlider(QtRangeSliderTest._form, 0, 10, left_thumb_value, 5) + slider.set_left_thumb_value(left_thumb_value) + self.assertEqual(slider.get_left_thumb_value(), left_thumb_value) + + def test_set_same_right_thumb_value(self): + right_thumb_value = 5 + slider = QtRangeSlider(QtRangeSliderTest._form, 0, 10, 3, right_thumb_value) + slider.set_right_thumb_value(right_thumb_value) + self.assertEqual(slider.get_right_thumb_value(), right_thumb_value)