Source code for display.pattern

"""
Pattern Display Widget
"""

from PySide6.QtCore import QRect, Qt
from PySide6.QtGui import QColor, QFont, QPainter, QPaintEvent, QPen
from PySide6.QtWidgets import QWidget


[docs] class PatternDisplay(QWidget): """Pattern Display Widget""" def __init__(self, parent: QWidget = None): super().__init__(parent) self.setMinimumSize(240, 120)
[docs] self.pattern_type = 0 # Default to "Up"
[docs] self.octave_range = 0
[docs] self.accent_rate = 0
[docs] def set_pattern( self, pattern_type: int, octave_range: int, accent_rate: int, ) -> None: self.pattern_type = pattern_type self.octave_range = octave_range self.accent_rate = accent_rate self.update() # Trigger repaint
[docs] def paintEvent(self, event: QPaintEvent) -> None: """Paint the pattern display. :param event: QPaintEvent """ painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) # Background painter.fillRect(self.rect(), QColor(40, 40, 40)) # Calculate dimensions with space for labels width = self.width() - 40 # More padding for note names height = self.height() - 40 # More padding for beat numbers x_start = 30 # Space for note names y_start = height + 20 # Space for beat numbers # Draw grid with labels self._draw_grid(painter, x_start, y_start, width, height) # Draw pattern points = self._get_pattern_points(x_start, y_start, width, height) self._draw_pattern(painter, points)
[docs] def _draw_grid( self, painter: QPainter, x: int, y: int, width: int, height: int ) -> None: """Draw the grid. :param painter: QPainter :param x: int :param y: int :param width: int :param height: int """ # Note names note_names = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] # Draw horizontal lines (octaves) painter.setPen(QPen(QColor(80, 80, 80), 1)) steps = (self.octave_range + 1) * 12 step_height = height / steps if steps > 0 else height # Font settings for labels painter.setFont(QFont("Sans", 8)) text_color = QColor(180, 180, 180) for i in range(steps + 1): y_pos = y - (i * step_height) # Draw line if i % 12 == 0: # Octave line painter.setPen(QPen(QColor(120, 120, 120), 1)) # Draw octave number octave = i // 12 painter.setPen(text_color) painter.drawText( QRect(x - 25, int(y_pos) - 8, 20, 16), Qt.AlignRight | Qt.AlignVCenter, f"C{octave}", ) else: painter.setPen(QPen(QColor(60, 60, 60), 1)) # Draw note name note = note_names[i % 12] if len(note) == 1: # Natural notes painter.setPen(text_color) painter.drawText( QRect(x - 25, int(y_pos) - 8, 20, 16), Qt.AlignRight | Qt.AlignVCenter, note, ) painter.setPen(QPen(QColor(60, 60, 60), 1)) painter.drawLine(x, int(y_pos), x + width, int(y_pos)) # Draw vertical lines (time divisions) steps = 16 # 16 steps for visualization step_width = width / steps for i in range(steps + 1): x_pos = x + (i * step_width) if i % 4 == 0: # Bar line painter.setPen(QPen(QColor(120, 120, 120), 1)) # Draw beat number beat = i // 4 + 1 painter.setPen(text_color) painter.drawText( QRect(int(x_pos) - 10, y + 5, 20, 16), Qt.AlignCenter, str(beat) ) else: painter.setPen(QPen(QColor(60, 60, 60), 1)) # Draw step dot painter.setPen(text_color) painter.drawText( QRect(int(x_pos) - 2, y + 5, 4, 16), Qt.AlignCenter, "ยท" ) painter.drawLine(int(x_pos), y, int(x_pos), y - height)
[docs] def _get_pattern_points(self, x: int, y: int, width: int, height: int) -> list: """Get the pattern points. :param x: int :param y: int :param width: int :param height: int """ points = [] steps = 16 step_width = width / steps # Calculate note positions based on pattern preset_type if self.pattern_type == 0: # Up notes = self._generate_up_pattern() elif self.pattern_type == 1: # Down notes = self._generate_down_pattern() elif self.pattern_type == 2: # Up/Down notes = self._generate_updown_pattern() elif self.pattern_type == 3: # Random notes = self._generate_random_pattern() elif self.pattern_type == 4: # Note Order notes = self._generate_note_order_pattern() elif self.pattern_type == 5: # Up x2 notes = self._generate_up2_pattern() elif self.pattern_type == 6: # Down x2 notes = self._generate_down2_pattern() else: # Up&Down notes = self._generate_upanddown_pattern() # Convert notes to points note_range = (self.octave_range + 1) * 12 for i, note in enumerate(notes): x_pos = x + (i * step_width) y_pos = y - (note * height / note_range if note_range > 0 else 0) points.append((int(x_pos), int(y_pos))) return points
[docs] def _draw_pattern(self, painter: QPainter, points: list) -> None: """Draw the pattern. :param painter: QPainter :param points: list """ if not points: return # Draw lines connecting points painter.setPen(QPen(QColor("#2897B7"), 2)) for i in range(len(points) - 1): painter.drawLine( points[i][0], points[i][1], points[i + 1][0], points[i + 1][1] ) # Draw points for i, point in enumerate(points): # Accented notes in orange, others in blue if (i % 4 == 0) or (self.accent_rate > 0 and i % 2 == 0 and i % 4 != 0): painter.setBrush(QColor("#FFA200")) size = 8 else: painter.setBrush(QColor("#2897B7")) size = 6 painter.drawEllipse(point[0] - size // 2, point[1] - size // 2, size, size)
# Pattern generation methods
[docs] def _generate_up_pattern(self) -> list[int]: return list(range(13)) * 2
[docs] def _generate_down_pattern(self) -> list[int]: return list(range(12, -1, -1)) * 2
[docs] def _generate_updown_pattern(self) -> list[int]: return list(range(13)) + list(range(11, -1, -1))
[docs] def _generate_random_pattern(self) -> list[int]: from random import randrange return [randrange(13) for _ in range(16)]
[docs] def _generate_note_order_pattern(self) -> list[int]: return [0, 4, 7, 12] * 4
[docs] def _generate_up2_pattern(self) -> list[int]: return [i // 2 for i in range(26)]
[docs] def _generate_down2_pattern(self) -> list[int]: return [i // 2 for i in range(24, -1, -1)]
[docs] def _generate_upanddown_pattern(self) -> list[int]: return list(range(7)) + list(range(7, -1, -1)) * 2