我正在尝试创建一个 GUI 键盘。我正在使用 QPushButton 来复制按键。我还没有专注于将它链接到 MIDI。但是,我想知道是否可以使用 QPushButton 的属性创建一个类。我指的是 QPushButton.move/resize/setStyleSheet 等函数。
就目前而言,我的代码非常长(如下所示)。这是由于不断重复代码来制作每个按钮。然而,我注意到大小、样式表和位置(仅在 y 轴上)等属性是相同的,主要区别在于黑键和白键之间的 x 轴和 y 轴。如果我要创建一个类,我将如何创建具有各种属性的对象?
再次强调,如果我的逻辑有缺陷或者有任何错误,请随时告诉我。
import sys
from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QPushButton, QAction, QLineEdit, QMessageBox, QLabel \
,QDialog
from PyQt5.QtGui import QIcon, QHideEvent, QFont, QPixmap
from PyQt5.QtCore import pyqtSlot, QObject, QSize
class App(QMainWindow):
def __init__(self):
super().__init__()
self.title = 'Piano '
self.left = 620
self.top =250
self.width = 1920
self.height = 1080
self.initUI()
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
self.setFixedWidth(self.width)
self.setFixedHeight(self.height)
self.setStyleSheet("background-color:lightgrey")
self.setWindowIcon(QIcon('img.png'))
# Create a button in the window
self.button1 = QPushButton('C', self)
self.button1.move(10, 650)
self.button1.setFont(QFont('Arial', 14))
self.button1.resize(100,400)
self.button1.setStyleSheet("background-color:white; border :1px solid ;") #Black Not Working
self.button2 = QPushButton('D', self)
self.button2.move(110,650)
self.button2.setFont(QFont('Arial', 14))
self.button2.resize(100, 400)
self.button2.setStyleSheet("background-color:white; border :1px solid ;")
self.button3 = QPushButton('E', self)
self.button3.move(210, 650)
self.button3.setFont(QFont('Arial', 14))
self.button3.resize(100, 400)
self.button3.setStyleSheet("background-color:white; border :1px solid ;")
self.button4 = QPushButton('F', self)
self.button4.move(310, 650)
self.button4.setFont(QFont('Arial', 14))
self.button4.resize(100, 400)
self.button4.setStyleSheet("background-color:white; border :1px solid ;")
self.button5 = QPushButton('G', self)
self.button5.move(410, 650)
self.button5.setFont(QFont('Arial', 14))
self.button5.resize(100, 400)
self.button5.setStyleSheet("background-color:white; border :1px solid ;")
self.button6 = QPushButton('A', self)
self.button6.move(510, 650)
self.button6.setFont(QFont('Arial', 14))
self.button6.resize(100, 400)
self.button6.setStyleSheet("background-color:white; border :1px solid ;")
self.button7 = QPushButton('B', self)
self.button7.move(610, 650)
self.button7.setFont(QFont('Arial', 14))
self.button7.resize(100, 400)
self.button7.setStyleSheet("background-color:white; border :1px solid ;")
self.button8 = QPushButton('C2', self)
self.button8.move(710, 650)
self.button8.setFont(QFont('Arial', 14))
self.button8.resize(100, 400)
self.button8.setStyleSheet("background-color:white; border :1px solid ;")
self.button2 = QPushButton('D2', self)
self.button2.move(810, 650)
self.button2.setFont(QFont('Arial', 14))
self.button2.resize(100, 400)
self.button2.setStyleSheet("background-color:white; border :1px solid ;")
self.button3 = QPushButton('E2', self)
self.button3.move(910, 650)
self.button3.setFont(QFont('Arial', 14))
self.button3.resize(100, 400)
self.button3.setStyleSheet("background-color:white; border :1px solid ;")
self.button4 = QPushButton('F2', self)
self.button4.move(1010, 650)
self.button4.setFont(QFont('Arial', 14))
self.button4.resize(100, 400)
self.button4.setStyleSheet("background-color:white; border :1px solid ;")
self.button5 = QPushButton('G2', self)
self.button5.move(1110, 650)
self.button5.setFont(QFont('Arial', 14))
self.button5.resize(100, 400)
self.button5.setStyleSheet("background-color:white; border :1px solid ;")
self.button6 = QPushButton('A2', self)
self.button6.move(1210, 650)
self.button6.setFont(QFont('Arial', 14))
self.button6.resize(100, 400)
self.button6.setStyleSheet("background-color:white; border :1px solid ;")
self.button7 = QPushButton('B2', self)
self.button7.move(1310, 650)
self.button7.setFont(QFont('Arial', 14))
self.button7.resize(100, 400)
self.button7.setStyleSheet("background-color:white; border :1px solid ;")
self.button8 = QPushButton('C3', self)
self.button8.move(1410, 650)
self.button8.setFont(QFont('Arial', 14))
self.button8.resize(100, 400)
self.button8.setStyleSheet("background-color:white; border :1px solid ;")
self.button1up = QPushButton('C#', self)
self.button1up.move(85, 650)
self.button1up.setFont(QFont('Arial', 14))
self.button1up.resize(50, 300)
self.button1up.setStyleSheet("background-color:black; border :1px solid ;")
self.button2up = QPushButton('D#', self)
self.button2up.move(185, 650)
self.button2up.setFont(QFont('Arial', 14))
self.button2up.resize(50, 300)
self.button2up.setStyleSheet("background-color:black; border :1px solid ;")
self.button3up = QPushButton('F#', self)
self.button3up.move(385, 650)
self.button3up.setFont(QFont('Arial', 14))
self.button3up.resize(50, 300)
self.button3up.setStyleSheet("background-color:black; border :1px solid ;")
self.button4up = QPushButton('G#', self)
self.button4up.move(485, 650)
self.button4up.setFont(QFont('Arial', 14))
self.button4up.resize(50, 300)
self.button4up.setStyleSheet("background-color:black; border :1px solid ;")
self.button5up = QPushButton('A#', self)
self.button5up.move(585, 650)
self.button5up.setFont(QFont('Arial', 14))
self.button5up.resize(50, 300)
self.button5up.setStyleSheet("background-color:black; border :1px solid ;")
self.button6up = QPushButton(self)
self.button6up.move(785, 650)
self.button6up.setFont(QFont('Arial', 14))
self.button6up.resize(50, 300)
self.button6up.setStyleSheet("background-color:black; border :1px solid ;")
self.button7up = QPushButton(self)
self.button7up.move(885, 650)
self.button7up.setFont(QFont('Arial', 14))
self.button7up.resize(50, 300)
self.button7up.setStyleSheet("background-color:black; border :1px solid ;")
self.button8up = QPushButton(self)
self.button8up.move(1085, 650)
self.button8up.setFont(QFont('Arial', 14))
self.button8up.resize(50, 300)
self.button8up.setStyleSheet("background-color:black; border :1px solid ;")
self.button9up = QPushButton(self)
self.button9up.move(1185, 650)
self.button9up.setFont(QFont('Arial', 14))
self.button9up.resize(50, 300)
self.button9up.setStyleSheet("background-color:black; border :1px solid ;")
self.button10up = QPushButton(self)
self.button10up.move(1285, 650)
self.button10up.setFont(QFont('Arial', 14))
self.button10up.resize(50, 300)
self.button10up.setStyleSheet("background-color:black; border :1px solid ;")
# connect button to function on_click
self.button1.clicked.connect(self.b1)
self.show()
@pyqtSlot()
def b1(self):
pass
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
最佳答案
第一个逻辑解决方案是为每个键创建一个数据列表,如下所示:
KeyMap = (
# key name, x, y, width, height, color
('C', 10, 650, 100, 400, 'white'),
# ...
('C#', 85, 650, 50, 300, 'black'),
)
然后循环浏览列表:
baseStyle = 'background-color: {}; border :1px solid;'
for key, x, y, width, height, color in KeyMap:
button = QPushButton('key', self)
button.setGeometry(x, y, width, height)
button.setFont(QFont('Arial', 14))
button.setStyleSheet(baseStyle.format(color)
不幸的是,虽然上面的方法对于简单的例子来说可能工作得很好,但我可以根据经验告诉你,它对于这种情况(类似钢琴的键盘)来说效果不太好,主要是由于以下两个原因:
- 固定几何形状很少是一个好主意,如果您出于任何原因需要调整键盘大小(包括使其适合屏幕,特别是如果它很小),这将使手动编码变得更加困难;
- 您可能需要将键盘扩展到更多 Octave 或想要更改按键的宽度和高度之间的比率;
我在尝试创建 UI 键盘方面拥有丰富的经验,最终得到了一个完全自定义的小部件(基于 QGraphicsView),但对于简单的情况,以下内容可能会满足您的需求。
诀窍是使用 Qt 网格布局,它有一个有趣的“功能”,即如果放置在其“单元格”之间,则允许项目重叠。
以下代码使用 2 行(顶部为白键和黑键,底部为白键),每个白键使用 3 列:白键使用全部三列,黑键使用第三列前一个白键,以及下一个白键的第一个;想象一下这样的表格:
+---+---+---+---+---+---+---+---+---+
| · | · | | · | · |
| · | C | | D | · |
| · | sharp | | sharp | · |
| · | · | | · | · |
+ · · · +---+---+ · +---+---+ · · · +
| · · | · · | · · |
| · C · | · D · | · E · |
| · · | · · | · · |
+---+---+---+---+---+---+---+---+---+
然后,您可以为按键创建一个基本类(包括按下和释放时按键触发的信号),并使用简单的列表轻松获取按键是白色还是黑色并放置/相应地设计它们。
可选参数显然用于 Octave 范围和 Octave 开始(因为您可能会使用 MIDI,这很重要,因为您将自动获取键值)。
from PyQt5 import QtCore, QtWidgets
BlackIdx = 1, 3, -1, 6, 8, 10
WhiteIdx = 0, 2, 4, 5, 7, 9, 11
KeyNames = 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'
class KeyButton(QtWidgets.QPushButton):
triggered = QtCore.pyqtSignal(int, bool)
def __init__(self, key, isBlack=False):
super().__init__()
self.key = key
# this will be used by the stylesheet
self.setProperty('isBlack', isBlack)
octave, keyIdx = divmod(key, 12)
self.setText('{}{}'.format(KeyNames[keyIdx], octave))
self.setMinimumWidth(25)
self.setMinimumSize(25, 80 if isBlack else 120)
# ensure that the key expands vertically
self.setSizePolicy(QtWidgets.QSizePolicy.Preferred,
QtWidgets.QSizePolicy.Expanding)
# connect the pressed and released (not clicked!) signals to our custom one
self.pressed.connect(lambda: self.triggered.emit(key, True))
self.released.connect(lambda: self.triggered.emit(key, False))
class Keyboard(QtWidgets.QWidget):
def __init__(self, octaves=2, octaveStart=3):
super().__init__()
layout = QtWidgets.QGridLayout(self)
# the ratio between key heights: white keys are 1/3 longer than black ones
layout.setRowStretch(0, 2)
layout.setRowStretch(1, 1)
layout.setSpacing(0)
blackKeys = []
for octave in range(octaves):
for k in range(12):
isBlack = k in BlackIdx
keyButton = KeyButton(k + (octaveStart + octave) * 12, isBlack)
keyButton.triggered.connect(self.keyTriggered)
if isBlack:
keyPos = BlackIdx.index(k)
# column based on the index of the key list, plus 2 "cells"
col = keyPos * 3 + 2
# only one row in the layout
vSpan = 1
# only two columns
hSpan = 2
blackKeys.append(keyButton)
else:
keyPos = WhiteIdx.index(k)
col = keyPos * 3
# two rows
vSpan = 2
# three columns
hSpan = 3
col += octave * 21
layout.addWidget(keyButton, 0, col, vSpan, hSpan)
# "blank" spacers between E-F and B-C, to keep the spacings homogeneous
efSpacer = QtWidgets.QWidget()
efSpacer.setMinimumWidth(25)
layout.addWidget(efSpacer, 0, octave * 21 + 8, 1, 2)
efSpacer.lower()
baSpacer = QtWidgets.QWidget()
baSpacer.setMinimumWidth(25)
layout.addWidget(baSpacer, 0, octave * 21 + 20, 1, 2)
baSpacer.lower()
# the last C note, with a minimum width a bit bigger
octave += 1
lastButton = KeyButton((octaveStart + octave) * 12)
lastButton.setMinimumWidth(32)
lastButton.triggered.connect(self.keyTriggered)
layout.addWidget(lastButton, 0, octave * 21, 2, 3)
# raise all black keys on top of everything else
for keyButton in blackKeys:
keyButton.raise_()
# set the stretch of layout cells, if it's in the middle, it's bigger
for col in range(layout.columnCount()):
if col % 3 == 1:
layout.setColumnStretch(col, 4)
else:
layout.setColumnStretch(col, 3)
self.setStyleSheet('''
KeyButton {
color: rgb(50, 50, 50);
border: 1px outset rgb(128, 128, 128);
border-radius: 2px;
background: white;
}
KeyButton:pressed {
border-style: inset;
}
KeyButton[isBlack=true] {
color: rgb(250, 250, 250);
background: black;
}
KeyButton[isBlack=true]:pressed {
background: rgb(50, 50, 50);
}
''')
def keyTriggered(self, key, pressed):
octave, keyIdx = divmod(key, 12)
keyName = '{}{}'.format(KeyNames[keyIdx], octave)
state = 'pressed' if pressed else 'released'
print('Key {} ({}) {}'.format(key, keyName, state))
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
keyboard = Keyboard()
keyboard.show()
sys.exit(app.exec_())
PS:我用几年前制作的键盘创建了一个要点。请注意,这段代码相当旧,当时我还没有经验(您可能需要更改 import
语句,因为它的目标是 Qt.py 模块,该模块允许透明集成PyQt4/5 和 PySide):
https://gist.github.com/MaurizioB/43a053575f17eae371a9d7394e66a46e
关于python - 是否可以使用 PYQT5 中的元素创建一个类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66139144/