tl;dr
Используйте лямбда и испускайте сигналы вручную:
class Window(QMainWindow):
keyClicked = QtCore.pyqtSignal(object)
# ...
def makeButtons(self):
# ...
for x in range(len(self.dict)):
self.dict[x] = button = QtWidgets.QPushButton(self)
# ...
button.clicked.connect(lambda _, key=self.list[x]: self.keyClicked.emit(key))
# ...
Объяснение
Всякий раз, когда у вас есть много виджетов, которые ведут себя одинаково, вы должны предоставить общий интерфейс для их поведения. lambda
функции могут помочь вам в этом, но вы должны знать об области, к которой принадлежат переменные (помните, что как только функция завершается/возвратится, любая "анонимная" переменная, которая не имеет постоянного отношения к другим объектам python будет собирать мусор, что практически означает «удалено»).
Как видно из приведенного выше примера, я использую два аргумента.
Первый предоставляется всякий раз, когда генерируется сигнал (любой потомок QAbstractButton выдает сигнал clicked
с аргументами bool, которые сообщает о новом статусе «проверено», поэтому, если кнопка может быть проверена, аргументом будет состояние кнопки «проверено», в противном случае он всегда будет False
). Мы можем просто проигнорировать это, но его нужно добавить в качестве аргумента лямбда-функции.
Последний аргумент ключевое слово предоставляется для обеспечения согласованности с областью действия текущий цикл. Таким образом мы можем гарантировать, что аргумент key
функции лямбда всегда будет аргументом текущего цикла (в противном случае он всегда будет последним for
значением< /em> присвоен этому for
циклу).
Приложение
Эта часть ответа совершенно не требуется и не нужна для реальной цели этого вопроса; тем не менее, я предполагаю, что вы плохо знакомы не только с Python и PyQt в целом, но и с UX сторона, с которой почти всегда должно быть связано программирование. Как программисты, мы должны знать об этом, поскольку большинство пользователей, которые в конечном итоге будут использовать наши продукты, не являются программистами.
Создание графического интерфейса клавиатуры — непростая задача.
Люди привыкли к клавиатурам, которыми пользуются каждый день (угадайте, смартфоны). Выбор раскладки на основе алфавитного порядка для клавиатуры, использующей стандартную раскладку «три ряда», не только непопулярен, но и неправильный. Мы (к счастью) далеки от того промежутка времени, который был до 2010-х годов, когда почти никто вообще не знал, как печатать, но мы также должны учитывать результаты этого аспекта.
Чтобы добавить еще больше беспорядка в то учтите, что даже если QWERTY-раскладка почти стандартная, есть и другие раскладки, которые частично (или полностью) от нее отличаются. Например, французы используют AZERTY, а некоторые страны Центральной Европы используют QWERTZ, не говоря уже о письмах, не основанных на латинице.
И все это относится только к буквам стандартного алфавита.
Наконец, код
from string import ascii_uppercase
from PyQt5 import QtCore, QtWidgets
class Keyboard(QtWidgets.QWidget):
ASCII, QWERTY, DVORAK = 0, 1, 2
KeyLayoutLetters = {
ASCII: [ascii_uppercase[r*9:r*9+9] for r in range(3)],
QWERTY: ['QWERTYUIOP', 'ASDFGHJKL', 'ZXCVBNM'],
DVORAK: ['PYFGCRL', 'AOEUIDHTNS ', 'QJKXBMWVZ'],
}
# some default special stretch set for specific keyboard layouts
KeyStretch = {
QWERTY: [(1, 1), (1, 1), (1, 2)],
DVORAK: [(2, 1), (1, 2), (3, 1)],
}
keySize = 50
spacing = 2
letterClicked = QtCore.pyqtSignal(object)
def __init__(self):
super(Keyboard, self).__init__()
self.setKeyboardLayout()
def setKeyboardLayout(self, keyLayout=None):
keyLayout = keyLayout if keyLayout is not None else self.ASCII
if self.layout() is not None:
QtWidgets.QWidget().setLayout(self.layout())
layout = QtWidgets.QVBoxLayout(self)
layout.setSpacing(self.spacing)
stretches = self.KeyStretch.get(keyLayout, [(1, 1)] * 3)
for row, rowChars in enumerate(self.KeyLayoutLetters.get(keyLayout, self.KeyLayoutLetters[self.ASCII])):
rowLayout = QtWidgets.QHBoxLayout()
rowLayout.setSpacing(self.spacing)
layout.addLayout(rowLayout)
stretchLeft, stretchRight = stretches[row]
rowLayout.addStretch(stretchLeft)
for letter in rowChars:
if not letter.strip():
spacer = QtWidgets.QWidget()
rowLayout.addWidget(spacer)
spacer.setFixedSize(self.keySize * .5, self.keySize)
continue
letterButton = QtWidgets.QPushButton(letter)
rowLayout.addWidget(letterButton, stretch=0)
letterButton.setFixedSize(self.keySize, self.keySize)
letterButton.clicked.connect(lambda _, key=letter: self.letterClicked.emit(key))
rowLayout.addStretch(stretchRight)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
central = QtWidgets.QWidget()
self.setCentralWidget(central)
layout = QtWidgets.QVBoxLayout(central)
# just a QLineEdit widget to test the text intertion
self.lineEdit = QtWidgets.QLineEdit()
layout.addWidget(self.lineEdit)
self.keyboard = Keyboard()
layout.addWidget(self.keyboard)
self.keyboard.setKeyboardLayout(Keyboard.DVORAK)
self.keyboard.letterClicked.connect(self.lineEdit.insert)
self.keySelector = QtWidgets.QComboBox()
layout.addWidget(self.keySelector)
self.keySelector.addItems(['ASCII', 'QWERTY', 'DVORAK'])
self.keySelector.currentIndexChanged.connect(self.keyboard.setKeyboardLayout)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
keyboard = MainWindow()
keyboard.show()
sys.exit(app.exec_())
Имейте в виду, что это действительно простая и необработанная реализация того, что вы, вероятно, ищете. Существует множество аспектов, о которых вам нужно позаботиться (в основном, связанных со специальными символами, типичными для языка пользователя).
17.11.2019