Проблема при попытке скопировать объект 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 ответа:
Предложение о создании отдельного файла пользовательского интерфейса для виджета, который вы хотите скопировать, кажется разумным решением. Хотя казалось бы, что создание отдельного графического модуля для виджета с помощью 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, для разработки элементов графического интерфейса, даже если некоторые участвуют переменные, такие как просто сколько из этих вкладок вы хотите?