Ввод данных
Contents
Ввод данных#
Qt
предоставляет богатый набор виджетов для ввода данных.
QLineEdit — ввод одной строки текстовых данных; QTextEdit и QPlainTextEdit — ввод нескольких строк текста, в том числе и форматированного.
для ввода чисел и некоторых перечисляемых значений (например, месяц), часто используются так называемые
spin box
поля: QSpinBox для ввода целых чисел и дискретных перечисляемых значений, QDoubleSpinBox для ввода действительных чисел; QDateTimeEdit, QDateEdit и QTimeEdit для ввода даты и/или времени;также для ввода численных значений можно применять ползунки: QSlider, QScrollBar и QDial;
QTableWidget и QTableView могут использоваться и для ввода данных;
Кроме того, для ввода данных и ответа на вопросы можно использовать диалоговые окна, который будут обсуждаться в следующем разделе.
Строка текста. QLineEdit
#
Для ввода одной строки текста обычно используется QLineEdit. Когда на виджете QLineEdit
стоит фокус, в нем мигает курсор и в это время Qt
перехватывает большинство нажатий на клавиатуру и интерпретирует их в качестве ввода символов. Кроме символов алфавита QLineEdit
принимает и управляющий символы. Например, нажатие на клавишу Enter
сигнализирует о завершении редактирования, что приводит к возбуждению сигнала editingFinished.
Вообще говоря, можно управлять тем, будет ли очередной символ принят к вводу или отвергнут. Для этого методом setValidator необходимо настроить QValidator. Qt
предлагает ряд встроенных валидаторов. Например, валидатор целых чисел QIntValidator и валидатор действительных чисел QDoubleValidator. Произвольный валидатор можно написать используя регулярные выражения и соответствующий валидатор QRegularExpressionValidator. Кроме того, длину текста можно ограничить методом setMaxLength.
Вне зависимости от источника при изменении текста в текстовом поле излучается сигнал textChanged. Сигнал textEdited излучается только при редактировании текста пользователем, т.е. он игнорирует изменения текста методом setText.
Уже упомянутый сигнал editingFinished возбуждается не при каждом изменении текста, а только при завершении редактировании, о чем сигнализирует потеря фокуса виджетом или нажатие на Enter
. При этом сигнал не излучится, если во время редактирования текст не изменился или изменился на корректный с точки зрения валидатора.
Также есть приличное количество методов-слотов. Например, метод clear очищает текстовое поле, метод copy копирует его содержимое в буфер обмена, метод paste вставляет в текстовое поле содержимое из текстового поля, selectAll выделяет все содержимое окна.
Отображаемый в этом поле текст необязательно должен совпадать с тем, который находится в буфере ввода. Методом setEchoMode можно настроить другой стиль отображения. На вход он ожидает enum
QLineEdit.EchoMode, который принимает значения:
QLineEdit.Normal
— обычный режим, набираемый текст совпадает с отображаемым;QLineEdit.NoEcho
— не отображать ничего;QLineEdit.Password
— пароль, вместо каждого символа отображать звездочку;QLineEdit.PasswordEchoOnEdit
— как и предыдущий, но отображать текст пока он вводится.
Получить введенный текст можно методом text, а отображаемый текст методом displayText.
Код под спойлером ниже воссоздаёт окно выше и демонстрирует большинство описанных выше возможностей виджета QLineEdit
.
Код.
import sys
from functools import partial
from PySide6.QtGui import *
from PySide6.QtWidgets import *
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
widget = QWidget()
main_layout = QVBoxLayout()
widget.setLayout(main_layout)
self.setCentralWidget(widget)
# edit
self.edit = QLineEdit()
self.edit.setClearButtonEnabled(True)
# buttons row
clear_button = QPushButton("Clear")
copy_button = QPushButton("Copy")
paste_button = QPushButton("Paste")
select_button = QPushButton("Select all")
# labels
self.label = QLabel("")
self.label.setAlignment(Qt.AlignLeft)
# checkboxes
self.is_password = QCheckBox("password")
self.numbers_only = QCheckBox("numbers only")
# layout
main_layout.addWidget(self.edit)
button_layout = QHBoxLayout()
button_layout.addWidget(clear_button)
button_layout.addWidget(copy_button)
button_layout.addWidget(paste_button)
button_layout.addWidget(select_button)
main_layout.addLayout(button_layout)
main_layout.addWidget(self.label)
checkbox_layout = QHBoxLayout()
checkbox_layout.addWidget(self.is_password)
checkbox_layout.addWidget(self.numbers_only)
main_layout.addLayout(checkbox_layout)
# connections
clear_button.clicked.connect(self.edit.clear)
copy_button.clicked.connect(self.edit.copy)
paste_button.clicked.connect(self.edit.paste)
select_button.clicked.connect(self.edit.selectAll)
self.edit.editingFinished.connect(self.sync)
self.is_password.stateChanged.connect(self.update_echo_mode)
self.numbers_only.stateChanged.connect(self.update_validator)
def sync(self):
self.label.setText(self.edit.text())
def update_echo_mode(self):
mode = QLineEdit.Password if self.is_password.isChecked() else QLineEdit.Normal
self.edit.setEchoMode(mode)
def update_validator(self):
validator = QIntValidator() if self.numbers_only.isChecked() else None
validator.setBottom(0)
self.edit.setValidator(validator)
app = QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
app.exec()
Числа. QSpinBox
и его разновидности#
Виджет SpinBox
в отличие от QLineEdit
обычно используется для ввода чисел. Кроме ввода с клавиатуры этот виджет позволяет увеличивать или уменьшать вводимое значение, нажимая на специальные кнопки. Всего есть следующие разновидности спинбоксов:
QSpinBox — ввод целых чисел и перечисляемых значений;
QDoubleSpinBox — ввод действительных значений;
QDateTimeEdit, QDateEdit и QTimeEdit — ввод даты и времени.
Все они наследуют от абстрактного базового класса QAbstractSpinBox. Чтобы настроить ввод чисел, необходимо знать про следующие методы:
setMinimum и setMaximum — настроить диапазон;
setValue — установить значение, в том числе и начальное;
setSingleStep — установить шаг.
При изменении значений испускается сигнал. valueChanged
Для QDoubleSpinBox
методом setDecimals выставить количество десятичных знаков. QSpinBox
можно настроить на выбор перечисляемых значений, перегрузив метод textFromValue. Тогда удобнее использовать сигнал textChanged.
Код под спойлером ниже воссоздаёт окно выше и демонстрирует большинство описанных выше возможностей виджета QSpinBox
и QDoubleSpinBox
.
Код.
import sys
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg, NavigationToolbar2QT
from PySide6.QtCore import *
from PySide6.QtWidgets import *
class MPLGraph(FigureCanvasQTAgg):
def __init__(self):
fig, ax = plt.subplots(figsize=(2, 2))
ax.set_xlim([0, 1])
ax.set_ylim([0, 1])
self.x = np.linspace(0, 2 * np.pi, 1000)
self.line, = ax.plot(self.x, self.x)
super().__init__(fig)
def update_power(self, power):
y = self.x ** power
self.line.set_ydata(y)
self.draw()
def update_linewidth(self, width):
self.line.set_linewidth(width)
self.draw()
def update_color(self, color):
self.line.set_color(color)
self.draw()
class PowerSpinBox(QSpinBox):
def __init__(self):
super().__init__()
self.setMinimum(0)
self.setMaximum(100)
self.setValue(1)
self.setPrefix("Power: ")
class RainbowColorSpinBox(QSpinBox):
colors = ["red",
"orange",
"yellow",
"green",
"blue",
"magenta",
"violet"]
def __init__(self):
super().__init__()
self.setMinimum(0)
self.setMaximum(6)
def textFromValue(self, val):
return self.colors[val]
class LineWidthSpinBox(QDoubleSpinBox):
def __init__(self):
super().__init__()
self.setMinimum(0.1)
self.setMaximum(10)
self.setSingleStep(0.1)
self.setValue(1.)
self.setSuffix(" px")
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setGeometry(0, 0, 600, 400)
widget = QWidget()
layout = QVBoxLayout()
widget.setLayout(layout)
self.setCentralWidget(widget)
# widgets
graph = MPLGraph()
linewidth_sb = LineWidthSpinBox()
linewidth_label = QLabel("Line width")
linewidth_label.setAlignment(Qt.AlignRight)
power_sb = PowerSpinBox()
power_label = QLabel("Power")
power_label.setAlignment(Qt.AlignRight)
color_sb = RainbowColorSpinBox()
color_label = QLabel("Color")
color_label.setAlignment(Qt.AlignRight)
# layout
layout.addWidget(graph)
bottom_row = QHBoxLayout()
bottom_row.addWidget(linewidth_label)
bottom_row.addWidget(linewidth_sb)
bottom_row.addWidget(power_label)
bottom_row.addWidget(power_sb)
bottom_row.addWidget(color_label)
bottom_row.addWidget(color_sb)
layout.addLayout(bottom_row)
# connections
linewidth_sb.valueChanged.connect(graph.update_linewidth)
power_sb.valueChanged.connect(graph.update_power)
color_sb.textChanged.connect(graph.update_color)
app = QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
app.exec()
Ползунки. QSlider
#
В ряде ситуация удобно использовать ползунки, для интерактивного изменения численных значений. Принцип их действия очень похож на QSpinBox
: они хранят в себе целое число, при изменении которого испускается сигнал valueChanged
, диапазон контролируется методами setMinimum
и setMaximum
. Для воплощения ползунков есть три следующие виджеты:
Пример применения этих виджетов приводится в спойлере ниже.
Код.
import sys
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from PySide6.QtCore import *
from PySide6.QtWidgets import *
class MPLGraph(FigureCanvasQTAgg):
def __init__(self):
fig, self.ax = plt.subplots(figsize=(2, 2), layout="tight")
self.ax.set_xticks(np.arange(0, 4 * np.pi + 0.0001, np.pi / 2))
self.ax.set_xticklabels([
"0",
r"$\dfrac{\pi}{2}$",
r"$\pi$",
r"$\dfrac{3\pi}{2}$",
r"$2\pi$",
r"$\dfrac{5\pi}{2}$",
r"$3\pi$",
r"$\dfrac{7\pi}{2}$",
r"$4\pi$"
])
self.ax.grid()
self.x = np.linspace(0, 4 * np.pi, 1000)
self.magnitude = 1.
self.phase = 0.
self.line, = self.ax.plot(self.x, self.compute_y())
self.ax.set_xlim([0, 2 * np.pi])
self.ax.set_ylim([-1, 1])
super().__init__(fig)
def compute_y(self):
return self.magnitude * np.sin(self.x - self.phase)
def update_plot(self):
self.line.set_ydata(self.compute_y())
self.draw()
def update_horizontal_range(self, scroll_value):
left_boundary = scroll_value / 100. * 2 * np.pi
self.ax.set_xlim([left_boundary, left_boundary + 2 * np.pi])
self.draw()
def update_magnitude(self, slider_value):
self.magnitude = slider_value / 100.
self.update_plot()
def update_phase(self, dial_value):
self.phase = dial_value / 100. * np.pi
self.update_plot()
class RangeScrollBar(QScrollBar):
def __init__(self):
super().__init__(Qt.Horizontal)
self.setMinimum(0)
self.setMaximum(100)
class HeightSlider(QSlider):
def __init__(self):
super().__init__(Qt.Vertical)
self.setMinimum(0)
self.setMaximum(100)
self.setValue(100)
class PhaseDial(QDial):
def __init__(self):
super().__init__()
self.setMinimum(0)
self.setMaximum(100)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setGeometry(0, 0, 600, 400)
widget = QWidget()
layout = QGridLayout()
widget.setLayout(layout)
self.setCentralWidget(widget)
# widgets
graph = MPLGraph()
range_scroll_bar = RangeScrollBar()
magnitude_slider = HeightSlider()
phase_dial = PhaseDial()
# layout
layout.addWidget(magnitude_slider, 1, 1)
layout.addWidget(phase_dial, 2, 1)
layout.addWidget(graph, 1, 2, 2, 2)
layout.addWidget(range_scroll_bar, 3, 2, 1, 2)
# connections
range_scroll_bar.valueChanged.connect(graph.update_horizontal_range)
magnitude_slider.valueChanged.connect(graph.update_magnitude)
phase_dial.valueChanged.connect(graph.update_phase)
app = QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
app.exec()