Как в PyQt5 изменить GUI через сторонний поток? Изменить размер колонки в tableview pyqt5


python - Изменение ширины столбца в соответствии с QTableWidget pyqt

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

self.statTable.setSizeAdjustPolicy( QtWidgets.QAbstractScrollArea.AdjustToContents)

Вам просто нужно сделать:

self.statTable.resizeColumnsToContents()

всякий раз, когда таблица повторно заполняется.

Для PyQt4 все должно быть рассчитано вручную, и несколько хаков также необходимы для получения полностью согласованных результатов. Демо ниже script работает хорошо для меня, но YMMV:

import random from PyQt4 import QtCore, QtGui class Window(QtGui.QWidget): def __init__(self): super(Window, self).__init__() self.table = QtGui.QTableWidget(5, 2, self) self.button = QtGui.QPushButton('Populate', self) self.button.clicked.connect(self.populate) layout = QtGui.QGridLayout(self) layout.addWidget(self.table, 0, 0) layout.addWidget(self.button, 1, 0) layout.setColumnStretch(1, 1) def populate(self): words = 'Red Green Blue Yellow Black White Purple'.split() length = random.randint(2, len(words)) self.table.setRowCount(random.randint(3, 30)) for column in range(self.table.columnCount()): for row in range(self.table.rowCount()): item = QtGui.QTableWidgetItem(' '.join( random.sample(words, random.randint(1, length)))) self.table.setItem(row, column, item) self.table.setVisible(False) self.table.verticalScrollBar().setValue(0) self.table.resizeColumnsToContents() self.table.setVisible(True) self.setTableWidth() def setTableWidth(self): width = self.table.verticalHeader().width() width += self.table.horizontalHeader().length() if self.table.verticalScrollBar().isVisible(): width += self.table.verticalScrollBar().width() width += self.table.frameWidth() * 2 self.table.setFixedWidth(width) def resizeEvent(self, event): self.setTableWidth() super(Window, self).resizeEvent(event) if __name__ == '__main__': import sys app = QtGui.QApplication(sys.argv) window = Window() window.setGeometry(700, 150, 800, 400) window.show() sys.exit(app.exec_())

qaru.site

Урок 006. Работа с QTableWidget

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

Выглядеть таблица будет следующим образом:

Реализация

from PyQt5.QtWidgets import QApplication, QMainWindow, QGridLayout, QWidget, QTableWidget, QTableWidgetItem from PyQt5.QtCore import QSize, Qt # Наследуемся от QMainWindow class MainWindow(QMainWindow): # Переопределяем конструктор класса def __init__(self): # Обязательно нужно вызвать метод супер класса QMainWindow.__init__(self) self.setMinimumSize(QSize(480, 80)) # Устанавливаем размеры self.setWindowTitle("Работа с QTableWidget") # Устанавливаем заголовок окна central_widget = QWidget(self) # Создаём центральный виджет self.setCentralWidget(central_widget) # Устанавливаем центральный виджет grid_layout = QGridLayout() # Создаём QGridLayout central_widget.setLayout(grid_layout) # Устанавливаем данное размещение в центральный виджет table = QTableWidget(self) # Создаём таблицу table.setColumnCount(3) # Устанавливаем три колонки table.setRowCount(1) # и одну строку в таблице # Устанавливаем заголовки таблицы table.setHorizontalHeaderLabels(["Header 1", "Header 2", "Header 3"]) # Устанавливаем всплывающие подсказки на заголовки table.horizontalHeaderItem(0).setToolTip("Column 1 ") table.horizontalHeaderItem(1).setToolTip("Column 2 ") table.horizontalHeaderItem(2).setToolTip("Column 3 ") # Устанавливаем выравнивание на заголовки table.horizontalHeaderItem(0).setTextAlignment(Qt.AlignLeft) table.horizontalHeaderItem(1).setTextAlignment(Qt.AlignHCenter) table.horizontalHeaderItem(2).setTextAlignment(Qt.AlignRight) # заполняем первую строку table.setItem(0, 0, QTableWidgetItem("Text in column 1")) table.setItem(0, 1, QTableWidgetItem("Text in column 2")) table.setItem(0, 2, QTableWidgetItem("Text in column 3")) # делаем ресайз колонок по содержимому table.resizeColumnsToContents() grid_layout.addWidget(table, 0, 0) # Добавляем таблицу в сетку if __name__ == "__main__": import sys app = QApplication(sys.argv) mw = MainWindow() mw.show() sys.exit(app.exec())

evileg.com

PyQt4 — Управление расположением виджетов / Хабр

Важной частью программирования является управление расположением элементов. Управление расположением это то, как мы размещаем виджеты на форме. Тут есть два пути: использование абсолютного позиционирования (absolute positioning) или же использование классов расположения (layout classes).Абсолютное позиционирование Программист указывает положение и размер каждого виджета в пикселях. Когда вы используете абсолютное расположение вы должны понимать несколько вещей: размер и положение виджета не изменяется при изменении размеров окна приложение может выглядеть различно на разных платформах изменение шрифта в вашем приложении может испортить расположение если вы решаете изменить раскладку, вы должны полностью повторить её, что отнимает много времени#!/usr/bin/python import sys from PyQt4 import QtGui

class Absolute(QtGui.QWidget):     def __init__(self, parent=None):         QtGui.QWidget.__init__(self, parent)

        self.setWindowTitle('Communication')

        label = QtGui.QLabel('Couldn\'t', self)         label.move(15, 10)

        label = QtGui.QLabel('care', self)         label.move(35, 40)

        label = QtGui.QLabel('less', self)         label.move(55, 65)

        label = QtGui.QLabel('And', self)         label.move(115, 65)

        label = QtGui.QLabel('then', self)         label.move(135, 45)

        label = QtGui.QLabel('you', self)         label.move(115, 25)

        label = QtGui.QLabel('kissed', self)         label.move(145, 10)

        label = QtGui.QLabel('me', self)         label.move(215, 10)

        self.resize(250, 150)

app = QtGui.QApplication(sys.argv) qb = Absolute() qb.show() sys.exit(app.exec_())

Мы просто вызываем метод move() для изменения положения виджетов, в нашем случае это QLabel. Мы располагаем их согласно координатам X и Y. Начало системы координат находится в левом верхнем углу окна. Координата X растёт справа налево, а Y сверху вниз.

Box Layout (не придумал перевода) Управление расположением с помощью классов раскладки является более гибким и практичным. Это предпочтительный способ расположения виджетов. Простые классы раскладки это QHBoxLayout и QVBoxLayout. Они располагают виджеты горизонтально и вертикально. Представим, что мы хотим разместить две кнопки в правом нижнем углу формы. Чтобы создать такую раскладку мы будем использовать один горизонтальный и один вертикальный ящик (box). Необходимое пространство мы получим добавив фактор растяжения (stretch factor).#!/usr/bin/python

import sys from PyQt4 import QtGui

class BoxLayout(QtGui.QWidget):     def __init__(self, parent=None):         QtGui.QWidget.__init__(self, parent)

        self.setWindowTitle('box layout')

        ok = QtGui.QPushButton("OK")         cancel = QtGui.QPushButton("Cancel")

        hbox = QtGui.QHBoxLayout()         hbox.addStretch(1)         hbox.addWidget(ok)         hbox.addWidget(cancel)

        vbox = QtGui.QVBoxLayout()         vbox.addStretch(1)         vbox.addLayout(hbox)

        self.setLayout(vbox)

        self.resize(300, 150)

app = QtGui.QApplication(sys.argv) qb = BoxLayout() qb.show() sys.exit(app.exec_())ok = QtGui.QPushButton("OK") cancel = QtGui.QPushButton("Cancel") Здесь мы создаём две кнопки QPushButton.hbox = QtGui.QHBoxLayout() hbox.addStretch(1) hbox.addWidget(ok) hbox.addWidget(cancel) Создаём горизонтальную раскладку и добавляем фактор растяжения для обоих кнопок.vbox = QtGui.QVBoxLayout() vbox.addStretch(1) vbox.addLayout(hbox) Создаём верктикальную раскладку.self.setLayout(vbox) И в конце устанавливаем главную раскладку для окна.

QGridLayout Самый универсальный класс раскладок это расположение таблицей. Эта раскладка делит пространство на строки и столбцы. Для её создания используется класс QGridLayout. #!/usr/bin/python

import sys from PyQt4 import QtGui

class GridLayout(QtGui.QWidget):     def __init__(self, parent=None):         QtGui.QWidget.__init__(self, parent)

        self.setWindowTitle('grid layout')

        names = ['Cls', 'Bck', '', 'Close', '7', '8', '9', '/',             '4', '5', '6', '*', '1', '2', '3', '-',             '0', '.', '=', '+']         grid = QtGui.QGridLayout()         j = 0         pos = [(0, 0), (0, 1), (0, 2), (0, 3),             (1, 0), (1, 1), (1, 2), (1, 3),             (2, 0), (2, 1), (2, 2), (2, 3),             (3, 0), (3, 1), (3, 2), (3, 3 ),             (4, 0), (4, 1), (4, 2), (4, 3)]

        for i in names:             button = QtGui.QPushButton(i)             if j == 2:                 grid.addWidget(QtGui.QLabel(''), 0, 2)             else: grid.addWidget(button, pos[j][0], pos[j][1])             j = j + 1

        self.setLayout(grid)

app = QtGui.QApplication(sys.argv) qb = GridLayout() qb.show() sys.exit(app.exec_()) В нашем примере, мы создаём таблицу кнопок. Одну ячейку оставляем пустой, добавляя один виджет QLabel.grid = QtGui.QGridLayout() Здесь мы создаём раскладку таблицей.if j == 2:     grid.addWidget(QtGui.QLabel(''), 0, 2) else:     grid.addWidget(button, pos[j][0], pos[j][1]) Чтобы добавить виджет в таблицу мы должны вызвать метод addWidget(), передав в качестве аргументов виджет, а также номера строки и столбца.

Виджеты могут занимать несколько строк или столбцов и в следующем примере мы покажем это.#!/usr/bin/python

import sys from PyQt4 import QtGui

class GridLayout2(QtGui.QWidget):     def __init__(self, parent=None):         QtGui.QWidget.__init__(self, parent)

        self.setWindowTitle('grid layout')

        title = QtGui.QLabel('Title')         author = QtGui.QLabel('Author')         review = QtGui.QLabel('Review')

        titleEdit = QtGui.QLineEdit()         authorEdit = QtGui.QLineEdit()         reviewEdit = QtGui.QTextEdit()

        grid = QtGui.QGridLayout()         grid.setSpacing(10)

        grid.addWidget(title, 1, 0)         grid.addWidget(titleEdit, 1, 1)

        grid.addWidget(author, 2, 0)         grid.addWidget(authorEdit, 2, 1)

        grid.addWidget(review, 3, 0)         grid.addWidget(reviewEdit, 3, 1, 5, 1)

        self.setLayout(grid)         self.resize(350, 300)

app = QtGui.QApplication(sys.argv) qb = GridLayout2() qb.show() sys.exit(app.exec_())grid = QtGui.QGridLayout() grid.setSpacing(10) Создаём раскладку таблицей и указываем расстояние между виджетами.grid.addWidget(reviewEdit, 3, 1, 5, 1) Если мы добавляем виджет в раскладку, мы можем указать сколько строк или столбцов он объединяет. В нашем случае reviewEdit объединяет 5 строк.

habr.com

Первые программы на PyQt5 [Урок 2]

programmy-na-pyqt5.png

В этой части руководства PyQt5

мы изучим некоторую базовую функциональность.

Этот простой пример выводит маленькое окно. Мы можем делать множество вещей с этим окном. Мы можем менять его размер, раскрывать на весь экран или свернуть в панель задач. Это потребовало бы много программного кода. Кто-то уже запрограммировал эту функциональность. Поскольку это повторяется в большинстве приложений, нет необходимости программировать это с самого начала. PyQt5 – это инструментарий высокого уровня. Если бы мы писали с помощью инструментов более низкого уровня, нижеследующий пример кода мог бы с лёгкостью содержать сотни строк.

#!/usr/bin/python3 # -*- coding: utf-8 -*- import sys from PyQt5.QtWidgets import QApplication, QWidget if __name__ == '__main__': app = QApplication(sys.argv) w = QWidget() w.resize(250, 150) w.move(300, 300) w.setWindowTitle('Simple') w.show() sys.exit(app.exec_())

Пример кода выше выводит маленькое окно на экране.

import sys from PyQt5.QtWidgets import QApplication, QWidget

Здесь мы обеспечили необходимый импорт модулей. Основные виджеты расположены в модуле PyQt5.QtWidgets.

app = QApplication(sys.argv)

Каждое приложение PyQt5 должно создать объект приложения. Параметр sys.argv - это список аргументов из командной строки. Скрипты Python могут быть запущены из программной оболочки. Это один из способов, как мы можем контролировать запуск наших скриптов.

w = QWidget()

Виджет QWidget – это основной класс всех объектов пользовательского интерфейса в PyQt5. Мы обеспечиваем конструктор по умолчанию для QWidget. Конструктор по умолчанию не имеет родителя. Виджет без родителя называется окном.

w.resize(250, 150)

Метод resize() изменяет размер виджета. Здесь задана ширина 250px и высота 150px.

w.move(300, 300)

Метод move() перемещает виджет на позицию с координатами x=300 и y=300 на экране.

w.setWindowTitle('Simple')

Здесь мы устанавливаем название нашего окна. Оно показывается в строке заголовка.

w.show()

Метод show() отображает виджет на экране. Виджет сначала создаётся в памяти и позже показывается на экране.

sys.exit(app.exec_())

Наконец, мы входим в главный цикл приложения. Обработка событий начинается в этой точке. Главный цикл получает события из системы и отправляет их виджетам приложения. Цикл завершается, если мы вызываем метод exit() или главное окно было закрыто. Метод sys.exit() гарантирует чистый выход. Среда будет проинформирована, когда приложение завершится.

Метод exec_() содержит нижнее подчеркивание, потому что exec – уже используемое имя в Python. И, соответственно, имя exec_() было использовано взамен.

Окно Simple

Рисунок: Окно Simple

Иконка приложения

Иконка приложения – это маленькое изображение, которое обычно отображается в верхнем левом углу строки заголовка. В следующем примере мы увидим, как сделать её в PyQt5. Мы также познакомимся с несколькими новыми методами.

#!/usr/bin/python3 # -*- coding: utf-8 -*- import sys from PyQt5.QtWidgets import QApplication, QWidget from PyQt5.QtGui import QIcon class Example(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): self.setGeometry(300, 300, 300, 220) self.setWindowTitle('Icon') self.setWindowIcon(QIcon('web.png')) self.show() if __name__ == '__main__': app = QApplication(sys.argv) ex = Example() sys.exit(app.exec_())

Предыдущий пример был написан в процедурном стиле. Язык программирования Python поддерживает и процедурный, и объектно-ориентированный стиль программирования. Программирование в PyQt5 означает объектно-ориентированное программирование (ООП).

class Example(QWidget): def __init__(self): super().__init__() ...

Есть три важных вещи в объектно-ориентированном программировании – это классы, данные и методы. Здесь мы создаём новый класс с именем Example. Класс Example наследуется из класса QWidget. Это значит, что мы вызываем два конструктора: первый для класса Example и второй для унаследованного класса. Метод super() возвращает объект родителя класса Example и мы вызываем его конструктор. Метод __init__() – это конструктор класса в языке Python.

self.initUI()

Создание GUI поручено методу initUI().

self.setGeometry(300, 300, 300, 220) self.setWindowTitle('Icon') self.setWindowIcon(QIcon('web.png'))

Все три метода были унаследованы из класса QWidget. setGeometry делает две вещи: он определяет место окна на экране и устанавливает его размер. Первые два параметра – позиции x и y нашего окна. Третий – ширина, и четвёртый – высота окна. Фактически, setGeometry сочетает методы resize() и move() в одном. Последний метод устанавливает иконку приложения. Чтобы сделать это, мы создали объект QIcon. QIcon принимает путь к нашей иконке для её отображения.

if __name__ == '__main__': app = QApplication(sys.argv) ex = Example() sys.exit(app.exec_())

Приложение и объекты примера созданы. Главный цикл запущен.

Окно с иконкой

Рисунок: Окно с иконкой

Показ всплывающих подсказок

Мы можем обеспечить любой из наших виджетов всплывающей подсказкой.

#!/usr/bin/python3 # -*- coding: utf-8 -*- import sys from PyQt5.QtWidgets import (QWidget, QToolTip, QPushButton, QApplication) from PyQt5.QtGui import QFont class Example(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): QToolTip.setFont(QFont('SansSerif', 10)) self.setToolTip('This is a <b>QWidget</b> widget') btn = QPushButton('Button', self) btn.setToolTip('This is a <b>QPushButton</b> widget') btn.resize(btn.sizeHint()) btn.move(50, 50) self.setGeometry(300, 300, 300, 200) self.setWindowTitle('Tooltips') self.show() if __name__ == '__main__': app = QApplication(sys.argv) ex = Example() sys.exit(app.exec_())

В этом примере, мы показываем подсказку для двух PyQt5 виджетов.

QToolTip.setFont(QFont('SansSerif', 10))

Этот статический метод устанавливает шрифт, используемый для показа всплывающих подсказок. Мы используем шрифт 10px SansSerif.

self.setToolTip('This is a <b>QWidget</b> widget')

Чтобы создать подсказку, мы вызываем метод setTooltip(). Мы можем использовать HTML форматирование текста.

btn = QPushButton('Button', self) btn.setToolTip('This is a <b>QPushButton</b> widget')

Мы создаём виджет кнопки и устанавливаем всплывающую подсказку для неё.

btn.resize(btn.sizeHint()) btn.move(50, 50)

Меняем размер у кнопки, перемещаем её в окно. Метод sizeHint() даёт рекомендуемый размер для кнопки.

Всплывающие подсказки

Рисунок: Всплывающие подсказки

Закрытие окна

Очевидный способ закрыть окно – это кликнуть на знаке «X» в строке заголовка. В следующем примере, мы покажем, как мы можем программно закрыть наше окно. Мы кратко коснёмся темы сигналов и слотов.

Ниже следует конструктор виджета QPushButton, который мы используем в нашем примере.

QPushButton(string_text, QWidget parent = None)

Параметр string_text – это текст, который будет отображён на кнопке. Parent – это виджет, на котором мы разместим нашу кнопку. В этом случае это будет QWidget.

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

#!/usr/bin/python3 # -*- coding: utf-8 -*- import sys from PyQt5.QtWidgets import QWidget, QPushButton, QApplication from PyQt5.QtCore import QCoreApplication class Example(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): qbtn = QPushButton('Quit', self) qbtn.clicked.connect(QCoreApplication.instance().quit) qbtn.resize(qbtn.sizeHint()) qbtn.move(50, 50) self.setGeometry(300, 300, 250, 150) self.setWindowTitle('Quit button') self.show() if __name__ == '__main__': app = QApplication(sys.argv) ex = Example() sys.exit(app.exec_())

В этом примере, мы создали кнопку выхода. После нажатия на кнопку, приложение завершается.

from PyQt5.QtCore import QCoreApplication

Нам необходим объект из модуля QtCore.

qbtn = QPushButton('Quit', self)

Мы создали кнопку. Кнопка является образцом класса QPushButton. Первый параметр конструктора – это метка кнопки. Второй параметр – родительский виджет. Виджет родителя – это Example, который наследуется из QWidget.

qbtn.clicked.connect(QCoreApplication.instance().quit)

Система обработки событий в PyQt5 построена на механизме сигналов и слотов. Если мы кликаем по кнопке, выдаётся сигнал clicked. Слот может быть слотом Qt или любым слотом, вызываемым из Python. QCoreApplication содержит цикл главного события; он обрабатывает и выполняет все события. Метод instance даёт нам его текущий экземпляр. Заметим, что QCoreApplication создаётся с QApplication. Кликнутый сигнал соединяется с методом quit(), который и завершает приложение. Взаимосвязь сделана между двумя объектами: отправителем и получателем. Отправитель – нажатие кнопки, получатель – объект приложения.

Кнопка выхода

Рисунок: Кнопка выхода

Диалоговое окно

По умолчанию, если вы кликаете по кнопке «X» в строке заголовка, QWidget закрывается. Иногда мы хотим изменить это стандартное поведение. К примеру, если у нас есть открытый в редакторе файл, в котором мы сделали некоторые изменения, мы показываем сообщение для подтверждения выхода.

#!/usr/bin/python3 # -*- coding: utf-8 -*- import sys from PyQt5.QtWidgets import QWidget, QMessageBox, QApplication class Example(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): self.setGeometry(300, 300, 250, 150) self.setWindowTitle('Message box') self.show() def closeEvent(self, event): reply = QMessageBox.question(self, 'Message', "Are you sure to quit?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: event.accept() else: event.ignore() if __name__ == '__main__': app = QApplication(sys.argv) ex = Example() sys.exit(app.exec_())

Если мы закрываем QWidget, вызывается QCloseEvent. Чтобы изменить поведение виджета, нам необходимо переопределить обработчик событий closeEvent().

reply = QMessageBox.question(self, 'Message', "Are you sure to quit?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No)

Мы показываем сообщение с двумя кнопками: «Yes» и «No». Первая строка появляется в строке заголовка. Вторая строка – это текст сообщения, отображаемый с помощью диалогового окна. Третий аргумент указывает комбинацию кнопок, появляющихся в диалоге. Последний параметр – кнопка по умолчанию. Это кнопка, которая первоначально имеет на себе указатель клавиатуры. Возвращаемое значение хранится в переменной reply.

if reply == QtGui.QMessageBox.Yes: event.accept() else: event.ignore()

Здесь мы проверяем возвращаемое значение. Если мы кликаем кнопку «Yes», мы принимаем событие, которое ведёт к закрытию виджета и завершению приложения. В противном случае, мы игнорируем закрывающее событие.

Диалоговое окно

Рисунок: Диалоговое окно

Центрирование окна на экране

Нижеследующий скрипт показывает, как мы можем центрировать окно экране.

#!/usr/bin/python3 # -*- coding: utf-8 -*- import sys from PyQt5.QtWidgets import QWidget, QDesktopWidget, QApplication class Example(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): self.resize(250, 150) self.center() self.setWindowTitle('Center') self.show() def center(self): qr = self.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) if __name__ == '__main__': app = QApplication(sys.argv) ex = Example() sys.exit(app.exec_())

Класс QtGui.QDesktopWidget предоставляет информацию о пользовательском рабочем столе, включая размер экрана.

self.center()

Код, который будет центрировать окно, размещён в специальном методе center().

qr = self.frameGeometry()

Мы получаем прямоугольник, точно определяющий форму главного окна.

cp = QDesktopWidget().availableGeometry().center()

Мы выясняем разрешение экрана нашего монитора. Из этого разрешения, мы получаем центральную точку.

qr.moveCenter(cp)

Наш прямоугольник уже имеет высоту и ширину. Теперь мы устанавливаем центр прямоугольника в центр экрана. Размер прямоугольника не изменяется.

self.move(qr.topLeft())

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

В этой части руководства PyQt5, мы успели изучить некоторые основы...

Продолжение: Меню и панели инструментов в PyQt5 [Урок №3]
Прошлый урок: Введение в PyQt5 [Урок 1]

python-3.ru

Управление макетом в PyQt5 [Урок №4]

Управление макетом в PyQt5 [Урок №4]

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

Абсолютное позиционирование

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

  • Размер и позиция виджета не меняются, если мы меняем размер окна
  • Приложения могут выглядеть по-разному на разных платформах
  • Изменение шрифтов в нашем приложении может испортить макет
  • Если мы решаем изменить наш макет, мы должны полностью переделать его, что утомительно и время-затратно.

Следующий пример размещает виджеты в абсолютных координатах.

#!/usr/bin/python3 # -*- coding: utf-8 -*- import sys from PyQt5.QtWidgets import QWidget, QLabel, QApplication class Example(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): lbl1 = QLabel('Zetcode', self) lbl1.move(15, 10) lbl2 = QLabel('tutorials', self) lbl2.move(35, 40) lbl3 = QLabel('for programmers', self) lbl3.move(55, 70) self.setGeometry(300, 300, 250, 150) self.setWindowTitle('Absolute') self.show() if __name__ == '__main__': app = QApplication(sys.argv) ex = Example() sys.exit(app.exec_())

Мы используем метод move(), чтобы разместить наши виджеты. В этом случае ими являются метки. Мы размещаем их путём предоставления координат x и y. Начало координатной системы – левый верхний угол. Значения x возрастают слева направо. Значения y растут сверху вниз.

lbl1 = QLabel('Zetcode', self) lbl1.move(15, 10)

Виджет метки расположен в x=15 и y=10.

абсолютное позиционирование

Рисунок: абсолютное позиционирование

Блочный макет

Управление макетом с классами макета является более гибким и практичным. Это предпочтительный путь размещения виджетов в окне. QHBoxLayout и QVBoxLayout – это основные классы макета, которые выстраивают виджеты горизонтально или вертикально.

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

#!/usr/bin/python3 # -*- coding: utf-8 -*- import sys from PyQt5.QtWidgets import (QWidget, QPushButton, QHBoxLayout, QVBoxLayout, QApplication) class Example(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): okButton = QPushButton("OK") cancelButton = QPushButton("Cancel") hbox = QHBoxLayout() hbox.addStretch(1) hbox.addWidget(okButton) hbox.addWidget(cancelButton) vbox = QVBoxLayout() vbox.addStretch(1) vbox.addLayout(hbox) self.setLayout(vbox) self.setGeometry(300, 300, 300, 150) self.setWindowTitle('Buttons') self.show() if __name__ == '__main__': app = QApplication(sys.argv) ex = Example() sys.exit(app.exec_())

Пример размещает две кнопки в нижнем правом углу окна. Они остаются там, когда мы изменяем размер окна приложения. Мы можем использовать и HBoxLayout, и QVBoxLayout.

okButton = QPushButton("OK") cancelButton = QPushButton("Cancel") Здесь мы создаём две кнопки. hbox = QHBoxLayout() hbox.addStretch(1) hbox.addWidget(okButton) hbox.addWidget(cancelButton)

Мы создаём макет горизонтального блока, добавляем показатель растяжения и обе кнопки. Растяжение добавляет растянутое свободное пространство перед двумя кнопками. Это прижмёт их к правому краю окна.

vbox = QVBoxLayout() vbox.addStretch(1) vbox.addLayout(hbox)

Чтобы создать необходимый макет, мы поставим горизонтальный макет в вертикальный. Показатель растяжения в вертикальном блоке прижмёт горизонтальный блок с кнопками к нижней части окна.

self.setLayout(vbox)

Наконец, мы устанавливаем главный макет окна.

Кнопки

Рисунок: Кнопки

QGridLayout

Самый универсальный класс макета – это сеточный макет. Этот макет делит пространство на строки и столбцы. Чтобы создать сеточный макет, мы используем класс QGridLayout.

#!/usr/bin/python3 # -*- coding: utf-8 -*- import sys from PyQt5.QtWidgets import (QWidget, QGridLayout, QPushButton, QApplication) class Example(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): grid = QGridLayout() self.setLayout(grid) names = ['Cls', 'Bck', '', 'Close', '7', '8', '9', '/', '4', '5', '6', '*', '1', '2', '3', '-', '0', '.', '=', '+'] positions = [(i,j) for i in range(5) for j in range(4)] for position, name in zip(positions, names): if name == '': continue button = QPushButton(name) grid.addWidget(button, *position) self.move(300, 150) self.setWindowTitle('Calculator') self.show() if __name__ == '__main__': app = QApplication(sys.argv) ex = Example() sys.exit(app.exec_())

В нашем примере, мы создаём сетку из кнопок. Чтобы заполнить один промежуток, мы добавляем один виджет QLabel.

grid = QGridLayout() self.setLayout(grid)

Создаётся экземпляр QGridLayout, он назначается как макет окна приложения.

names = ['Cls', 'Bck', '', 'Close', '7', '8', '9', '/', '4', '5', '6', '*', '1', '2', '3', '-', '0', '.', '=', '+']

Это метки, используемые в дальнейшем для кнопок.

positions = [(i,j) for i in range(5) for j in range(4)]

Мы создаём список позиций для сетки.

for position, name in zip(positions, names): if name == '': continue button = QPushButton(name) grid.addWidget(button, *position)

Используя метод addWidget, создаются и добавляются кнопки к макету.

Каркас калькулятора

Рисунок: Каркас калькулятора

Обзорный пример

Виджеты могут охватывать несколько столбцов и строк в сетке. В следующем примере мы продемонстрируем это.

#!/usr/bin/python3 # -*- coding: utf-8 -*- import sys from PyQt5.QtWidgets import (QWidget, QLabel, QLineEdit, QTextEdit, QGridLayout, QApplication) class Example(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): title = QLabel('Title') author = QLabel('Author') review = QLabel('Review') titleEdit = QLineEdit() authorEdit = QLineEdit() reviewEdit = QTextEdit() grid = QGridLayout() grid.setSpacing(10) grid.addWidget(title, 1, 0) grid.addWidget(titleEdit, 1, 1) grid.addWidget(author, 2, 0) grid.addWidget(authorEdit, 2, 1) grid.addWidget(review, 3, 0) grid.addWidget(reviewEdit, 3, 1, 5, 1) self.setLayout(grid) self.setGeometry(300, 300, 350, 300) self.setWindowTitle('Review') self.show() if __name__ == '__main__': app = QApplication(sys.argv) ex = Example() sys.exit(app.exec_())

Мы создаём окно, в котором мы имеем три метки, две строки правки и один виджет редактирования текста. Макет сделан с помощью QGridLayout.

grid = QGridLayout() grid.setSpacing(10)

Мы создаём сеточный макет и устанавливаем промежуток между виджетами.

grid.addWidget(reviewEdit, 3, 1, 5, 1)

Если мы добавляем виджет к сетке, мы можем обеспечить объединение строк и столбцов виджета. В нашем примере, мы делаем так, что виджет reviewEdit объединяет 5 строк.

Обзорный пример

Рисунок: Обзорный пример

Эта часть руководства по PyQt5 была посвящена управлению макетом.

Продолжение: События и сигналы в PyQt5 [Урок №5]
Прошлый урок: Меню и панели инструментов в PyQt5 [Урок №3]

python-3.ru

python - Как в PyQt5 изменить GUI через сторонний поток?

Вот часть программы

# -*- coding: utf-8 -*- import PyQt5.QtWidgets import PyQt5.QtCore import sys import time class MyThread(PyQt5.QtCore.QThread): def __init__(self, parent=None): PyQt5.QtCore.QThread.__init__(self, parent) def run(self): time.sleep(60) # ? class MyTabWidget(PyQt5.QtWidgets.QTabWidget): def __init__(self, parent=None): PyQt5.QtWidgets.QTabWidget.__init__(self, parent) self.widget = PyQt5.QtWidgets.QWidget() self.en = PyQt5.QtWidgets.QLineEdit(self.widget) self.ru = PyQt5.QtWidgets.QLineEdit(self.widget) self.ipa = PyQt5.QtWidgets.QLineEdit(self.widget) self.add = PyQt5.QtWidgets.QPushButton("add") self.form = PyQt5.QtWidgets.QFormLayout(self.widget) self.form.addRow("&En", self.en) self.form.addRow("&Ru", self.ru) self.form.addRow("&IPA", self.ipa) self.form.addRow(self.add) self.widget2 = PyQt5.QtWidgets.QWidget() self.repeat = PyQt5.QtWidgets.QPushButton("repeat") self.form2 = PyQt5.QtWidgets.QFormLayout(self.widget2) self.form2.addRow(self.repeat) self.addTab(self.widget, "add") self.addTab(self.widget2, "repeat") self.th = MyThread() self.th.start() if __name__ == "__main__": tab = MyTabWidget() tab.show() app = PyQt5.QtWidgets.QApplication(sys.argv) sys.exit(app.exec_())

результатом которого является:

alt text

Как мне, в запущенном потоке th, изменить надпись repeat на ***repeat?

alt text

В четвертой версии PyQt знаю данные потока GUI изменялись через сигнал. Но в 5-ой создание сигнала немного по другому. Там как-то с PyQt5.QtCore.pyqtSignal надо мудрить. Вот здесь я и запутался.

ru.stackoverflow.com