Библиотека wxPython в Python

В этом руководстве мы узнаем, как использовать библиотеку wxPython для разработки графических пользовательских интерфейсов (GUI) для настольных приложений на Python. GUI – это часть вашего приложения, которая позволяет пользователю взаимодействовать с вашим приложением без необходимости вводить команды, они могут делать практически все одним щелчком мыши.

Некоторые из популярных альтернатив Python для разработки графического интерфейса включают Tkinter и pyqt. Однако в этом руководстве мы узнаем о wxPython.

Прежде чем мы двинемся дальше, есть несколько предварительных условий для этого урока. Вы должны иметь базовое представление о синтаксисе Python и программировании хотя бы на начальном уровне на каком-либо другом языке. Хотя вы можете следовать ему, даже если вы не соответствуете этим критериям, но некоторые части могут оказаться для вас немного сложными. Если да, не стесняйтесь спрашивать разъяснения в комментариях.

Установка wxPython в Python

Процесс установки wxPython довольно прост, хотя он немного отличается в зависимости от используемой вами системы.

Mac и Windows

WxPython довольно легко установить на Mac и Windows с помощью диспетчера пакетов pip. Если в вашей системе установлен pip, выполните следующую команду для загрузки, чтобы установить wxPython:

$ pip install wxpython

Linux

Для Linux эта процедура может быть немного неудобной, так как в ней есть много обязательных библиотек, которые необходимо установить. Я бы порекомендовал попробовать последовательно выполнить следующие две команды:

# Command 1
$ sudo apt-get install dpkg-dev build-essential python2.7-dev python3.5-dev python3.6-dev libgstreamer-plugins-base1.0-dev libnotify-dev libwebkitgtk-3.0-dev libwebkit-dev libwebkitgtk-dev libjpeg-dev libtiff-dev libgtk2.0-dev libsdl1.2-dev libgstreamer-plugins-base0.10-dev freeglut3 freeglut3-dev

# Command 2
$ pip install --upgrade --pre -f https://wxpython.org/Phoenix/snapshot-builds/ wxPython

Однако, если они не работают, вам придется вручную установить эти библиотеки.

Примеры создания графического интерфейса

В этом разделе мы будем работать с wxPython и создадим базовое приложение для обработки строк с некоторыми базовыми функциями, такими как подсчет количества слов, отображение частоты каждого слова, наиболее часто повторяющегося слова и т.д.

Прежде чем двигаться дальше, мы создадим очень простой скелет приложения, который мы будем использовать в качестве отправной точки в следующих примерах для реализации более продвинутых функций графического интерфейса.

Ниже приведен базовый скелет или структура приложения с графическим интерфейсом, созданного с использованием wxPython. Мы изменим его дальше в следующем разделе, чтобы сделать его объектно-ориентированным для дополнительной функциональности.

import wx

# Creates an App object which runs a loop to display the
# GUI on the screen
myapp = wx.App()

# Initialises a frame that the user would be able to
# interact with
init_frame = wx.Frame(parent=None, title='Word Play')

# Display the initialised frame on screen
init_frame.Show()

# Run a loop on the app object
myapp.MainLoop()

Если цикл не запущен (то есть вызов app.MainLoop()), тогда кадр появится на экране на долю секунды, и даже прежде, чем вы сможете его увидеть, он исчезнет. Эта функция гарантирует, что кадр остается видимым на экране до тех пор, пока пользователь не выйдет из программы, и делает это путем выполнения кадра в цикле.

Примечание. При запуске на Mac я получил следующую ошибку, когда запустил свой код с помощью команды filename.py в терминале:

This program needs access to the screen. Please run with a Framework build of python, and only when you are logged in on the main display of your Mac.

Чтобы избавиться от этого, просто используйте pythonw вместо python в приведенной выше команде.

После запуска программы вы должны увидеть следующее пустое окно на экране:

Пустое окно на экране

Объектно-ориентированный код

Прежде чем добавлять функциональные возможности в наш код, давайте сначала разберем его на модули, создав классы и функции, чтобы он выглядел чище и его было легче расширять. Функциональность следующего кода такая же, как и раньше, однако он был переработан для реализации концепций объектно-ориентированного программирования.

import wx
import operator

# We make a class for frame, so that each time we
# create a new frame, we can simply create a new
# object for it

class WordPlay(wx.Frame):
    def __init__(self, parent, title):
        super(WordPlay, self).__init__(parent, title=title)
        self.Show()

def main():
    myapp = wx.App()
    WordPlay(None, title='Word Play')
    myapp.MainLoop()

main()

В приведенном выше скрипте мы создаем класс WordPlay, который наследует класс wxFrame. Конструктор класса WordPlay принимает два параметра: родительский и заголовок. Внутри дочернего конструктора вызывается конструктор родительского класса для класса wxPython, и ему передаются атрибуты parent и title. Наконец, вызывается метод show для отображения кадра. В методе main() создается объект класса WordPlay.

Код теперь выглядит более структурированным и чистым, его легче понять, и в приведенный выше код можно легко добавить больше функций.

Добавление функций

Мы будем добавлять функции по одной, чтобы избежать путаницы в отношении того, какая часть кода добавляется для какой конкретной функции. В нашем базовом приложении нам нужно текстовое поле, в которое мы можем добавить текст, а затем несколько кнопок для выполнения различных функций с этим текстом, таких как вычисление количества слов в нем, частоты каждого слова и т.д., за которыми следует вывод отображается на экране нашего приложения.

Начнем с добавления текстового поля в наше приложение, в которое мы можем добавить наш текст.

# Some of the code will be the same as the one above,
# so make sure that you understand that before moving
# to this part

import wx
import operator

# We make a class for frame, so that each time we create a new frame,
# we can simply create a new object for it

class WordPlay(wx.Frame):
    def __init__(self, parent, title):
        super(WordPlay, self).__init__(parent, title=title)
        self.widgets()
        self.Show()

    # Declare a function to add new buttons, icons, etc. to our app
    def widgets(self):
        text_box = wx.BoxSizer(wx.VERTICAL) # Vertical orientation

        self.textbox = wx.TextCtrl(self, style=wx.TE_RIGHT)
        text_box.Add(self.textbox, flag=wx.EXPAND | wx.TOP | wx.BOTTOM, border=5)

        grid = wx.GridSizer(5, 5, 10, 10) # Rows, columns, vertical gap, horizontal gap
        text_box.Add(grid, proportion=2, flag=wx.EXPAND)

        self.SetSizer(text_box)

def main():
    myapp = wx.App()
    WordPlay(None, title='Word Play')
    myapp.MainLoop()

main()

Как видите, выше мы добавили новую функцию с именем widgets(), и она была вызвана в конструкторе класса WordPlay. Его цель – добавить на наш экран новые виджеты. Однако в нашем случае нас интересует только добавление одного виджета, то есть текстового поля, в которое мы можем добавить некоторый текст.

Давайте теперь разберемся с некоторыми важными вещами, которые происходят внутри этой функции widgets(). Метод BoxSizer(), как следует из названия, управляет размером виджета, а также его положением (относительным или абсолютным). Wx.VERTICAL указывает, что мы хотим вертикальную ориентацию для этого виджета. TextCtrl в основном добавляет небольшое текстовое поле в наш текущий объект from, где пользователь может ввести текст. Метод GridSizer() помогает нам создать табличную структуру для нашего окна.

Хорошо, посмотрим, как теперь выглядит наше приложение.

Создание табличной структуры

Текстовое поле теперь можно увидеть в окне нашего приложения.

Пойдем дальше и добавим в наше приложение две кнопки: одну для подсчета количества слов в тексте, а вторую для отображения наиболее часто повторяющегося слова. Мы сделаем это за два шага: сначала мы добавим две новые кнопки, а затем добавим обработчики событий в нашу программу, которые сообщат нам, какую кнопку нажал пользователь, вместе с текстом, введенным в текстовое поле, так что над входом может быть выполнено определенное действие.

Добавить кнопки довольно просто, для этого нужно лишь добавить дополнительный код в нашу функцию «виджеты». В блоке кода ниже мы будем отображать только обновленную функцию виджетов, остальная часть кода остается прежней.

# Adding buttons to our main window

def widgets(self):
    text_box = wx.BoxSizer(wx.VERTICAL)

    self.textbox = wx.TextCtrl(self, style=wx.TE_RIGHT)
    text_box.Add(self.textbox, flag=wx.EXPAND | wx.TOP | wx.BOTTOM, border=5)

    grid = wx.GridSizer(2, 5, 5) # Values have changed to make adjustments to button positions
    button_list = ['Count Words', 'Most Repeated Word'] # List of button labels

    for lab in button_list:
        button = wx.Button(self, -1, lab) # Initialise a button object
        grid.Add(button, 0, wx.EXPAND) # Add a new button to the grid with the label from button_list

    text_box.Add(grid, proportion=2, flag=wx.EXPAND)

    self.SetSizer(text_box)

Добавление двух новых кнопок

Как видите, в наше главное окно теперь добавлены две новые кнопки.

Добавление обработчика событий

Интерфейс нашего приложения теперь готов, все, что нам нужно сделать, это добавить обработчики событий для выполнения определенных действий при нажатии кнопки. Для этого нам нужно будет создать новую функцию и добавить дополнительную строку кода в функцию виджетов. Начнем с написания нашей функции.

# Declare an event handler function

def event_handler(self, event):
    # Get label of the button clicked
    btn_label = event.GetEventObject().GetLabel()

    # Get the text entered by user
    text_entered = self.textbox.GetValue()

    # Split the sentence into words
    words_list = text_entered.split()

    # Perform different actions based on different button clicks
    if btn_label == "Count Words":
        result = len(words_list)
    elif btn_label == "Most Repeated Word":
        # Declare an empty dictionary to store all words and
        # the number of times they occur in the text
        word_dict = {}

        for word in words_list:
            # Track count of each word in our dict
            if word in word_dict:
                word_dict[word] += 1
            else:
                word_dict[word] = 1

            # Sort the dict in descending order so that the
            # most repeated word is at the top
            sorted_dict = sorted(word_dict.items(),
                                key=operator.itemgetter(1),
                                reverse=True)

            # First value in the dict would be the most repeated word
            result = sorted_dict[0]

    # Set the value of the text box as the result of our computation
    self.textbox.SetValue(str(result))

Логика, лежащая в основе функции «Наиболее часто повторяющееся слово», заключается в том, что мы сначала запускаем цикл, который выполняет итерацию по слову из списка всех слов. Затем он проверяет, существует ли это конкретное слово уже в словаре.

Если да, то это означает, что он повторяется, и его значение увеличивается на единицу каждый раз, когда слово появляется снова. В противном случае, если он не существует в словаре, это означает, что он появился в предложении впервые, и его значение «вхождения» должно быть установлено на 1. Наконец, мы сортируем словарь (аналогично сортировке списка в Python) в порядке убывания, чтобы слово с наивысшим значением (частотой) выходило на первое место, которое мы затем можем отобразить.

Хорошо, теперь, когда мы написали вычисление или действие, которое должно выполняться при нажатии определенной кнопки, давайте «привяжем» это действие к этой конкретной кнопке. Для этого нам нужно немного изменить функцию наших виджетов.

# Only one line needs to be added in the "for loop" of
# our widgets function, so that's all we're showing
for lab in button_list:
    button = wx.Button(self, -1, lab)
    self.Bind(wx.EVT_BUTTON, self.event_handler, button)
    grid.Add(button, 0, wx.EXPAND)

В приведенном выше коде привязка происходит в вызове self.Bind. Что он делает, так это то, что он связывает определенное действие с определенной кнопкой, так что, когда вы нажимаете эту кнопку, будет выполнено определенное действие, связанное с ней.

В нашем конкретном случае у нас есть только одна функция обработчика событий, которая обрабатывает оба действия, проверяя во время выполнения, какая кнопка была нажата через свойство ‘label’, а затем выполняет связанное действие. Итак, в вызове self.Bind мы привязываем все наши кнопки к одной функции event_handler.

Хорошо, теперь наш код готов. Давайте попробуем обе наши функции и посмотрим, все ли работает должным образом.

На первом этапе, как показано ниже, мы вводим строку в текстовое поле:

Ввод строки в текстовое поле

Затем, если мы нажмем кнопку «Подсчитать слова», вы увидите цифру «7» в текстовом поле, поскольку в строке было 7 слов.

Кнопка «Подсчитать слова»

Теперь давайте напишем еще одну строку в текстовое поле, как показано на следующем рисунке:

Написание строки

Теперь, если мы нажмем кнопку «Наиболее часто повторяющееся слово», вы увидите в текстовом поле наиболее часто повторяющиеся слова с указанием частоты их появления, как показано ниже:

Кнопка «Наиболее часто повторяющееся слово»

Прекрасно работает!

Мы добавили только две функции, но цель состояла в том, чтобы показать вам, как все эти компоненты связаны между собой. Вы можете добавить столько функций, сколько захотите, просто написав для них дополнительные функции.

Кроме того, в этом уроке не уделялось особого внимания эстетике. В wxPython доступно множество виджетов, чтобы украсить вашу программу теперь, когда вы усвоили базовые знания этого инструментария.

Заключение

Подводя итог, мы узнали, что wxPython широко используется для разработки настольных приложений на основе графического интерфейса пользователя, и что у Python также есть несколько других интересных альтернатив для него.

Мы узнали, как создать модульное приложение с помощью wxPython, которое можно легко расширить, как можно увидеть в этом руководстве, где мы создали базовое приложение-скелет и добавили дополнительные функции шаг за шагом.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *