Программа остановки vtkRenderWindowInteractor

#python #multithreading #listener #render #vtk

#python #многопоточность #слушатель #визуализация #vtk

Вопрос:

Я работал над прототипом приложения на Python для стереоскопического отображения 3D-модели с использованием VTK, но я столкнулся с некоторыми проблемами в интерфейсе. Цель приведенного ниже кода на данный момент состоит в том, чтобы увеличить масштаб обоих окон визуализации при нажатии средней мыши. Однако при вызове vtkRenderWindowInteractor.Start() функции мои vtkRenderWindowInteractor файлы фактически останавливают всю программу, как если бы они выполнялись в одном потоке. Еще более любопытно то, что прерывания клавиатуры не выдаются, когда я использую CTRL-C (я работаю в оболочке UNIX), пока я не закрою окна рендеринга вручную с помощью кнопки «x». Если я просто закрою окно вручную, не нажимая CTRL-C, программа запустится сразу после Start() вызова (например, в приведенном ниже коде, бесконечный цикл while). Я привел последовательность снимков экрана в конце этого поста, чтобы точно представить, что происходит в том случае, если мое объяснение сбивает с толку.

Я пробовал несколько обходных путей, чтобы исправить это, но пока ни один из них не сработал. Разделение рендеринга на изолированные потоки не имело никакого значения, даже когда я пытался использовать ncurses для ввода, в то время как разветвление их на новый процесс привело к некоторым проблемам с ОС, с которыми я бы предпочел не иметь дела. Самый современный метод стилей взаимодействия (показан ниже), в котором я просто использую встроенные прослушиватели VTK, работает в определенной степени, позволяя мне обнаруживать входные данные, когда окна находятся в фокусе, а интеракторы активны, но из-за отсутствия связи между камерой и MyInteractorStyle классом я не могу получить доступ камеры без включения повторяют цикл после Start() звонка, что приводит меня прямо к тому, с чего я начал.

Есть какие-нибудь мысли? Я просто неправильно понимаю, как предполагается использовать инструменты рендеринга VTK?

 from vtk import*
import os.path
#import thread
#import time
#import threading 
#import curses

class MyInteractorStyle(vtk.vtkInteractorStyleTrackballCamera):

        pos1 = [0, 0, 200]
        foc1 = [0, 0, 0]
        pos2 = [40, 0, 200]
        foc2 = [0, 0, 0]

        def __init__(self,parent=None):
                self.AddObserver("MiddleButtonPressEvent", self.middleButtonPressEvent)
                self.AddObserver("MiddleButtonReleaseEvent", self.middleButtonReleaseEvent)
        def middleButtonPressEvent(self,obj,event):
                print "Middle button pressed"
                self.pos1[2]  = 10
                self.pos2[2]  = 30
                self.OnMiddleButtonDown()
                return
        def middleButtonReleaseEvent(self,obj,event):
                print "Middle button released"
                self.OnMiddleButtonUp()
                return
def main():


        # create two cameras
        camera1 = vtkCamera()
        camera1.SetPosition(0,0,200)
        camera1.SetFocalPoint(0,0,0)

        camera2 = vtkCamera()
        camera2.SetPosition(40,0,200)
        camera2.SetFocalPoint(0,0,0)


        # create a rendering window and renderer
        ren1 = vtkRenderer()
        ren1.SetActiveCamera(camera1)

        ren2 = vtkRenderer()
        ren2.SetActiveCamera(camera2)

        # create source
        reader = vtkPolyDataReader()
        path = "/home/compilezone/Documents/3DSlicer/SlicerScenes/LegoModel-6_25/Model_5_blood.vtk"
        reader.SetFileName(path)
        print(path)
        reader.Update()

        # create render window

        renWin1 = vtkRenderWindow()
        renWin1.AddRenderer(ren1)

        renWin2 = vtkRenderWindow()
        renWin2.AddRenderer(ren2)

        # create a render window interactor

        inputHandler = MyInteractorStyle()

        iren1 = vtkRenderWindowInteractor()
        iren1.SetRenderWindow(renWin1)
        iren1.SetInteractorStyle(inputHandler)

        iren2 = vtkRenderWindowInteractor()
        iren2.SetRenderWindow(renWin2)
        iren2.SetInteractorStyle(inputHandler)

        # mapper
        mapper = vtkPolyDataMapper()
        mapper.SetInput(reader.GetOutput())

        # actor
        actor = vtkActor()
        actor.SetMapper(mapper)

        # assign actor to the renderer
        ren1.AddActor(actor)
        ren2.AddActor(actor)

        # enable user interface interactor
        iren1.Initialize()
        iren2.Initialize()
        renWin1.Render()
        renWin2.Render()
        iren1.Start()
        iren2.Start()
        print "Test"
        while 1:
                pos1 = iren1.GetInteractorStyle().pos1
                foc1 = iren1.GetInteractorStyle().foc1
                pos2 = iren2.GetInteractorStyle().pos2
                foc2 = iren2.GetInteractorStyle().foc2
                print     

if __name__ == '__main__':
        main()  
 

Запущенная программа

KeyboardInterrupt (нажатие CTRL-C и повторение в терминале, но ничего не происходит)

Рендеринг окон вручную закрыт, вызван KeyboardInterrupt

Комментарии:

1. По-видимому Start() , согласно документации, метод должен возвращаться только при ручном закрытии окна, но на самом деле это вызывает больше вопросов, чем ответов. Как можно было бы вызвать оба iren1 метода ‘s и iren2 ‘s Start() , если iren1 ‘s Start() не возвращались до закрытия окна?

Ответ №1:

Вызов Start() RenderWindowInteractor запускает цикл событий, необходимый для выполнения событий визуализации, так же, как цикл событий в графическом интерфейсе. Итак, то, что вы пытаетесь сделать, запуская два цикла событий, на самом деле не имеет смысла.

Концептуальным обходным решением было бы не вызывать Start RenderWindowInteractors, а написать небольшой графический интерфейс с несколькими специфичными для инструментария RenderWindowInteractors и использовать цикл событий этого GUI.

В качестве примера, вот как это делается с помощью специфичного для GUI toolkit кода в классе wxvtktrenderwindowinteractor от tvtk, который не вызывает start в RenderWindowInteractor, а вместо этого использует цикл событий GUI для управления событиями:

 def wxVTKRenderWindowInteractorConeExample():
 """Like it says, just a simple example
 """
 # every wx app needs an app
 app = wx.PySimpleApp()

 # create the top-level frame, sizer and wxVTKRWI
 frame = wx.Frame(None, -1, "wxVTKRenderWindowInteractor", size=(400,400))
 widget = wxVTKRenderWindowInteractor(frame, -1)
 sizer = wx.BoxSizer(wx.VERTICAL)
 sizer.Add(widget, 1, wx.EXPAND)
 frame.SetSizer(sizer)
 frame.Layout()

 # It would be more correct (API-wise) to call widget.Initialize() and
 # widget.Start() here, but Initialize() calls RenderWindow.Render().
 # That Render() call will get through before we can setup the
 # RenderWindow() to render via the wxWidgets-created context; this
 # causes flashing on some platforms and downright breaks things on
 # other platforms.  Instead, we call widget.Enable().  This means
 # that the RWI::Initialized ivar is not set, but in THIS SPECIFIC CASE,
 # that doesn't matter.
 widget.Enable(1)

 widget.AddObserver("ExitEvent", lambda o,e,f=frame: f.Close())

 ren = vtk.vtkRenderer()
 widget.GetRenderWindow().AddRenderer(ren)

 cone = vtk.vtkConeSource()
 cone.SetResolution(8)

 coneMapper = vtk.vtkPolyDataMapper()
 coneMapper.SetInput(cone.GetOutput())

 coneActor = vtk.vtkActor()
 coneActor.SetMapper(coneMapper)

 ren.AddActor(coneActor)

 # show the window
 frame.Show()

 app.MainLoop()
 

(Обратите внимание, что этот код не изменен и имеет некоторые явные отличия от того, что вы пытаетесь сделать.)

Кроме того, причина, по которой ctrl C не работает, заключается в том, что цикл событий VTK ничего не делает с этим событием. Некоторые графические интерфейсы действительно уважают это событие, включая wxpython. Но если вы не используете графический интерфейс, который учитывает это событие (например, Qt), вы можете вручную указать интерпретатору python перехватить это событие и аварийно завершить работу вместо пересылки события в цикл событий GUI:

 import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)
 

Комментарии:

1. Полезно знать, почему CTRL-C не работал. Я бы предпочел не использовать другой графический интерфейс, поскольку я уверен, что это можно сделать только в VTK. Есть ли у меня какой-нибудь способ написать прослушиватель событий, который самостоятельно управляет камерой, подобно, скажем, leftButtonPressEvent() VTK? Я искал источник для этого слушателя, но все, что я нашел, это заголовки, поэтому я не знаю примеров. Я поддержал вас, потому что предоставленная вами информация была хорошей, но предоставленное вами решение не соответствует масштабу моего проекта. Есть ли шанс, что вы могли бы предоставить чисто решение VTK?

2. Что означает «прослушиватель событий, который сам управляет камерой»? Это кажется несовместимым с идеей прослушивателя событий, который прослушивает события. Я даже не знаю, что вы имеете в виду под этим. Вы имеете в виду создать прослушиватель событий, который прослушивает периодическое событие, которое повторяется через регулярные промежутки времени? Я подозреваю, что вы могли бы это сделать, но это было бы сложно, и я не понимаю, как это могло бы помочь.

3. Прошу прощения, я уточню. В предоставленных vtkInteractorStyles классах в VTK есть обработчики событий middleButtonPressEvent() , такие как leftButtonPressEvent() , и т.д. Я пытаюсь выяснить, как использовать эти обработчики (или написать свои собственные), чтобы я мог управлять камерой через свои собственные обработчики, а не через включенные. Конечная цель — иметь возможность управлять камерой с помощью данных гироскопа, а не с помощью мыши. Очевидно, это означает, что графический интерфейс не нужен, поэтому мне нужен способ изменения ориентации камеры, положения и т.д. без использования предоставленного vtkInteractorStyles

4. Взгляните на ‘vtkCustomInteractorStyle’. Также посмотрите, как графические интерфейсы переопределяют стили взаимодействия по умолчанию, перехватывая события графического интерфейса.

5. Я только что еще раз перечитал свой первоначальный вопрос, и вы полностью ответили на него. Мне следовало бы задать новый вопрос по поводу моей vtkInteractorStyles проблемы, но, тем не менее, благодарю за ответы. Я проверю это.

Ответ №2:

Для тех, кто случайно столкнется с такой же проблемой, как невозможность управлять камерой из vtkInteractorStyle классов, ознакомьтесь с Dolly() , Pan() , Spin() , Rotate() , Zoom() , и UniformScale() . Все это должно позволить вам получить доступ к камере из вашего дочернего класса vtkInteractorStyle , который вы используете. Удачи!

РЕДАКТИРОВАТЬ: еще лучше, просто прикрепите свою камеру к своему классу, который наследуется от vtkInteractorStyle как свойство, например:

style = MyInteractorStyleClass()
style.camera = myCam

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