Это было обсуждено не так давно в списке рассылки PyQt. Цитируя комментарии Джованни Баджо на эту тему:
Это в основном то же самое. Основное отличие состоит в том, что QThreads лучше интегрированы с Qt (асинхронные сигналы / слоты, цикл событий и т. Д.). Кроме того, вы не можете использовать Qt из потока Python (вы не можете, например, отправить событие в основной поток через QApplication.postEvent): для работы вам нужен QThread.
Общее правило может заключаться в использовании QThreads, если вы собираетесь каким-то образом взаимодействовать с Qt и использовать потоки Python в противном случае.
И некоторые более ранние комментарии по этому вопросу от автора PyQt: «они оба обертки вокруг тех же реализаций собственных потоков». И обе реализации используют GIL таким же образом.
Нити Python будут проще и безопаснее, и поскольку они предназначены для приложения на основе ввода-вывода, они могут обойти GIL.
EDIT: больше по потокам
Нити Python
Потоки Python – это системные потоки. Тем не менее, Python использует глобальную блокировку интерпретатора (GIL), чтобы гарантировать, что интерпретатор выполняет только определенный блок размера инструкций байтового кода за раз. К счастью, Python выпускает GIL во время операций ввода-вывода, делая потоки полезными для имитации неблокирующего ввода-вывода.
Важное предупреждение: это может ввести в заблуждение, так как количество инструкций байт-кода не соответствует количеству строк в программе. Даже одно назначение не может быть атомарным в Python, поэтому блокировка мьютекса необходима для любого блока кода, который должен выполняться атомарно, даже с GIL.
Потоки QT
Когда Python отключает управление сторонним компилированным модулем, он выпускает GIL. В обязанности модуля входит обеспечение атомарности, когда это необходимо. Когда управление передается обратно, Python будет использовать GIL. Это может сделать использование сторонних библиотек в связи с запутанностью потоков. Еще сложнее использовать внешнюю библиотеку потоков, поскольку она добавляет неопределенность относительно того, где и когда управление находится в руках модуля и интерпретатора.
Потоки QT работают с выпущенным GIL. Потоки QT могут выполнять код библиотеки QT (и другой код скомпилированного модуля, который не получает GIL) одновременно. Тем не менее, код Python, выполняемый в контексте потока QT, по- прежнему приобретает GIL, и теперь вам нужно управлять двумя наборами логики для блокировки вашего кода.
В конце концов, как потоки QT, так и потоки Python являются обертками вокруг потоков системы. Потоки Python являются более безопасными для использования, поскольку те части, которые не написаны на Python (неявно использующие GIL), используют GIL в любом случае (хотя вышеописанное предостережение применяется).
Неблокирующий ввод-вывод
Темы добавляют необычайную сложность вашему приложению. Особенно при работе с уже сложным взаимодействием между интерпретатором Python и скомпилированным модульным кодом. В то время как многие находят, что программирование на основе событий сложно отслеживать, неблокирующий ввод-вывод, основанный на событиях, часто гораздо менее сложно рассуждать, чем потоки.
С асинхронным вводом-выводом вы всегда можете быть уверены, что для каждого открытого дескриптора путь выполнения согласован и упорядочен. Есть, очевидно, проблемы, которые необходимо решить, например, что делать, когда код, зависящий от одного открытого канала, зависит от результатов кода, который будет вызываться, когда другой открытый канал возвращает данные.
Одним из прекрасных решений для ввода-вывода без событий, основанных на событиях, является новая библиотека Diesel . На данный момент он ограничен Linux, но он необычайно быстрый и довольно элегантный.
Также стоит потратить время на изучение pyevent , обертки вокруг замечательной библиотеки libevent, которая обеспечивает базовую основу для программирования на основе событий, используя самый быстрый доступный для вашей системы метод (определенный во время компиляции).
Я задал себе тот же вопрос, когда работал над PyTalk .
Если вы используете Qt, вам нужно использовать QThread
чтобы иметь возможность использовать структуру Qt и, в первую очередь, систему сигналов / слотов.
С движком сигнала / слота вы сможете разговаривать из потока в другой и с каждой частью вашего проекта.
Более того, вопрос об этом выборе очень невелик, так как оба являются связями C ++.
Вот мой опыт PyQt и потока.
Я рекомендую вам использовать QThread
.
Я тоже не могу рекомендовать, но я могу попытаться описать различия между потоками CPython и Qt.
Прежде всего, потоки CPython не запускаются одновременно, по крайней мере, не с кодом Python. Да, они создают системные потоки для каждого потока Python, однако разрешен запуск только потока, в котором сейчас выполняется Global Interpreter Lock (расширения C и код FFI могут обходить его, но байт-код Python не выполняется, пока поток не содержит GIL).
С другой стороны, у нас есть потоки Qt, которые в основном являются общими для системных потоков, не имеют Global Interpreter Lock и, таким образом, могут работать одновременно. Я не уверен, как PyQt справляется с этим, однако, если ваши потоки Qt не назовут код Python, они должны иметь возможность запускать одновременно (блокировать различные дополнительные блокировки, которые могут быть реализованы в различных структурах).
Для дополнительной тонкой настройки вы можете изменить количество инструкций байт-кода, которые интерпретируются перед переключением права собственности на GIL – более низкие значения означают большее переключение контекста (и, возможно, более высокую отзывчивость), но более низкую производительность для каждого потока (коммутаторы контекста имеют свою стоимость – если вы попробуйте переключить каждые несколько инструкций, это не ускорит скорость. )
Надеюсь, что это поможет с вашими проблемами 🙂
Потоки Python будут проще и безопаснее, и, поскольку они предназначены для приложений на основе ввода-вывода, они могут обходить GIL. Тем не менее, рассматривали ли вы неблокирующий ввод-вывод с использованием Twisted или неблокирующих сокетов / select?
РЕДАКТИРОВАТЬ: подробнее о потоках
Потоки Python
Потоки Python — это системные потоки. Однако Python использует глобальную блокировку интерпретатора (GIL), чтобы гарантировать, что интерпретатор одновременно выполняет только блок инструкций байтового кода определенного размера. К счастью, Python выпускает GIL во время операций ввода / вывода, делая потоки полезными для моделирования неблокирующего ввода / вывода.
Важное предупреждение: это может ввести в заблуждение, поскольку количество инструкций байт-кода не соответствует количеству строк в программе.
Даже одно присвоение не может быть атомарным в Python, поэтому блокировка мьютекса необходима для любого блока кода, который должен выполняться атомарно, даже с GIL.QT потоки
Когда Python передает управление стороннему скомпилированному модулю, он освобождает GIL. Ответственность за обеспечение атомарности там, где это необходимо, ложится на модуль. Когда управление будет передано обратно, Python будет использовать GIL. Это может сбивать с толку использование сторонних библиотек в сочетании с потоками. Еще сложнее использовать внешнюю библиотеку потоков, потому что это добавляет неопределенности относительно того, где и когда управление находится в руках модуля, а не интерпретатора.
Потоки QT работают с выпущенным GIL. Потоки QT могут выполнять код библиотеки QT (и другой код скомпилированного модуля, который не получает GIL) одновременно. Однако код Python, выполняемый в контексте потока QT, по- прежнему получает GIL, и теперь вам нужно управлять
В конце концов, и потоки QT, и потоки Python являются оболочкой для системных потоков. Потоки Python немного безопаснее использовать, поскольку те части, которые написаны не на Python (неявно с использованием GIL), в любом случае используют GIL (хотя приведенное выше предостережение все еще применяется).
Неблокирующий ввод / вывод
Потоки чрезвычайно усложняют ваше приложение. Особенно при и без того сложном взаимодействии интерпретатора Python и скомпилированного кода модуля. Хотя многим сложно следовать программированию на основе событий, неблокирующий ввод-вывод, основанный на событиях, зачастую гораздо труднее рассуждать, чем потоки.
С асинхронным вводом-выводом вы всегда можете быть уверены, что для каждого открытого дескриптора путь выполнения согласован и упорядочен. Очевидно, есть проблемы, которые необходимо решить, например, что делать, когда код, зависящий от одного открытого канала, в дальнейшем зависит от результатов кода, который будет вызываться, когда другой открытый канал возвращает данные.
Хорошим решением для событийного неблокирующего ввода-вывода является новая библиотека Diesel . На данный момент он ограничен Linux, но он необычайно быстр и довольно элегантен.
Также стоит потратить время на изучение pyevent , оболочки для замечательной библиотеки libevent, которая обеспечивает базовую структуру для программирования на основе событий с использованием самого быстрого доступного метода для вашей системы (определяется во время компиляции).
Я пишу приложение с графическим интерфейсом, которое регулярно получает данные через веб-соединение. Так как этот поиск занимает некоторое время, это приводит к тому, что пользовательский интерфейс перестает отвечать на запросы в процессе поиска (его нельзя разбить на более мелкие части). Вот почему я хотел бы передать веб-соединение в отдельный рабочий поток.
[Да, я знаю, сейчас у меня две проблемы а> . ]
В любом случае, приложение использует PyQt4, поэтому я хотел бы знать, какой вариант лучше: использовать потоки Qt или использовать модуль Python threading
? Каковы преимущества / недостатки каждого? Или у тебя совершенно другое предложение?
Изменить (re bounty). Хотя в моем конкретном случае решение, вероятно, будет использовать неблокирующий сетевой запрос, например Джефф Обер и предложил Лукаш Лалинский (так что, в сущности, оставляя проблемы с параллелизмом для сетевой реализации), я бы по-прежнему как более глубокий ответ на общий вопрос:
Каковы преимущества и недостатки использования потоков PyQt4 (т.е. Qt) по сравнению с собственными потоками Python (из модуля threading
)?
Изменить 2 . Спасибо всем за ответы. Хотя не существует 100% -ого согласия, похоже, что существует широко распространенное мнение, что ответом является «использовать Qt», поскольку его преимущество заключается в интеграции с остальной частью библиотеки, не вызывая при этом реальных недостатков.
Для тех, кто хочет выбрать между двумя реализациями потоков, я настоятельно рекомендую прочитать все ответы, представленные здесь, включая ветку списка рассылки PyQt, которая abbot ссылается на.
Было несколько ответов, которые я рассмотрел для награды; в конце концов я выбрал настоятеля для очень релевантной внешней ссылки; это был, однако, близкий звонок.
Еще раз спасибо.
120
balpha 20 Окт 2009 в 19:54
Лучший ответ
Это обсуждалось не так давно в списке рассылки PyQt. Цитируя комментарии Джованни Баджо на эту тему:
Это в основном то же самое. Основное отличие состоит в том, что QThreads лучше интегрированы с Qt (асинхронные сигналы / слоты, цикл обработки событий и т. Д.). Кроме того, вы не можете использовать Qt из потока Python (например, вы не можете публиковать события в главном потоке через QApplication. postEvent): вам нужен QThread, чтобы это работало.
Общее практическое правило может заключаться в использовании QThreads, если вы собираетесь каким-либо образом взаимодействовать с Qt, и в противном случае использовать потоки Python.
И некоторые ранее прокомментировали эту тему от автора PyQt: «они оба являются обертками вокруг одних и тех же собственных реализаций потоков». И обе реализации используют GIL одинаково.
110
abbot 3 Апр 2012 в 15:33
Я не могу прокомментировать точные различия между потоками Python и PyQt, но я делал то, что вы пытаетесь сделать, используя QThread
, QNetworkAcessManager
и проверяя, чтобы вызвать QApplication.processEvents()
пока нить жива. Если реакция GUI действительно является проблемой, которую вы пытаетесь решить, позже поможет.
0
brianz 25 Окт 2009 в 05:24
Я тоже не могу рекомендовать, но я могу попытаться описать различия между потоками CPython и Qt.
Прежде всего, потоки CPython не работают одновременно, по крайней мере, не код Python. Да, они создают системные потоки для каждого потока Python, однако разрешено запускать только поток, в настоящее время содержащий глобальную блокировку интерпретатора (расширения C и код FFI могут его обойти, но байт-код Python не выполняется, пока поток не содержит GIL).
С другой стороны, у нас есть потоки Qt, которые по сути являются общим слоем над системными потоками, не имеют глобальной блокировки интерпретатора и, следовательно, способны работать одновременно. Я не уверен, как PyQt справляется с этим, однако, если ваши потоки Qt не вызывают код Python, они должны быть в состоянии работать одновременно (за исключением различных дополнительных блокировок, которые могут быть реализованы в различных структурах).
Для дополнительной тонкой настройки вы можете изменить количество инструкций байт-кода, которые интерпретируются перед переключением владельца GIL — более низкие значения означают большее переключение контекста (и, возможно, более высокую скорость отклика), но более низкую производительность для отдельного потока (переключатели контекста имеют свою стоимость — если вы попробуйте переключать каждые несколько инструкций, это не помогает скорости.)
Надеюсь, это поможет с вашими проблемами 🙂
5
p_l 28 Окт 2009 в 09:15
У Джеффа есть несколько хороших моментов. Только один основной поток может выполнять любые обновления графического интерфейса. Если вам нужно обновить графический интерфейс из потока, Qt-4 сигналы подключения в очереди упрощают отправку данных между потоками и будут автоматически вызываться, если вы используете QThread; Я не уверен, будут ли они, если вы используете потоки Python, хотя добавить параметр в connect()
легко.
9
ekhumoro 19 Янв 2017 в 17:29
Я задавал себе тот же вопрос, когда работал над PyTalk.
Если вы используете Qt, вам нужно использовать QThread
, чтобы иметь возможность использовать каркас Qt и особенно систему сигналов / слотов.
С помощью механизма сигналов / слотов вы сможете общаться из одного потока в другой и с каждой частью вашего проекта.
Более того, вопрос об этом выборе не очень высок, поскольку оба являются привязками C ++.
Вот мой опыт PyQt и потока.
Я рекомендую вам использовать QThread
.
14
Natim 27 Окт 2009 в 01:17
Преимущество QThread
заключается в том, что он интегрирован с остальной частью библиотеки Qt. То есть методы с поддержкой потоков в Qt должны будут знать, в каком потоке они работают, и для перемещения объектов между потоками вам нужно будет использовать QThread
. Еще одна полезная функция — запуск собственного цикла событий в потоке.
Если вы обращаетесь к HTTP-серверу, вы должны рассмотреть QNetworkAccessManager
.
21
Lukáš Lalinský 20 Окт 2009 в 16:11
Потоки Python будут проще и безопаснее, и, поскольку они предназначены для приложений на основе ввода / вывода, они могут обходить GIL. Тем не менее, вы рассматривали неблокирующие операции ввода-вывода с использованием витых или неблокирующих сокетов / select?
РЕДАКТИРОВАТЬ: больше о темах
Темы Python
Потоки Python — это системные потоки. Однако Python использует глобальную блокировку интерпретатора (GIL), чтобы гарантировать, что интерпретатор только когда-либо выполняет блок байтового кода определенного размера за раз. К счастью, Python освобождает GIL во время операций ввода / вывода, что делает потоки полезными для имитации неблокирующего ввода / вывода.
Важное предупреждение: Это может вводить в заблуждение, поскольку число инструкций байт-кода не соответствует количеству строк в программе. Даже одно назначение не может быть атомарным в Python, поэтому блокировка мьютекса необходима для любого блока кода, который должен выполняться атомарно, даже с GIL.
Темы QT
Когда Python передает управление стороннему скомпилированному модулю, он освобождает GIL. Модуль несет ответственность за обеспечение атомарности, где это необходимо. Когда управление передается обратно, Python будет использовать GIL. Это может затруднить использование сторонних библиотек в сочетании с потоками. Еще сложнее использовать внешнюю библиотеку потоков, поскольку она добавляет неопределенность относительно того, где и когда управление находится в руках модуля против интерпретатора.
Потоки QT работают с освобожденным GIL. Потоки QT могут выполнять код библиотеки QT (и другой код скомпилированного модуля, который не получает GIL) одновременно. Однако код Python, выполняемый в контексте потока QT still , получает GIL, и теперь вам нужно управлять двумя наборами логики для блокировки вашего кода.
В конце и потоки QT, и потоки Python являются обертками вокруг системных потоков. Потоки Python немного безопаснее в использовании, поскольку те части, которые не написаны на Python (неявно используют GIL), в любом случае используют GIL (хотя оговорка выше все еще применима).
Неблокирующий ввод / вывод
Потоки чрезвычайно усложняют ваше приложение. Особенно, когда речь идет о и без того сложном взаимодействии между интерпретатором Python и скомпилированным модульным кодом. В то время как многим трудно следовать программированию на основе событий, неблокирующий ввод-вывод на основе событий зачастую гораздо менее труден для рассуждения, чем потоки.
Благодаря асинхронному вводу-выводу вы всегда можете быть уверены, что для каждого открытого дескриптора путь выполнения является согласованным и упорядоченным. Очевидно, есть проблемы, которые необходимо решить, например, что делать, когда код, зависящий от одного открытого канала, далее зависит от результатов кода, вызываемого, когда другой открытый канал возвращает данные.
Хорошим решением для неблокирующего ввода-вывода на основе событий является новая дизельная библиотека. На данный момент он ограничен Linux, но он чрезвычайно быстрый и довольно элегантный.
Также стоит потратить время на изучение pyevent — обертки вокруг замечательной библиотеки libevent, которая предоставляет базовая основа для программирования на основе событий с использованием самого быстрого метода, доступного для вашей системы (определяется во время компиляции).
34
Jeff Ober 29 Окт 2009 в 11:52
Класс QMediaStreamsControl предоставляет элемент управления выбором медиапотока. Подробнее …
Заголовок: | #include |
qmake: | QT + = multimedia |
Наследует: | QMediaControl |
Этот класс устарел. Он предназначен для сохранения работоспособности старого исходного кода. Мы настоятельно не рекомендуем использовать его в новом коде.
перечисление | StreamType {AudioStream, VideoStream, SubPictureStream, UnknownStream, DataStream} |
Класс QMediaStreamsControl предоставляет описания доступных медиапотоков и позволяет активировать и деактивировать отдельные потоки.
Имя интерфейса QMediaStreamsControl — org. qt-project.qt.mediastreamscontrol / 5.0
, как определено в QMediaStreamsControl_iid.
См. Также QMediaService :: requestControl ().
Тип медиапотока.
Константа | Значение | Описание |
---|---|---|
QMediaStreamsControl :: AudioStream | 2 | Аудиопоток. |
QMediaStreamsControl :: VideoStream | 1 | Видеопоток. |
QMediaStreamsControl :: SubPictureStream | 3 | Поток фрагмента изображения или телетекста. |
QMediaStreamsControl :: UnknownStream | 0 | Тип потока неизвестен. |
QMediaStreamsControl :: DataStream | 4 |
[защищенный]
QMediaStreamsControl :: QMediaStreamsControl (родительский объект QObject * = nullptr)Создает новый элемент управления потоками мультимедиа с заданным родительским элементом .
[сигнал]
void QMediaStreamsControl :: activeStreamsChanged ()Сигнал излучается при изменении списка активных потоков.
[сигнал]
void QMediaStreamsControl :: streamsChanged ()Сигнал излучается при изменении списка доступных потоков.
[виртуальный]
QMediaStreamsControl :: ~ QMediaStreamsControl ()Уничтожает элемент управления медиа-потоками.
[чисто виртуальный]
bool QMediaStreamsControl :: isActive (int streamNumber )Возвращает истину, если медиапоток streamNumber активен.
[чисто виртуальный]
QVariant QMediaStreamsControl :: metaData (int streamNumber , const QString & key )Возвращает значение метаданных ключа для данного streamNumber .
Полезные ключи метаданных: QMediaMetaData :: Title, QMediaMetaData :: Description и QMediaMetaData :: Language.
[чисто виртуальный]
void QMediaStreamsControl :: setActive (int streamNumber , bool state )Устанавливает активное состояние медиапотока streamNumber .
Установка для активного состояния медиапотока значения true активирует его. Если ранее был активен какой-либо другой поток того же типа, он будет деактивирован. Установка активного состояния медиапотока на false деактивирует его.
См. Также isActive ().
[чисто виртуальный]
int QMediaStreamsControl :: streamCount ()Возвращает количество медиапотоков.
[чисто виртуальный]
QMediaStreamsControl :: StreamType QMediaStreamsControl :: streamType (int streamNumber )Вернуть тип медиапотока streamNumber .
org.qt-project.qt.mediastreamscontrol / 5.0
Определяет имя интерфейса класса QMediaStreamsControl.
© 2021 Компания Qt Company Ltd. Вклад в документацию, включенный в этот документ, является собственностью их владельцы. Представленная здесь документация лицензирована в соответствии с условиями лицензии GNU Free Documentation License версии 1.3, опубликованной Free Software Foundation.Qt и соответствующие логотипы являются товарными знаками компании Qt Company Ltd. в Финляндии и / или других странах по всему миру. Все остальные товарные знаки являются собственностью их владельцев.
Класс QFileDevice предоставляет интерфейс для чтения и записи в открытые файлы. Подробнее …
Этот класс был представлен в Qt 5.0.
Это перечисление описывает ошибки, которые могут быть возвращены функцией error ().
Константа | Значение | Описание |
---|---|---|
QFileDevice :: NoError | 0 | Ошибка не возникла. |
QFileDevice :: ReadError | 1 | Произошла ошибка при чтении из файла. |
QFileDevice :: WriteError | 2 | Произошла ошибка при записи в файл. |
QFileDevice :: FatalError | 3 | Произошла фатальная ошибка. |
QFileDevice :: ResourceError | 4 | Недостаточно ресурсов (например, слишком много открытых файлов, нехватка памяти и т. Д.) |
QFileDevice :: OpenError | 5 | Не удалось открыть файл. |
QFileDevice :: AbortError | 6 | Операция была прервана. |
QFileDevice :: TimeOutError | 7 | Истекло время ожидания. |
QFileDevice :: UnspecifiedError | 8 | Произошла неопределенная ошибка. |
QFileDevice :: RemoveError | 9 | Не удалось удалить файл. |
QFileDevice :: RenameError | 10 | Не удалось переименовать файл. |
QFileDevice :: PositionError | 11 | Позиция в файле не может быть изменена. |
QFileDevice :: ResizeError | 12 | Не удалось изменить размер файла. |
QFileDevice :: PermissionsError | 13 | Нет доступа к файлу. |
QFileDevice :: CopyError | 14 | Не удалось скопировать файл. |
Это перечисление используется при открытии файла для указания дополнительных параметров, которые применяются только к файлам, а не к общему QIODevice.
Константа | Значение | Описание |
---|---|---|
QFileDevice :: AutoCloseHandle | 0x0001 | Дескриптор файла, переданный в open (), должен быть закрыт с помощью close (), поведение по умолчанию таково, что закрытие просто сбрасывает файл, и приложение отвечает за закрытие дескриптора файла.При открытии файла по имени этот флаг игнорируется, поскольку Qt всегда владеет дескриптором файла и должна его закрыть. |
QFileDevice :: DontCloseHandle | 0 | Если не закрыто явно, базовый дескриптор файла остается открытым при уничтожении объекта QFile. |
Тип FileHandleFlags - это typedef для QFlags
Это перечисление используется функциями fileTime () и setFileTime ().
Константа | Значение | Описание |
---|---|---|
QFileDevice :: FileAccessTime | 0 | Время последнего обращения к файлу (например, чтение или запись). |
QFileDevice :: FileBirthTime | 1 | Время создания файла (может не поддерживаться в UNIX). |
QFileDevice :: FileMetadataChangeTime | 2 | Время последнего изменения метаданных файла. |
QFileDevice :: FileModificationTime | 3 | Время последнего изменения файла. |
Это перечисление было введено или изменено в Qt 5.10.
См. Также setFileTime (), fileTime () и QFileInfo :: fileTime ().
ПеречислениеЭто перечисление описывает специальные параметры, которые могут использоваться функцией map ().
Константа | Значение | Описание |
---|---|---|
QFileDevice :: NoOptions | 0 | Без опций. |
QFileDevice :: MapPrivateOption | 0x0001 | Отображаемая память будет частной, поэтому любые изменения не будут видны другим процессам и не будут записаны на диск. Любые такие модификации будут потеряны, когда память не будет отображена. Не указано, будут ли изменения, внесенные в файл, сделанные после создания сопоставления, видны через отображаемую память. Это значение перечисления было введено в Qt 5.4. |
Это перечисление было введено или изменено в Qt 4.4.
Это перечисление используется функцией permission () для сообщения о разрешениях и владении файлом. Значения могут быть объединены оператором ИЛИ для проверки нескольких значений разрешений и владения.
Константа | Значение | Описание |
---|---|---|
QFileDevice :: ReadOwner | 0x4000 | Файл доступен для чтения владельцем файла. |
QFileDevice :: WriteOwner | 0x2000 | Файл доступен для записи владельцем файла. |
QFileDevice :: ExeOwner | 0x1000 | Файл является исполняемым владельцем файла. |
QFileDevice :: ReadUser | 0x0400 | Пользователь может читать файл. |
QFileDevice :: WriteUser | 0x0200 | Пользователь доступен для записи. |
QFileDevice :: ExeUser | 0x0100 | Пользователь может исполнять файл. |
QFileDevice :: ReadGroup | 0x0040 | Файл доступен для чтения группой. |
QFileDevice :: WriteGroup | 0x0020 | Группа доступна для записи в файл. |
QFileDevice :: ExeGroup | 0x0010 | Файл может исполняться группой. |
QFileDevice :: ReadOther | 0x0004 | Файл доступен для чтения всем. |
QFileDevice :: WriteOther | 0x0002 | Файл доступен для записи любому пользователю. |
QFileDevice :: ExeOther | 0x0001 | Файл может быть выполнен кем угодно. |
Предупреждение: Из-за различий в платформах, поддерживаемых Qt, семантика ReadUser, WriteUser и ExeUser зависит от платформы: в Unix возвращаются права владельца файла, а в Windows - права текущего пользователя. возвращаются.Это поведение может измениться в будущей версии Qt.
Примечание: В файловых системах NTFS проверка прав собственности и разрешений по умолчанию отключена из соображений производительности. Чтобы включить его, включите следующую строку:
внешний Q_CORE_EXPORT int qt_ntfs_permission_lookup;
Проверка разрешений затем включается и выключается путем увеличения и уменьшения qt_ntfs_permission_lookup
на 1.
qt_ntfs_permission_lookup ++; qt_ntfs_permission_lookup--;
Тип разрешений - это typedef для QFlags
[виртуальный]
QFileDevice :: ~ QFileDevice ()Уничтожает файловое устройство, при необходимости закрывая его.
[переопределить виртуальный]
bool QFileDevice :: atEnd () constПереопределения: QIODevice :: atEnd () const.
Возвращает true
, если достигнут конец файла; в противном случае возвращает false.
Для обычных пустых файлов в Unix (например,грамм. те, что в / proc
), эта функция возвращает true
, поскольку файловая система сообщает, что размер такого файла равен 0. Следовательно, вы не должны зависеть от atEnd () при чтении данных из такого файла, а скорее вызывайте read () до тех пор, пока не перестанут считываться данные.
[переопределить виртуальный]
void QFileDevice :: close ()Переопределения: QIODevice :: close ().
Вызывает QFileDevice :: flush () и закрывает файл. Ошибки сброса игнорируются.
См. Также QIODevice :: close ().
Возвращает состояние ошибки файла.
Состояние устройства ввода-вывода возвращает код ошибки. Например, если open () возвращает false
или операция чтения / записи возвращает -1, эту функцию можно вызвать, чтобы выяснить причину сбоя операции.
См. Также unsetError ().
[виртуальный]
QString QFileDevice :: fileName () constВозвращает имя файла.Реализация по умолчанию в QFileDevice возвращает пустую строку.
Возвращает время файла, указанное время . Если время не может быть определено, верните QDateTime () (недопустимая дата и время).
Эта функция была представлена в Qt 5.10.
См. Также setFileTime (), FileTime и QDateTime :: isValid ().
Сбрасывает все буферизованные данные в файл.В случае успеха возвращает true
; в противном случае возвращает false
.
Возвращает дескриптор файла.
Это небольшое положительное целое число, подходящее для использования с функциями библиотеки C, такими как fdopen ()
и fcntl ()
. В системах, которые используют файловые дескрипторы для сокетов (например, системы Unix, но не Windows), дескриптор также может использоваться с QSocketNotifier.
Если файл не открыт или произошла ошибка, handle () возвращает -1.
См. Также QSocketNotifier.
[переопределить виртуальный]
bool QFileDevice :: isSequential () constПереопределения: QIODevice :: isSequential () const.
Возвращает true
, если с файлом можно работать только последовательно; в противном случае возвращает false
.
Большинство файлов поддерживают произвольный доступ, но некоторые специальные файлы не могут.
См. Также QIODevice :: isSequential ().
Отображает размером байта файла в память, начиная со смещения .Файл должен быть открыт для успешного сопоставления, но файл не обязательно должен оставаться открытым после сопоставления памяти. Когда QFile уничтожается или открывается новый файл с этим объектом, любые карты, которые не были отображены, автоматически отключаются.
Отображение будет иметь тот же открытый режим, что и файл (чтение и / или запись), за исключением случаев использования MapPrivateOption, и в этом случае всегда возможна запись в отображаемую память.
Любые параметры отображения могут быть переданы через флагов .
Возвращает указатель на память или nullptr
в случае ошибки.
См. Также unmap ().
[виртуальный]
QFileDevice :: Permissions QFileDevice :: permissions () constВозвращает полную комбинацию OR-ed вместе QFile :: Permission для файла.
См. Также setPermissions ().
[переопределить виртуальный]
qint64 QFileDevice :: pos () constПереопределения: QIODevice :: pos () const.
[переопределить виртуальную защиту]
qint64 QFileDevice :: readData (char * data , qint64 len )Переопределения: QIODevice :: readData (char * data, qint64 maxSize).
[переопределить виртуальную защиту]
qint64 QFileDevice :: readLineData (char * data , qint64 maxlen )Переопределения: QIODevice :: readLineData (char * data, qint64 maxSize).
[виртуальный]
bool QFileDevice :: resize (qint64 sz ) Устанавливает размер файла (в байтах) sz .Возвращает true
, если изменение размера выполнено успешно; в противном случае - ложь. Если sz больше, чем текущий файл, новые байты будут установлены в 0; если sz меньше, файл просто обрезается.
Предупреждение: Эта функция может завершиться ошибкой, если файл не существует.
См. Также размер ().
[переопределить виртуальный]
bool QFileDevice :: seek (qint64 pos )Переопределения: QIODevice :: seek (qint64 pos).
Для устройств с произвольным доступом эта функция устанавливает текущую позицию на pos , возвращая истину в случае успеха или ложь в случае ошибки. Для последовательных устройств поведение по умолчанию - ничего не делать и возвращать false.
Поиск за пределами конца файла: если позиция находится за пределами конца файла, функция seek () не будет немедленно расширять файл. Если запись выполняется в эту позицию, файл будет расширен. Содержимое файла между предыдущим концом файла и вновь записанными данными НЕОПРЕДЕЛЕННО и варьируется в зависимости от платформы и файловой системы.
Устанавливает время файла, заданное параметром fileTime , равным newDate , возвращая истину в случае успеха; в противном случае возвращает false.
Примечание: Для использования этой функции файл должен быть открыт.
Эта функция была представлена в Qt 5.10.
См. Также fileTime () и FileTime.
[виртуальный]
bool QFileDevice :: setPermissions (QFileDevice :: Permissions разрешения ) Устанавливает для файла права доступа , указанные .Возвращает true
в случае успеха или false
, если разрешения не могут быть изменены.
Предупреждение: Эта функция не управляет списками ACL, что может ограничить ее эффективность.
См. Также разрешения ().
[переопределить виртуальный]
qint64 QFileDevice :: size () constПереопределения: QIODevice :: size () const.
Возвращает размер файла.
Для обычных пустых файлов в Unix (например, в / proc
) эта функция возвращает 0; содержимое такого файла создается по запросу в ответ на ваш вызов read ().
Отключает отображение памяти по адресу .
Возвращает true
, если unmap успешно; в противном случае - ложь.
См. Также карту ().
Устанавливает для файла ошибку QFileDevice :: NoError.
См. Также ошибку ().
[переопределить виртуальную защиту]
qint64 QFileDevice :: writeData (const char * data , qint64 len )Переопределения: QIODevice :: writeData (const char * data, qint64 maxSize).
Класс QTemporaryFile - это устройство ввода-вывода, которое работает с временными файлами. Подробнее ...
QTemporaryFile используется для безопасного создания уникальных временных файлов. Сам файл создается путем вызова open (). Имя временного файла гарантированно уникально (т.е. вы гарантированно не перезаписываете существующий файл), и файл впоследствии будет удален после уничтожения объекта QTemporaryFile.Это важный метод, позволяющий избежать повреждения данных для приложений, хранящих данные во временных файлах. Имя файла либо автоматически генерируется, либо создается на основе шаблона, который передается конструктору QTemporaryFile.
Пример:
Повторное открытие QTemporaryFile после вызова close () безопасно. Пока сам объект QTemporaryFile не уничтожен, уникальный временный файл будет существовать и будет оставаться открытым внутри QTemporaryFile.
Имя временного файла можно узнать, вызвав fileName ().Обратите внимание, что это определяется только после первого открытия файла; перед этим функция возвращает пустую строку.
Временный файл будет иметь некоторую статическую часть имени и некоторую часть, которая считается уникальной. Имя файла по умолчанию будет определено из QCoreApplication :: applicationName () (иначе qt_temp
) и будет помещено во временный путь, возвращенный QDir :: tempPath (). Если вы укажете собственное имя файла, относительный путь к файлу по умолчанию не будет помещен во временный каталог, а будет относиться к текущему рабочему каталогу.
Указанные имена файлов могут содержать следующий шаблон XXXXXX
(шесть символов «X» в верхнем регистре), который будет заменен автоматически сгенерированной частью имени файла. Обратите внимание, что в шаблоне учитывается регистр. Если шаблон отсутствует в имени файла, QTemporaryFile добавляет сгенерированную часть к данному имени файла.
Примечание: В Linux QTemporaryFile попытается создать безымянные временные файлы. Если это удастся, open () вернет true, а exists () будет false.Если вы вызываете fileName () или любую вызывающую его функцию, QTemporaryFile даст файлу имя, поэтому большинство приложений не заметят разницы.
Создает QTemporaryFile с именем файла шаблона templateName и указанным родительским элементом . После открытия временного файла он будет использован для создания уникального имени файла.
Если templateName не содержит XXXXXX, он будет автоматически добавлен и использован как динамическая часть имени файла.
Если имя_шаблона - относительный путь, путь будет относиться к текущему рабочему каталогу. Вы можете использовать QDir :: tempPath () для создания templateName , если вы хотите использовать временный каталог системы.
См. Также open () и fileTemplate ().
Создает QTemporaryFile (с заданным родительским файлом ), используя в качестве шаблона файла имя приложения, возвращаемое QCoreApplication :: applicationName () (в противном случае qt_temp
), за которым следует ".XXXXXX ". Файл хранится во временном каталоге системы QDir :: tempPath ().
См. Также setFileTemplate ().
Создает QTemporaryFile с именем файла шаблона имя_шаблона . После открытия временного файла он будет использован для создания уникального имени файла.
Если templateName не содержит XXXXXX, он будет автоматически добавлен и использован как динамическая часть имени файла.
Если имя_шаблона - относительный путь, путь будет относиться к текущему рабочему каталогу. Вы можете использовать QDir :: tempPath () для создания templateName , если вы хотите использовать временный каталог системы.
См. Также open () и fileTemplate ().
Создает QTemporaryFile, используя в качестве шаблона файла имя приложения, возвращаемое QCoreApplication :: applicationName () (иначе qt_temp
), за которым следует ".XXXXXX ". Файл хранится во временном каталоге системы QDir :: tempPath ().
См. Также setFileTemplate () и QDir :: tempPath ().
[виртуальный]
QTemporaryFile :: ~ QTemporaryFile ()Уничтожает временный файловый объект, при необходимости файл автоматически закрывается, а в режиме автоматического удаления он автоматически удаляет файл.
См. Также autoRemove ().
Возвращает true
, если QTemporaryFile находится в режиме автоматического удаления.В режиме автоматического удаления имя файла автоматически удаляется с диска при уничтожении. Это позволяет очень легко создать объект QTemporaryFile в стеке, заполнить его данными, прочитать из него и, наконец, при возврате функции он автоматически очистится после себя.
Автоудаление включено по умолчанию.
См. Также setAutoRemove () и remove ().
[статические]
QTemporaryFile * QTemporaryFile :: createNativeFile (QFile и файл ) Если файл еще не является собственным файлом, то QTemporaryFile создается в QDir :: tempPath (), содержимое файла копируется в него, и возвращается указатель на временный файл.Ничего не делает и возвращает 0
, если файл уже является собственным файлом.
Например:
См. Также QFileInfo :: isNativePath ().
[статические]
QTemporaryFile * QTemporaryFile :: createNativeFile (const QString & имя_файла )Это перегруженная функция.
Работает с заданным fileName , а не с существующим объектом QFile.
[переопределить виртуальный]
QString QTemporaryFile :: fileName () constПереопределения: QFile :: fileName () const.
Возвращает полное уникальное имя файла, поддерживающее объект QTemporaryFile. Эта строка имеет значение NULL до открытия QTemporaryFile, после этого она будет содержать fileTemplate () плюс дополнительные символы, чтобы сделать ее уникальной.
См. Также fileTemplate ().
Возвращает установленный шаблон файла. Шаблон файла по умолчанию будет называться qcoreappname.XXXXXX и помещаться в QDir :: tempPath ().
См. Также setFileTemplate ().
QTemporaryFile всегда будет открыт в режиме QIODevice :: ReadWrite, это обеспечивает легкий доступ к данным в файле. Эта функция вернет истину в случае успеха и установит для fileName () уникальное имя используемого файла.
См. Также fileName ().
[переопределить виртуальную защиту]
bool QTemporaryFile :: open (QIODevice :: OpenMode flags )Переопределения: QFile :: open (режим QIODevice :: OpenMode).
Создает уникальное имя временного файла и открывает его. Позже вы можете получить уникальное имя, вызвав fileName (). Гарантируется, что файл был создан этой функцией (т.е. он никогда не существовал раньше).
Устанавливает QTemporaryFile в режим автоматического удаления, если b имеет значение true
.
Автоудаление включено по умолчанию.
Если вы установите для этого свойства значение false
, убедитесь, что приложение предоставляет способ удаления файла, когда он больше не нужен, включая передачу ответственности другому процессу.Всегда используйте функцию fileName () для получения имени и никогда не пытайтесь угадать имя, которое сгенерировал QTemporaryFile.
В некоторых системах, если fileName () не вызывается перед закрытием файла, временный файл может быть удален независимо от состояния этого свойства. На такое поведение нельзя полагаться, поэтому код приложения должен либо вызвать fileName (), либо оставить включенной функцию автоматического удаления.
См. Также autoRemove () и remove ().
Устанавливает статическую часть имени файла на имя .Если шаблон файла содержит XXXXXX, который будет автоматически заменен уникальной частью имени файла, в противном случае имя файла будет определено автоматически на основе указанной статической части.
Если имя содержит относительный путь к файлу, путь будет относиться к текущему рабочему каталогу. Вы можете использовать QDir :: tempPath () для создания name , если вы хотите использовать временный каталог системы.
См. Также fileTemplate ().
До сих пор мы рассмотрели, как выполнять работу в отдельных потоках, что позволяет выполнять сложные задачи, не прерывая пользовательский интерфейс.Это отлично работает при использовании библиотек Python для выполнения задач, но иногда вам нужно запускать внешние приложения, передавать параметры и получать результаты.
В этом руководстве мы рассмотрим QProcess
, систему Qt для запуска внешних программ из вашего собственного приложения.
Чтобы иметь возможность тестировать запуск внешних программ с помощью QProcess
, нам нужно что-то запустить. Здесь мы создадим для этой цели простой скрипт Python, который затем можно будет запустить из нашего приложения.Поместите следующее в файл и сохраните его с именем dummy_script.py
.
Я использую Python здесь, чтобы убедиться, что он работает на всех платформах. Если у вас есть инструмент командной строки, который вы хотите использовать, вы можете заменить его.
Не беспокойтесь о содержимом этого скрипта, это просто серия операторов print (потоковая запись) с ожиданием в полсекунды после. Это имитирует длительную внешнюю программу, которая периодически выводит сообщения о состоянии.Позже мы увидим, как извлечь данные из этого вывода.
питон импорт систем
время импорта
def flush_then_wait ():
sys.stdout.flush ()
sys.stderr.flush ()
time.sleep (0,5)
sys.stdout.write ("Стандартный вывод сценария 1 \ n")
sys.stdout.write ("Стандартный вывод сценария 2 \ n")
sys.stdout.write ("Стандартный вывод сценария 3 \ n")
sys.stderr.write ("Общее время: 00: 05: 00 \ n")
sys.stderr.write ("Всего выполнено: 10% \ n")
flush_then_wait ()
sys.stdout.write ("name = Мартин \ n")
sys.stdout.write ("Стандартный вывод сценария 4 \ n")
sys.stdout.write ("Сценарий stdout 5 \ n")
sys.stderr.write ("Всего выполнено: 30% \ n")
flush_then_wait ()
sys.stderr.write ("Прошедшее время: 00: 00: 10 \ n")
sys.stderr.write ("Прошедшее время: 00: 00: 50 \ n")
sys.stderr.write ("Всего выполнено: 50% \ n")
sys.stdout.write ("страна = Нидерланды \ n")
flush_then_wait ()
sys.stderr.write ("Прошедшее время: 00: 01: 10 \ n")
sys.stderr.write ("Всего выполнено: 100% \ n")
sys.stdout.write ("Стандартный вывод сценария 6 \ n")
sys.stdout.write ("Стандартный вывод сценария 7 \ n")
sys.stdout.write ("сайт = www.mfitzp.com \ n")
flush_then_wait ()
Теперь у нас есть dummy_script.py
, мы можем запустить его из нашего приложения Qt.
Чтобы поэкспериментировать с запуском программ через QProcess
, нам понадобится скелет приложения. Это показано ниже - простое окно с QPushButton
и QTextArea
. Нажатие кнопки вызывает наш пользовательский слот start_process
, в котором мы будем выполнять наш внешний процесс.
из импорта PyQt5.QtWidgets (QApplication, QMainWindow, QPushButton, QPlainTextEdit,
QVBoxLayout, QWidget)
из PyQt5.QtCore import QProcess
import sys
класс MainWindow (QMainWindow):
def __init __ (сам):
супер () .__ init __ ()
self.btn = QPushButton ("Выполнить")
self.btn.pressed.connect (self.start_process)
self.text = QPlainTextEdit ()
self.text.setReadOnly (Истина)
l = QVBoxLayout ()
l.addWidget (self.btn)
l.addWidget (самотекст)
w = QWidget ()
w.setLayout (l)
self.setCentralWidget (w)
def start_process (сам):
# Здесь мы запустим наш процесс.проходить
app = QApplication (sys.argv)
w = MainWindow ()
w.show ()
app.exec_ ()
Убедитесь, что работает, смотреть пока особо не на что - нажатие кнопки тоже ничего не делает.
Скелет приложения.
QProcess
для выполнения внешних приложений. Выполнение внешних программ с помощью QProcess довольно просто. Сначала вы создаете объект QProcess
, а затем вызываете .start ()
передает команду для выполнения и список
строковых аргументов.
p = QProcess ()
p.start ("<программа>", [<аргументы>])
В нашем примере мы запускаем пользовательский сценарий dummy_script.py
с Python, поэтому наш исполняемый файл - python
(или python3
), а наши аргументы - всего лишь dummy_script.py
.
p = QProcess ()
п.start ("python3", ['dummy_script.py'])
Если вы запускаете другую программу командной строки, вам необходимо указать для нее аргументы. Например, использование ffmpeg
для извлечения информации из видеофайла.
p = QProcess ()
p.start ("ffprobe", ['-show_format', '-show_streams', 'a.mp4.py'])
Используйте тот же подход с вашей собственной программой командной строки, не забывая разбивать аргументы на отдельные элементы в списке.
Мы можем взять пример p.start ("python3", ['dummy_script.py'])
и добавить его в скелет нашего приложения следующим образом. Мы также добавляем вспомогательный метод message ()
для записи сообщений в текстовое поле пользовательского интерфейса.
из импорта PyQt5.QtWidgets (QApplication, QMainWindow, QPushButton, QPlainTextEdit,
QVBoxLayout, QWidget)
из PyQt5.QtCore импорт QProcess
import sys
класс MainWindow (QMainWindow):
def __init __ (сам):
супер().__в этом__()
self.btn = QPushButton ("Выполнить")
self.btn.pressed.connect (self.start_process)
self.text = QPlainTextEdit ()
self.text.setReadOnly (Истина)
l = QVBoxLayout ()
l.addWidget (self.btn)
l.addWidget (самотекст)
w = QWidget ()
w.setLayout (l)
self.setCentralWidget (w)
сообщение def (self, s):
self.text.appendPlainText (s)
def start_process (сам):
self.message («Процесс выполнения.»)
self.p = QProcess () # Сохранять ссылку на QProcess (например,грамм. на себя) во время работы.
self.p.start ("python3", ['dummy_script.py'])
app = QApplication (sys.argv)
w = MainWindow ()
w.show ()
app.exec_ ()
Обратите внимание, что вы должны сохранять ссылку на созданный объект QProcess
во время его работы, например на сам.п.
. В противном случае объект будет удален преждевременно, и вы увидите сообщение QProcess: Destroyed, пока процесс ("python3") все еще выполняется. Ошибка
.
Если вы запустите этот пример и нажмете кнопку, ничего не произойдет.Внешний скрипт запущен, но вы не видите вывода.
Отображается сообщение о выполнении, но ничего больше.
Если вы нажмете кнопку несколько раз, вы можете увидеть такое сообщение -
трепать QProcess: уничтожено, пока процесс ("python3") все еще выполняется.
Это связано с тем, что если вы нажмете кнопку , когда процесс уже запущен , создание нового процесса заменяет ссылку на существующий объект QProcess
в self.p
, удалив его. Мы можем избежать этого, проверив значение self.p
перед выполнением нового процесса и подключив сигнал завершено , чтобы сбросить его обратно на Нет
, например
из импорта PyQt5.QtWidgets (QApplication, QMainWindow, QPushButton, QPlainTextEdit,
QVBoxLayout, QWidget)
из PyQt5.QtCore импорт QProcess
import sys
класс MainWindow (QMainWindow):
def __init __ (сам):
супер().__в этом__()
self.p = None # Пустое значение по умолчанию.
self.btn = QPushButton ("Выполнить")
self.btn.pressed.connect (self.start_process)
self.text = QPlainTextEdit ()
self.text.setReadOnly (Истина)
l = QVBoxLayout ()
l.addWidget (self.btn)
l.addWidget (самотекст)
w = QWidget ()
w.setLayout (l)
self.setCentralWidget (w)
сообщение def (self, s):
self.text.appendPlainText (s)
def start_process (сам):
если сам.p равно None: # Ни один процесс не запущен.
self.message («Процесс выполнения»)
self.p = QProcess () # Сохранять ссылку на QProcess (например, на себя) во время его работы.
self.p.finished.connect (self.process_finished) # Очистить после завершения.
self.p.start ("python3", ['dummy_script.py'])
def process_finished (сам):
self.message («Процесс завершен.»)
self.p = Нет
app = QApplication (sys.argv)
w = MainWindow ()
w.show ()
app.exec_ ()
Запустив это сейчас, вы можете запустить процесс, а после его завершения - запустить его снова.Каждый раз, когда процесс завершится, вы увидите сообщение «Процесс завершен». сообщение в текстовом поле.
Сообщение «Процесс завершен» отображается после его завершения.
QProcess
Пока что мы выполнили внешнюю программу и получили уведомления о ее запуске и остановке, но ничего не знаем о том, что она делает. Это нормально в некоторых случаях, когда вы просто хотите, чтобы задание выполнялось, но часто вам нужны более подробные отзывы. К счастью, QProcess
предоставляет ряд сигналов, которые можно использовать для отслеживания хода и состояния процессов.
Если вы знакомы с запуском внешних процессов с использованием подпроцесса
в Python, возможно, вы знакомы с потоками . Это файловые объекты, которые вы используете для извлечения данных из запущенного процесса. Два стандартных потока: стандартный вывод и стандартный поток ошибок . Первый получает результат данных, выводимых приложением, а второй получает диагностических или сообщений об ошибке . В зависимости от того, что вас интересует, оба из них могут быть полезны - многие программы (например, наш dummy_script.py
выводит информацию о ходе выполнения в поток стандартной ошибки .
В области Qt применяются те же принципы. Объект QProcess
имеет два сигнала .readyReadStandardOutput
и .readyReadStandardError
, которые используются для уведомления, когда данные доступны в соответствующих потоках. Затем мы можем читать из процесса, чтобы получить самые свежие данные.
Ниже приведен пример настройки QProcess
, который соединяет readyReadStandardOutput
и .readyReadStandardError
, а также отслеживание изменений состояния и сигналов завершения.
p = QProcess ()
p.readyReadStandardOutput.connect (self.handle_stdout)
p.readyReadStandardError.connect (self.handle_stderr)
p.stateChanged.connect (self.handle_state)
p.finished.connect (самоочистка)
p.start ("питон", ["dummy_script.py"])
Сигнал .stateChanged
срабатывает при изменении статуса процесса. Допустимые значения - определены в QProcess.Перечисление ProcessState
- показаны ниже.
Константа | Значение | Описание |
---|---|---|
QProcess.NotRunning | 0 | Процесс не запущен. |
QProcess.Starting | 1 | Процесс запускается, но программа еще не запущена. |
QProcess.Running | 2 | Процесс запущен и готов к чтению и записи. |
Включение этого в наш пример и реализация методов обработчика для каждого из них дает нам следующий полный код.
питон из импорта PyQt5.QtWidgets (QApplication, QMainWindow, QPushButton, QPlainTextEdit,
QVBoxLayout, QWidget)
из PyQt5.QtCore импорт QProcess
import sys
класс MainWindow (QMainWindow):
def __init __ (сам):
супер () .__ init __ ()
self.p = Нет
себя.btn = QPushButton ("Выполнить")
self.btn.pressed.connect (self.start_process)
self.text = QPlainTextEdit ()
self.text.setReadOnly (Истина)
l = QVBoxLayout ()
l.addWidget (self.btn)
l.addWidget (самотекст)
w = QWidget ()
w.setLayout (l)
self.setCentralWidget (w)
сообщение def (self, s):
self.text.appendPlainText (s)
def start_process (сам):
если self.p равен None: # Процесс не запущен.
self.message («Процесс выполнения»)
себя.p = QProcess () # Сохранять ссылку на QProcess (например, на себя) во время его работы.
self.p.readyReadStandardOutput.connect (self.handle_stdout)
self.p.readyReadStandardError.connect (self.handle_stderr)
self.p.stateChanged.connect (self.handle_state)
self.p.finished.connect (self.process_finished) # Очистить после завершения.
self.p.start ("python3", ['dummy_script.py'])
def handle_stderr (сам):
data = self.p.readAllStandardError ()
stderr = байты (данные).декодировать ("utf8")
self.message (stderr)
def handle_stdout (сам):
data = self.p.readAllStandardOutput ()
stdout = байты (данные) .decode ("utf8")
self.message (стандартный вывод)
def handle_state (себя, состояние):
состояния = {
QProcess.NotRunning: 'Не работает',
QProcess.Starting: 'Запуск',
QProcess.Running: 'Выполняется',
}
state_name = состояния [состояние]
self.message (f "Состояние изменено: {state_name}")
def process_finished (сам):
себя.сообщение ("Процесс завершен.")
self.p = Нет
app = QApplication (sys.argv)
w = MainWindow ()
w.show ()
app.exec_ ()
Если вы запустите это, вы увидите, что стандартный вывод, стандартная ошибка, изменения состояния и сообщения о запуске / остановке печатаются в текстовом поле. Обратите внимание, что мы конвертируем состояния обратно в дружественные строки перед выводом (используя dict
для сопоставления из значений перечисления).
Выходные данные нашего настраиваемого сценария показаны в текстовом поле.
Обработка вывода немного сложна и заслуживает более внимательного рассмотрения.
питон data = self.p.readAllStandardError ()
stderr = байты (данные) .decode ("utf8")
self.message (stderr)
Это необходимо, потому что QProcess.readAllStandardError
и QProcess.readAllStandardOutput
возвращают данные как байты, заключенные в объект Qt. Сначала мы должны преобразовать это в объект Python bytes ()
, а затем декодировать этот байтовый поток в строку (здесь используется кодировка UTF8).
В настоящее время мы просто выгружаем вывод программы в текстовое поле, но что, если бы мы хотели извлечь из него какие-то конкретные данные? Обычно это используется для отслеживания прогресса запущенной программы, чтобы мы могли отображать индикатор выполнения по мере ее завершения.
В этом примере наш демонстрационный скрипт dummy_script.py
возвращает серию строк, включая строки стандартной ошибки , которые показывают текущий процент завершения прогресса - e.грамм. Всего выполнено: 50%
. Мы можем обработать эти строки до прогресса и показать это в строке состояния.
В приведенном ниже примере мы извлекаем это с помощью специального регулярного выражения . Функция simple_percent_parser
сопоставляет содержимое потока стандартной ошибки и извлекает число от 00 до 100 для прогресса. Это значение используется для обновления индикатора выполнения, добавленного в пользовательский интерфейс.
из PyQt5.Импорт QtWidgets (QApplication, QMainWindow, QPushButton, QPlainTextEdit,
QVBoxLayout, QWidget, QProgressBar)
из PyQt5.QtCore импорт QProcess
import sys
импорт ре
# Регулярное выражение для извлечения% завершения.
progress_re = re.compile ("Всего выполнено: (\ d +)%")
def simple_percent_parser (вывод):
"" "
Соответствует строкам с использованием регулярного выражения progress_re,
возвращает одно целое число для% прогресса.
"" "
m = progress_re.search (вывод)
если М:
pc_complete = м.группа 1)
вернуть int (pc_complete)
класс MainWindow (QMainWindow):
def __init __ (сам):
супер () .__ init __ ()
self.p = Нет
self.btn = QPushButton ("Выполнить")
self.btn.pressed.connect (self.start_process)
self.text = QPlainTextEdit ()
self.text.setReadOnly (Истина)
self.progress = QProgressBar ()
self.progress.setRange (0, 100)
l = QVBoxLayout ()
l.addWidget (self.btn)
l.addWidget (self.progress)
l.addWidget (self.текст)
w = QWidget ()
w.setLayout (l)
self.setCentralWidget (w)
сообщение def (self, s):
self.text.appendPlainText (s)
def start_process (сам):
если self.p равен None: # Процесс не запущен.
self.message («Процесс выполнения»)
self.p = QProcess () # Сохранять ссылку на QProcess (например, на себя) во время его работы.
self.p.readyReadStandardOutput.connect (self.handle_stdout)
self.p.readyReadStandardError.connect (self.handle_stderr)
self.p.stateChanged.connect (self.handle_state)
self.p.finished.connect (self.process_finished) # Очистить после завершения.
self.p.start ("python3", ['dummy_script.py'])
def handle_stderr (сам):
data = self.p.readAllStandardError ()
stderr = байты (данные) .decode ("utf8")
# Извлечь прогресс, если он есть в данных.
прогресс = простой_перцент_парсер (stderr)
если прогресс:
self.progress.setValue (прогресс)
себя.сообщение (stderr)
def handle_stdout (сам):
data = self.p.readAllStandardOutput ()
stdout = байты (данные) .decode ("utf8")
self.message (стандартный вывод)
def handle_state (себя, состояние):
состояния = {
QProcess.NotRunning: 'Не работает',
QProcess.Starting: 'Запуск',
QProcess.Running: 'Выполняется',
}
state_name = состояния [состояние]
self.message (f "Состояние изменено: {state_name}")
def process_finished (сам):
self.message ("Процесс завершен.")
self.p = Нет
app = QApplication (sys.argv)
w = MainWindow ()
w.show ()
app.exec_ ()
Если вы запустите это и запустите процесс, вы увидите, что индикатор выполнения постепенно заполняется по мере получения сообщений о ходе выполнения от dummy_script.py
, проходящего через QProcess
.
Индикатор выполнения заполняется по мере завершения сценария.
Этот подход хорошо работает с любыми программами командной строки - в некоторых случаях вы можете захотеть проанализировать стандартный вывод , а не стандартную ошибку , но принципы идентичны.Иногда программы не дают вам заранее рассчитанного значения прогресса, и вам нужно проявить немного творчества. Если вам нравится задача, попробуйте изменить синтаксический анализатор, чтобы извлечь данные общего времени и прошедшего времени из стандартной ошибки dummy_script.py
и использовать их для вычисления индикатора выполнения. Вы также можете попробовать адаптировать запуск для других программ командной строки.
Во всех этих примерах мы храним ссылку на процесс в self.p
, что означает, что мы можем одновременно запустить только один процесс. Но вы можете запускать столько процессов, сколько захотите, вместе с вашим приложением. Если вам не нужно отслеживать информацию от них, вы можете просто сохранить ссылки на процессы в списке
.
Если вы запускаете несколько внешних программ одновременно и хотят отслеживать их состояния, вы можете подумать о создании класса менеджера , который сделает это за вас. Книга содержит больше примеров, в том числе этот менеджер, объединяющий анализ стандартного вывода QProcess
с представлениями модели для создания монитора выполнения в реальном времени для внешних процессов.
Диспетчер процессов, показывающий активные процессы и ход выполнения
Спасибо, @MichaelTunnell! Отличное зрелище, как всегда
Большое спасибо за более подробное изучение вопроса о CentOS / CentOS Stream , и да, мне бы хотелось посмотреть видео, если у вас есть время. Я предполагаю, что моей главной заботой было иметь доступ к RHEL , если я когда-либо буду обучаться для их сертификации, и CentOS был хорошим способом экспериментировать, сколько угодно, без необходимости платить лицензионный сбор Enterprise.Однако, если есть бесплатная версия для разработчиков, это будет даже лучше, я согласен. Я могу понять подход CentOS Stream к инновациям, хотя я опасаюсь, что он может иметь довольно большое значение с точки зрения риска / надежности. С другой стороны, я очень консервативен и не склонен рисковать в отношении ежедневных водителей. Debian меня очень устраивает, и, честно говоря, я бы более чем вероятно подумал, что если мне понадобится небольшое количество серверов, а не RHEL. Если вы ведете бизнес, зависящий от программного обеспечения корпоративного уровня, я действительно думаю, что вложение в техническую поддержку RedHat, вероятно, будет хорошей идеей и стоит, хотя стартапам поначалу может быть сложно. Rocky Linux может еще спасти положение для таких людей, но я бы хотел, чтобы их выбор имени был эээ… другим. На мой взгляд, у Рокки нет коннотации стабильности!
Раньше я мало слышал о Crux Linux . Звучит интересно, например, Slackware , который был первым дистрибутивом, который я когда-либо устанавливал. Все было в виде tar-файлов. Звучит немного как LFS тоже
Risc-V меня действительно интересует, поэтому я с нетерпением жду увеличения поддержки этого в QEMU .Также я рад, что Qt вводит новшества, даже если временами не поддерживает обратную совместимость, поскольку, на мой взгляд, у нас острая потребность в портативном инструментарии графического интерфейса. Я начинаю получать больше удовольствия от JavaFX , поскольку теперь он наконец-то переносится и на мобильные платформы. Как только у меня будет опыт работы с ним для настольных компьютеров, я попробую его для Android, чьи собственные инструменты неоднократно подводили меня, когда я смотрел на них год или два назад.
PPV AEW Plus 5.0
PPV 4.8721
PPV AEW Plus 4.6667
PPV AEW Plus 4.8108
Бесплатно 5.0
PPV AEW Plus 4.8737
Бесплатно 4.4
PPV AEW Plus 4,6111
Бесплатно 4.7
Бесплатно 4.9
Бесплатно 4.6
Бесплатно 4,625
Бесплатно 4,8571
Бесплатно 4,7273
PPV AEW Plus 4.8795
Я разрабатываю клиент для видеосервера (VDR) с Qt 4.4 с использованием фононного каркаса.
Phonon может использовать различные типы медиаисточников, одним из которых является QIODevice. Поскольку VDR / Vomp использует настраиваемый сетевой протокол передачи данных, мне нужно переопределить поток данных самостоятельно.
Вполне понятно, как наследовать от QIODevice, если у меня есть pull-mode:
Phonon-BE запрашивает данные, они поступают в QIODevice, и я реализую readData (...) соответственно, который запрашивает данные, например, от QTcpSocket, например, в случае файл передается клиенту. Я называю это вытяжным потоком.
Теперь я тоже хочу live-tv в приложении - это push-поток, данные просто идут по сети.
Здесь я застрял в том, как это сделать правильно, поскольку QIODevice имеет некоторый документ, который в основном необходимо сделать, но на самом деле нет никакой прямой поддержки (пока AFAIHS).
Мне нужно переопределить - когда я испускаю readyRead-Signal - функцию waitForReadyRead - а также readData (). Понятно, как начать стрим, как и как его закончить.
Что я понял до сих пор:
Теперь мне нужно буферизовать данные из живого потока, затем испустить сигнал readyRead, а затем позволить IODevice прочитать из моего буфера с помощью readData.В промежутке waitForReadyRead должен выдавать условие ожидания, пока не будут получены некоторые данные.
Я пока прав?
Теперь подробные вопросы с моей стороны:
Является ли QIODevice правильным классом для использования, поскольку он не имеет встроенной поддержки непосредственно для потоков?
QBuffer не кажется правильным, поскольку у него просто будет увеличивающийся буфер и другие классы, которых я не нашел.