Arhn - архитектура программирования

Автоматизация повторяющегося кода в Python 3 (PyQt5)

Я новичок в PyQt5 и довольно новичок в Python в целом. Мне нужна была раскладка клавиатуры, и вместо того, чтобы вручную создавать QPushButton для каждой буквы и устанавливать для каждой буквы текст, координаты и размер, я попытался автоматизировать это. Моя идея состояла в том, чтобы перебирать словарь, чтобы создать новое имя для каждой кнопки QPushButton. Затем мне пришлось использовать что-то другое, кроме self.dict[x], в качестве текста для QPushButton, потому что self.dict[x] сам был QPushButton. Я создал список со всеми персонажами и вместо него использовал list[x]. Я бы использовал список coords для настройки координат для каждого QPushButton через итерации. Моя попытка выглядела так:

from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow
import sys

class Window(QMainWindow):

    coords = [0, 200]
    size = 50
    count = 0
    list = [chr(x) for x in range(65, 91)]
    dict = {}
    for x in range(len(list)):
        dict[x]  = list[x]

    def __init__(self):
        super(Window, self).__init__()
        self.setGeometry(100, 100, 800, 500)
        self.setWindowTitle('')
        self.makeButtons()

    def makeButtons(self):
        for x in range(len(self.dict)):
            self.dict[x] = QtWidgets.QPushButton(self)
            self.dict[x].resize(self.size, self.size)
            self.dict[x].move(self.coords[0], self.coords[1])
            self.dict[x].setText(self.list[x])
            self.coords[0] += self.size
            self.count += 1
            if self.count == 9:
                self.coords = [0, 250]
            if self.count == 18:
                self.coords = [25, 300]


def window():
    app = QApplication(sys.argv)
    win = Window()
    win.show()
    sys.exit(app.exec_())

window()

который создает раскладку клавиатуры, которую я хочу. Однако я застрял, пытаясь создать метод для каждой кнопки QPushButton, поэтому я не могу добавить к кнопкам какую-либо функциональность. Можно ли реализовать автоматизацию создания других методов в методе makeButton, как я сделал с самими QPushButtons, или мне нужна другая стратегия для ее автоматизации?



Ответы:


1

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
Новые материалы

Коллекции публикаций по глубокому обучению
Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

Представляем: Pepita
Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

Советы по коду Laravel #2
1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

3 способа решить квадратное уравнение (3-й мой любимый) -
1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

Создание VR-миров с A-Frame
Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

Демистификация рекурсии
КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..


© 2024 arhn.ru, Arhn - архитектура программирования