Dup Ver Goto 📝

CustomPaintWidgets1

To
307 lines, 829 words, 7656 chars Page 'CustomPaintWidgets1' does not exist.

Linear and Rotary controls

These are intended to show MIDI CC values, hence the default min,max of 0..127.

Linear

from PySide6.QtCore import *
from PySide6.QtGui import *
from PySide6.QtWidgets import *
from rtmidi import MidiIn, MidiOut
import sys
import math
def radians(x):
  return x*math.pi/180
def degrees(x):
  return x*180/math.pi
app = QApplication([])

# Define Widgets
class Linear(QWidget):
  def __init__(self):
    super().__init__()
    self.value = 45
    self.maxval = 127
    self.minval = 0
    self.mx = 0
    self.my = 0
  def mousePressEvent(self,e):
    self.mval = self.value
    pos = e.pos()
    self.mx = pos.x()
    self.my = pos.y()
    print(f"press {pos.x()} {pos.y()}")
  def mouseMoveEvent(self,e):
    pos = e.pos()
    dy = - (pos.y() - self.my)
    self.value = max(min(self.mval + dy,self.maxval),self.minval)
    print(f"move {pos.x()} {pos.y()} -- {dy=}")
    self.update()

  def paintEvent(self, event: QPaintEvent):
    """Override method from QWidget
    Paint the Pixmap into the widget
    """
    rect = self.rect()
    w = rect.width()
    h = rect.height()
    w1 = w*0.9
    h1 = h*0.9
    cx = w/2
    cy = h/2
    m = min(w,h)
    r = (m/2) * 0.9
    y0 = cy - h1/2

    font = QFont("Arial",r/4)
    metrics = QFontMetrics(font)
    t = str(self.value)
    twidth = metrics.horizontalAdvance(t)
    theight = metrics.capHeight()
    tx = cx-(twidth/2)
    ty = cy+(theight/2)
    svalue = (self.value-self.minval)/(self.maxval-self.minval)
    with QPainter(self) as painter:
      painter.fillRect(rect,Qt.white)
      painter.setBrush(Qt.NoBrush)
      path = QPainterPath()
      path.moveTo(QPoint(cx,y0+h1))
      path.lineTo(QPoint(cx,y0))
      painter.setPen(QPen(Qt.black, 5))
      painter.drawPath(path)
      path.clear()
      path.moveTo(QPoint(cx,y0+h1))
      path.lineTo(QPoint(cx,y0+h1*(1-svalue)))
      painter.setPen(QPen(Qt.red,10))
      painter.drawPath(path)
      painter.setFont(font)
      painter.drawText(QPoint(tx,ty),t)

# Instantiate Widgets
linear = Linear()
linear.show()

# Loader
def main():
  exit(app.exec())

if __name__ == "__main__":
  main()

Rotary

from PySide6.QtCore import *
from PySide6.QtGui import *
from PySide6.QtWidgets import *
from rtmidi import MidiIn, MidiOut
import sys
import math
def radians(x):
  return x*math.pi/180
def degrees(x):
  return x*180/math.pi
app = QApplication([])

# Define Widgets
class Rotary(QWidget):
  def __init__(self):
    super().__init__()
    self.value = 45
    self.maxval = 127
    self.minval = 0
    self.mx = 0
    self.my = 0
  def mousePressEvent(self,e):
    self.mval = self.value
    pos = e.pos()
    self.mx = pos.x()
    self.my = pos.y()
    print(f"press {pos.x()} {pos.y()}")
  def mouseMoveEvent(self,e):
    pos = e.pos()
    dy = - (pos.y() - self.my)
    self.value = max(min(self.mval + dy,self.maxval),self.minval)
    print(f"move {pos.x()} {pos.y()} -- {dy=}")
    self.update()

  def paintEvent(self, event: QPaintEvent):
    """Override method from QWidget
    Paint the Pixmap into the widget
    """
    rect = self.rect()
    w = rect.width()
    h = rect.height()
    cx = w/2
    cy = h/2
    m = min(w,h)
    r = (m/2) * 0.9
    x = cx - r 
    y = cy - r
    ww = 2*r
    wh = 2*r
    wr = QRect(QPoint(x,y),QSize(ww,wh))

    wtheta = 300
    itheta = 270-((360-wtheta)//2)
    dtheta = wtheta*(self.value/(self.maxval-self.minval))
    font = QFont("Arial",r/2)
    metrics = QFontMetrics(font)
    t = str(self.value)
    twidth = metrics.horizontalAdvance(t)
    theight = metrics.capHeight()
    tx = cx-(twidth/2)
    ty = cy+(theight/2)
    with QPainter(self) as painter:
      painter.fillRect(rect,Qt.white)
      path = QPainterPath()
      path.arcMoveTo(wr,itheta)
      path.arcTo(wr,itheta,-dtheta)
      painter.setBrush(Qt.NoBrush)
      painter.setPen(QPen(Qt.black, 5))
      painter.drawPath(path)
      painter.setFont(font)
      painter.drawText(QPoint(tx,ty),t)

# Instantiate Widgets
rotary = Rotary()
rotary.show()

# Loader
def main():
  exit(app.exec())

if __name__ == "__main__":
  main()

Linear2

Illustrates how to use modifiers like shift and control (note mods.value and Qt.ShiftModifier.value if you want to bitwise compare), and have shift do fine-grained movements, and control jumping straight to the mouse position.

from PySide6.QtCore import *
from PySide6.QtGui import *
from PySide6.QtWidgets import *
from rtmidi import MidiIn, MidiOut
import sys
import math
def radians(x):
  return x*math.pi/180
def degrees(x):
  return x*180/math.pi
app = QApplication([])

# Define Widgets
class LinearSlider1(QWidget):
  def __init__(self):
    super().__init__()
    self.value = 45
    self.maxval = 127
    self.minval = 0
    self.mx = 0
    self.my = 0
    self.ly = 0
    self.lv = 0
    self.sens = 1 # scales so that dragging the height of the widget goes from min to max
  def mousePressEvent(self,e):
    self.mval = self.value
    pos = e.pos()
    self.mx = pos.x()
    self.my = pos.y()
    self.ly = self.my
    self.lv = self.value
    mods = e.modifiers()
    if mods == Qt.ControlModifier:
      return self.mouseMoveAbsolute(e)
    print(f"press {pos.x()} {pos.y()}")
  def mouseMoveAbsolute(self,e):
    rect = self.rect()
    h = rect.height()
    pos = e.pos()
    y = pos.y()
    x = 1 - y/h
    self.value = self.minval + x*(self.maxval - self.minval)
    self.value = min(self.maxval,max(self.minval,self.value))
    self.lv = self.value
    self.ly = y
    self.value = int(self.value)
    self.update()
  def mouseMoveEvent(self,e):
    mods = e.modifiers()
    sens = self.sens
    if mods == Qt.ControlModifier:
      return self.mouseMoveAbsolute(e)
    if mods.value & Qt.ShiftModifier.value:
      print("shift")
      sens /= 10
    rect = self.rect()
    h = rect.height()
    pos = e.pos()
    dy = - (pos.y() - self.ly)
    self.ly = pos.y()
    dy1 = sens*(dy / h)*(self.maxval - self.minval) 
    print("aa",self.lv,dy1)
    self.lv = max(min(dy1+self.lv,self.maxval),self.minval)

    self.value = int(self.lv) 
    self.update()
    return
    dy = - (pos.y() - self.my)
    rect = self.rect()
    h = rect.height()
    dy1 = sens*(dy / h)*(self.maxval-self.minval)
    print(f"{h=} {dy=} {dy1=}")
    self.value = int(max(min(self.mval + dy1,self.maxval),self.minval))
    print(f"move {pos.x()} {pos.y()} -- {dy=}")
    self.update()

  def paintEvent(self, event: QPaintEvent):
    """Override method from QWidget
    Paint the Pixmap into the widget
    """
    rect = self.rect()
    w = rect.width()
    h = rect.height()
    w1 = w
    h1 = h
    cx = w/2
    cy = h/2
    m = min(w,h)
    r = (m/2)
    y0 = cy - h1/2

    font = QFont("Arial",r/4)
    metrics = QFontMetrics(font)
    t = str(self.value)
    twidth = metrics.horizontalAdvance(t)
    theight = metrics.capHeight()
    tx = cx-(twidth/2)
    ty = cy+(theight/2)
    svalue = (self.value-self.minval)/(self.maxval-self.minval)
    with QPainter(self) as painter:
      painter.fillRect(rect,Qt.white)
      painter.setBrush(Qt.NoBrush)
      path = QPainterPath()
      path.moveTo(QPoint(cx,y0+h1))
      path.lineTo(QPoint(cx,y0))
      painter.setPen(QPen(Qt.black, 5))
      painter.drawPath(path)
      path.clear()
      path.moveTo(QPoint(cx,y0+h1))
      path.lineTo(QPoint(cx,y0+h1*(1-svalue)))
      painter.setPen(QPen(Qt.red,10))
      painter.drawPath(path)
      painter.setFont(font)
      painter.drawText(QPoint(tx,ty),t)

# Instantiate Widgets
slider = LinearSlider1()
slider.show()

# Loader
def main():
  exit(app.exec())

if __name__ == "__main__":
  main()