Как использовать обновление Qlabel с помощью jpg с помощью QNetworkRequest?

Я использую PyQt4 для создания формы, которая отображает веб-изображение в отдельном окне с помощью Qlabel. Должно быть так, что нажатие кнопки в форме обновляет Qlabel новым изображением, считывая URL-адрес нового изображения из ячейки таблицы в форме.

Проблема, с которой я столкнулся, заключается в том, что я не понимаю, как создать Qlabel как объект класса, который я могу затем обновить, установив его свойство setPixmap(). Вот что я написал до сих пор:

import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from PyQt4.QtNetwork import QNetworkAccessManager, QNetworkRequest


CAT_PICS = ["http://static.tumblr.com/ce35b04e242c6b8073f3ff7801147e9f/sz5wgey/obSmpcvso/tumblr_static_o-cats-kill-billions-facebook.jpg",
        "http://jasonlefkowitz.net/wp-content/uploads/2013/07/cats-16140154-1920-1080.jpg",
        "http://4.bp.blogspot.com/-MzZCzWI_6Xc/UIUQp1qPfzI/AAAAAAAAHpA/OTwHCJSWFAY/s1600/cats_animals_kittens_cat_kitten_cute_desktop_1680x1050_hd-wallpaper-753974.jpeg"]


class ImageLabel(QLabel):
    def __init__(self, parent=None):
        QLabel.__init__(self, parent)

        url = self.text()
        nam = QNetworkAccessManager()
        def finishRequest(reply):
            img = QImage()
            img.loadFromData(reply.readAll())
            myImage = QPixmap(img)

            self.setFixedSize(myImage.size())
            self.setPixmap(QPixmap(myImage))
        nam.finished.connect(finishRequest)
        nam.get(QNetworkRequest(QUrl(url)))



class MainForm(QDialog):
    index = 0
    def __init__(self):
        super(MainForm, self).__init__()

        changePhotoButton = QPushButton("Next Photo")
        layout = QHBoxLayout()
        layout.addWidget(changePhotoButton)
        self.setLayout(layout)

        self.connect(changePhotoButton, SIGNAL("clicked()"),
                     self.updatePhoto)


    def updatePhoto(self):
        url = CAT_PICS[self.index]
        imageLabel = ImageLabel(url)
        imageLabel.show()
        self.index += 1

def main():
    app = QApplication(sys.argv)
    form = MainForm()
    form.show()
    app.exec_()
main()

Код будет работать, однако, когда вызывается класс ImageLabel, QLabel создается с исходным текстом из URL-адреса, но изображение никогда не извлекается, и Qlabel немедленно удаляется из поля зрения.

Я был бы признателен за любые советы о том, как заставить это работать.


person wanyo    schedule 13.01.2014    source источник
comment
Почему вы создаете подкласс QLabel для этой операции?   -  person lpapp    schedule 13.01.2014
comment
Привет, я хотел сделать так, чтобы начальная форма могла обновлять Qlabel, нажав на кнопку. Пока я могу получить Qlabel с изображением, созданным путем размещения кода внутри класса ImageLabel в main(). Я не могу заставить его работать, разместив его где-нибудь еще. Я не знаю, как обращаться с частями nam.finished.connect(finishRequest) и def finishRequest(reply):.   -  person wanyo    schedule 13.01.2014
comment
Вам действительно не нужен подкласс для этого. Вы можете связать нажатие кнопки со слотом, который устанавливает соответствующее растровое изображение... Кроме того, вы используете imageLabel = ImageLabel(url), тогда как класс ImageLabel не имеет такого параметра метода init... OT: main() as строка выглядит странно в питоне.   -  person lpapp    schedule 13.01.2014
comment
Я не знаю, как обращаться с частями nam.finished.connect(finishRequest) и def finishRequest(reply): . -› Что именно вас там беспокоит?   -  person lpapp    schedule 13.01.2014
comment
Не могли бы вы написать пример кода, поскольку я не знаком со слотами? Я не совсем знаком с синтаксисом для nam.finished.connect(finishRequest) или def finishRequest(reply) и пока не нашел четкого объяснения.   -  person wanyo    schedule 13.01.2014


Ответы (1)


Короче говоря, я бы отказался от пользовательского подкласса QLabel и вместо этого расширил бы метод updatePhoto, как показано ниже. Однако, похоже, вы неправильно обработали URL-адрес.

def __init__(self):
    self.myLabel = QLabel()
    self.nam = QNetworkAccessManager()
    self.nam.finished.connect(self.finishRequest)
    ...

def finishRequest(self, reply):
    myPixmap = QPixmap()
    if not myPixmap.loadFromData(reply.readAll()):
        print('Could not load')
    self.myLabel.setFixedSize(myImage.size())
    self.myLabel.setPixmap(myPixmap)
    self.myLabel.show()

def updatePhoto(self):
    url = CAT_PICS[self.index]
    self.nam.get(QNetworkRequest(QUrl(url)))
    self.index += 1

Обратите внимание, что я бы использовал nam и myLabel как члены класса вашего соответствующего класса Python.

person lpapp    schedule 13.01.2014
comment
Привет, да, URL-адрес должен быть типа QUrl, а не str, спасибо за исправление. Я смущен некоторыми вещами с вашим ответом. Создание членов класса nam и myLabel означает ли это объявление их в классе MainForm, чтобы они были доступны через self.nam и self.myLabel? - person wanyo; 13.01.2014
comment
@wanyo: да, я обновил ответ, извиняюсь. Прошлой ночью у меня было мышление C++, а не python'ish. ;-) - person lpapp; 13.01.2014
comment
Я хотел спросить, как сделать членов классов nam и myLabel? Если я попытаюсь объявить их в классе MainForm, чтобы они были доступны через self.nam и self.myLabel, я получаю сообщение об ошибке, говорящее, что я должен создать QApplication перед использованием QPaintDevice. Также после добавления метода finishRequest(reply) в класс Mainform и внесения изменений в updatePhoto() я получаю проблему, заключающуюся в том, что finishRequest не определен, и этот метод finishRequest() должен иметь self в качестве первого параметра. - person wanyo; 13.01.2014
comment
Я понял, что, вероятно, мне следует объявить nam и mLabel в разделе конструктора класса MainForm, а не за его пределами. Это устраняет проблему с QApplicaton перед QPaintDevice. Я думаю, что теперь проблема заключается в том, как правильно объявить и вызвать finishRequest, поскольку в настоящее время у него нет доступа к myLabel. Я обновлю свой первоначальный вопрос с изменениями. Спасибо. - person wanyo; 13.01.2014
comment
@wanyo: я не уверен, что понял вопрос. Если это член класса, у вас есть доступ к myLabel, поскольку методы могут обращаться к членам класса. По общему признанию, это должен быть self.finishRequest для соединения. Я отредактирую эту опечатку позже с другими проблемами, если они возникнут. OT: в python нет конструктора, по крайней мере, в смысле python, но да, метод init выглядит для него допустимым местом, поскольку он оценивается только при создании экземпляра, а не для загрузки модуля, когда он находится снаружи. - person lpapp; 13.01.2014
comment
Сначала я думал объявить myLabel вне метода init, что приводило к тому, что python подходил. Я переместил объявления внутрь метода init, где добавил self.myLabel = QLabel() и self.nam = QNetworkManager(). Теперь к ним можно получить доступ в методе updatePhoto, но метод FinishRequest в настоящее время не имеет к ним доступа. Я не понимаю, что происходит при вызове self.finishRequest, какой параметр воспроизведения используется в методе и почему его не нужно указывать при вызове? - person wanyo; 13.01.2014
comment
@wanyo: прочтите это, pyqt.sourceforge.net/Docs/PyQt4/new_style_signals_slots.html Вам может понадобиться декоратор pyqtSlot() для завершения метода. - person lpapp; 13.01.2014
comment
Спасибо за ссылку, я почитаю и посмотрю, что я могу сделать из этого. Я подумал, что должен сказать, что мне удалось загрузить изображение, внеся небольшое изменение, добавив параметр self в FinishRequest(self, answer): первое изображение успешно загружается при нажатии кнопки. Но при последующих кликах в методе finishRequest происходит что-то странное, и дальнейшие изображения не загружаются. Я заставил его загрузить третье изображение при третьем щелчке, но теперь он просто терпит неудачу, печатая текст отладки. Не удалось загрузить один раз после второго щелчка, а затем дважды после третьего. - person wanyo; 13.01.2014
comment
Ах, да, self необходим... это снова образ мышления C++ прошлой ночи... Иногда бывает трудно переключить контекст. :-) Если не загрузится, проверьте форматы, если у вас установлены все плагины форматов изображений. Кажется, только третий - это jpeg, поэтому он может быть причиной того, что он загружается, а предыдущие jpg - нет. doc-snapshot.qt-project. орг/qdoc/ - person lpapp; 13.01.2014
comment
Переключение контекста затрудняет поиск примеров PyQt, которым можно следовать :) Я пробовал с различными изображениями и убедился, что они в формате jpg. Странное поведение, похоже, исходит от строки myPixMap.loadFromData(reply.readAll()). Если я упрощу программу, просто выбрав первый URL-адрес при нажатии кнопки. И посмотрите, что происходит в методе finishRequest, тогда я вижу, что при первом вызове создается действительный QPixmap и ему присваивается значение, но при последующем нажатии кнопки FinishRequest, похоже, вызывается несколько раз, и тогда его значение дается установить значение NULL - person wanyo; 13.01.2014
comment
@wanyo: ах, черт возьми, переместите эту строку в метод инициализации: self.nam.finished.connect(finishRequest) - person lpapp; 13.01.2014
comment
Отлично, это исправило. Не могли бы вы объяснить, что, черт возьми, происходит и почему метод finishRequest вызывается несколько раз? Можем ли мы обновить ваш ответ решением для будущих учащихся PyQt. Спасибо, что придерживаетесь этого. - person wanyo; 13.01.2014
comment
@wanyo: готово. Вы подключали сигнал к слоту еще раз для каждого нажатия кнопки, так что для каждого нового нажатия кнопки всегда был еще один вызов... Итак, это был один вызов слота, затем два, затем три и т.д... и так как в первый раз, когда вы читаете ответ полностью, для последующих вызовов слотов не было данных для чтения, следовательно, не было надлежащего QPixmap для создания. - person lpapp; 13.01.2014