from PyQt5 import QtWidgets, QtGui, QtCore from PyQt5.QtCore import Qt class _Bar(QtWidgets.QWidget): clickedValue = QtCore.pyqtSignal(int) def __init__(self, steps, *args, **kwargs): super().__init__(*args, **kwargs) self.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding) if isinstance(steps, list): # list of colors self.n_steps = len(steps) self.steps = steps elif isinstance(steps, int): self.n_steps = steps self.steps = ['red'] * steps else: raise TypeError('step must be a list or int') self._bar_solid_parent = 0.8 self._background_color = QtGui.QColor('black') self._padding = 4.0 def sizeHint(self): return QtCore.QSize(40, 120) def paintEvent(self, e): painter = QtGui.QPainter(self) brush = QtGui.QBrush() brush.setColor(self._background_color) brush.setStyle(Qt.SolidPattern) rect = QtCore.QRect(0, 0, painter.device().width(), painter.device().height()) painter.fillRect(rect, brush) # Get current state. dial = self.parent()._dial vmin, vmax = dial.minimum(), dial.maximum() value = dial.value() pc = (value - vmin) / (vmax - vmin) n_steps_to_draw = int(pc * self.n_steps) # Define our canvas padding = 5 d_height = painter.device().height() - (padding * 2) d_width = painter.device().width() - (padding * 2) # Draw the bars step_size = d_height / self.n_steps bar_height = int(step_size * self._bar_solid_parent) bar_spacer = int(step_size * self._bar_solid_parent / 2) for n in range(n_steps_to_draw): brush.setColor(QtGui.QColor(self.steps[n])) rect = QtCore.QRect( padding, int(padding + d_height - ((n + 1) * step_size) + bar_spacer), d_width, bar_height) painter.fillRect(rect, brush) def _trigger_refresh(self): self.update() def _calculate_clicked_value(self, e): parent = self.parent() vmin, vmax = parent.minimum(), parent.maximum() d_height = self.size().height() - (self._padding * 2) step_size = d_height / self.n_steps click_y = e.y() - self._padding - step_size / 2 pc = (d_height - click_y) / d_height value = vmin + pc * (vmax - vmin) self.clickedValue.emit(value) def mouseMoveEvent(self, e: QtGui.QMouseEvent): self._calculate_clicked_value(e) def mouseReleaseEvent(self, e: QtGui.QMouseEvent): self._calculate_clicked_value(e) class PowerBar(QtWidgets.QWidget): """ Custom Qt Widget to show a power bar and dial. Demonstrating compound and custom-drawn widget. Left-clicking the button shows the color-chooser, while right-clicking resets the color tho None (no color). """ colorChanged = QtCore.pyqtSignal() def __init__(self, steps=5, *args, **kwargs): super(PowerBar, self).__init__(*args, **kwargs) layout = QtWidgets.QVBoxLayout() self._bar = _Bar(steps) layout.addWidget(self._bar) # Create the QDial widget and set up defaults. self._dial = QtWidgets.QDial() self._dial.setNotchesVisible(True) self._dial.setWrapping(False) self._dial.valueChanged.connect(self._bar._trigger_refresh) self._bar.clickedValue.connect(self._dial.setValue) layout.addWidget(self._dial) self.setLayout(layout) def __getattr__(self, name): if name in self.__dict__: return self[name] return getattr(self._dial, name) def setColor(self, color): self._bar.steps = [color] * self._bar.n_steps self._bar.update() def setColors(self, colors): self._bar.n_steps = len(colors) self._bar.steps = colors self._bar.update() def setBarPadding(self, i): self._bar._padding = int(i) self._bar.update() def setBarSolidPercent(self, f): self._bar._bar_solid_percent = float(f) self._bar.update() def setBackgroundColor(self, color): self._bar._background_color = QtGui.QColor(color) self._bar.update() if __name__ == '__main__': app = QtWidgets.QApplication([]) volume = PowerBar() volume.show() app.exec_()