Проблема при попытке скопировать объект pyside


У меня довольно неприятная проблема с использованием пайсайд и я был бы рад любому совету.

Во-первых, некоторый контекст

Я создал простой графический интерфейс, используя Qt Designer и я использовал pyside-uic.exe на мой .ui файл для того, чтобы сгенерировать ассоциированный питон файл.

Я использую питон 3.3 и еще пайсайд 1.2.1 с помощью Qt Designer 4 (Qt 4.8.5).

Я использую следующий код для запуска моего GUI:

class my_dialog(QMainWindow, my_gui.Ui_main_window):
    def __init__(self, parent=None):
        super(my_dialog, self).__init__(parent)
        self.setupUi(self)

if ("__main__" == name):
    app = QApplication(sys.argv)
    main_dialog = my_dialog()

    # (1)

    main_dialog.show()
    sys.exit(app.exec_())

Чего бы я хотел достичь

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

В первый раз, когда мне нужно добавить вкладку, Я изменяю этот шаблон, и если мне нужны какие-либо дополнительные ТЭБ, я планировал ... создание копии этой вкладки а потом ... измените эту копию соответственно.

Проблема, с которой я столкнулся

Моя проблема в том, что я не могу найти способ скопировать виджет вкладки. После некоторых исследований я подумал, что copy модуль (или pickle модуль, см. редактирование) может сделать трюк (следующий код был вставлен в (1)):

new_tab = copy.deepcopy(main_dialog.my_tab)
main_dialog.my_tabs.addTab(new_tab, "")

Но это сработала следующая ошибка:

    main_dialog.my_tabs.addTab(new_tab, "")

RuntimeError: внутренний объект C++ (Pyside.QtGui.QWidget) уже удален

Что я мог найти самостоятельно

Я видел на SO и других сайтах, что могут быть проблемы, при использовании пайсайд, объектов, собираемых потому, что нет ссылки на них в питон.

Однако факт остается фактом, что даже если я перемещу этот код в очень setupUi() метод в файле .py, сгенерированном пайсайд, я все еще получаю точно такую же ошибку. Также стоит отметить, что я могу получить доступ к объекту my_tab для изменения его содержимого без каких-либо проблем.

Я могу создать другую вкладку с нуля в своем коде, и main_dialog.my_tabs.addTab(new_tab, "") прекрасно работает в этом контексте.

После некоторых экспериментов я понял, что проблема, вероятно, возникает при копировании объекта my_tab. Действительно, копирование объекта tab, который я только что созданный, я мог видеть, что попытка добавить копию на вкладки GUI тоже не удалась, и с той же ошибкой.

Похоже, что копия каким-то образом отказывает, или объект немедленно удаляется по какой-то причине. Во всяком случае, я так думаю...

Мой вопрос

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

Я мог бы конечно, возьмите код для вкладки из сгенерированного файла и Закодируйте мой собственный метод addTab(). Однако предполагается, что я буду строить из существующего файла .ui и избегать жесткого кодирования элементов графического интерфейса.

Редактировать:

При использовании pickle:

new_tab = pickle.loads(pickle.dumps(main_dialog.my_tab, -1))

Я получаю следующую ошибку:

    new_tab = pickle.loads(pickle.dumps(main_dialog.my_tab, -1))

_пикл.PicklingError: не могу мариновать : поиск атрибутов Pyside.Необходимости некоторые.Сигнализация не сработала.

2 2

2 ответа:

Предложение о создании отдельного файла пользовательского интерфейса для виджета, который вы хотите скопировать, кажется разумным решением. Хотя казалось бы, что создание отдельного графического модуля для виджета с помощью pyside-uic будет работать так же хорошо, как и использование QUiLoader (на самом деле, это было бы немного эффективнее).

Что касается вопроса о том, почему клонирование виджета с помощью, например, copy.deepcopy не работает: это потому, что он будет копировать только оболочку python, а не базовый объект C++. Несколько более полное объяснение можно найти в этот ответ .

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

Первое, что следует отметить, это то, что нет встроенной функции для клонирования виджета Qt , поэтому клонирование должно выполняться с использованием модулей, таких как copy, pickle или marshal.

Использование pickle или marshal терпит неудачу, поскольку объект оказываетсяне выбираемым .

В то время как copy.copy или copy.deeepcopy не вызывают никаких предупреждений / исключений / ошибок, копия не происходит, или удаляется сразу после этого по какой-то причине.

При попытке передать параметр deepcopy as в addTab, предупреждение/исключение/ошибка не выдаются, но программа останавливается на этой строке и выходит обратно в командную строку Python. Тот факт, что он занимает несколько секунд на этой линии, прежде чем выйти, заставляет меня предположить, что deepcopy пытается просмотреть атрибуты объекта и в какой-то момент терпит неудачу. То же самое с copy приводит к предыдущей ошибке C++ object deleted, упомянутой в вопросе, поэтому я можно только сделать вывод, что операция deepcopy не выполняется.

В результате единственный совет, который я мог бы дать человеку, ищущему подобный ответ, - это реализовать свою собственную функцию copy-widget, что в конечном итоге я и сделаю сейчас. Тем не менее, я хочу понять, как это deepcopy терпит неудачу вот так, так тихо, но провоцируя конец казни. Я запустил нить, чтобы попытаться найти ответ на этот там

Редактировать:

Я нашел решение для этого проблема, которая уважает мои требования не жесткого кодирования элементов GUI и использования Qt Designer для создания GUI и шаблонов для повторяющихся элементов. Я надеюсь, что это поможет любому, кто имеет ту же проблему:

Идея заключается в том, что с помощью Qt-и pyside-можно загрузить данный файл .ui во время выполнения, используя метод QUiLoader(). Таким образом, можно разобрать файл .ui для извлечения данного виджета (.ui файлы являются простыми XML-файлами) и использовать следующий код для использования это:
loader = QUiLoader()
ui_file = QFile("path_to_ui_file.ui")
ui_file.open(QFile.ReadOnly)
new_tab = loader.load(ui_file)
ui_file.close()
main_dialog.my_tabs.addTab(new_tab, "")

И это работает!

Несколько вещей о приведенном выше примере:

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