Python&VTK&PyQt5:如何在def __init__中截取vtk渲染的屏幕?

huangapple go评论69阅读模式
英文:

Python & VTK & PyQt5: How to screenshot vtk render in def __init__?

问题

如何在def __init__中进行屏幕截图?我需要完成这个功能吗?

要在def __init__中执行屏幕截图,你可以在初始化函数中调用shot_screen方法。在你的代码中,shot_screen方法已经可以截取屏幕并保存图片。要在def __init__中调用它,只需在初始化函数的适当位置添加以下代码行:

self.shot_screen()

你可以将这行代码添加到__init__函数的最后,这样在创建主窗口时就会自动执行屏幕截图操作。这将导致在加载 VTK 渲染器后立即进行一次屏幕截图。

希望这对你有所帮助。

英文:

What have I done:

  1. Create a app with PyQt5 and VTK, I add 2 VTK render in 2 QTWidget.

  2. In each VTK render, I read a .stl file and show it.

  3. I add a function screen_shot, it can screen shot VTK render and save this pics.

  4. I add a menubar, I can click a menubar and screen shot VTK render.

What I want to do:

I want execute screen_shot after load VTK render, I write it in def _init_. But I only get a 100 * 30 pic in this way (900 * 900 in fact). When I click menu, I will get a 900 * 900 pic.

Maybe my description beyond understanding, this my code:

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        QtWidgets.QMainWindow.__init__(self, parent)

        self.workspace = ""
        self.data_path = ""
        self.data_index = 0
        self.bkcolor = (1.0, 1.0, 1.0)

        self.frame = QtWidgets.QFrame()
        self.setWindowTitle("PythonVTKViewer")
        # self.frame.setLayout(self.box)

        self.stl_interactor = QtWidgets.QHBoxLayout()
        self.init_menubar()
        self.showMaximized()
        self.load_Interactor()
        self.shot_screen()

    def init_menubar(self):
        # init a menubar
        menubar = self.menuBar()

        fileMenu = menubar.addMenu('&Function')

        self.screen_shot_button = QtWidgets.QAction('ScreenShot', self)
        self.screen_shot_button.setShortcut('Ctrl+S')
        self.screen_shot_button.setStatusTip('ScreenShot')
        self.screen_shot_button.triggered.connect(self.shot_screen)
        fileMenu.addAction(self.screen_shot_button)

    def load_Interactor(self):
        # Create two render windows
        self.vtkWidget_left = QVTKRenderWindowInteractor(self.frame)
        self.vtkWidget_right = QVTKRenderWindowInteractor(self.frame)

        self.stl_interactor.addWidget(self.vtkWidget_left)
        self.stl_interactor.addWidget(self.vtkWidget_right)

        # Left renderer and right renderer
        self.ren_left = vtk.vtkRenderer()
        self.vtkWidget_left.GetRenderWindow().AddRenderer(self.ren_left)
        self.renWinLeft = self.vtkWidget_left.GetRenderWindow()
        self.iren_left = self.renWinLeft.GetInteractor()

        self.ren_right = vtk.vtkRenderer()
        self.vtkWidget_right.GetRenderWindow().AddRenderer(self.ren_right)
        self.iren_right = self.vtkWidget_right.GetRenderWindow().GetInteractor()
        
        # read stl file
        upper_path = os.path.join(self.workspace, "stl/scanner/start/UpperTeeth.stl")
        lower_path = os.path.join(self.workspace, "stl/scanner/start/LowerTeeth.stl")
        self.init_stl("./temp/upperteeth.stl")
        self.init_stl("./temp/lowerteeth.stl")

        self.frame.setLayout(self.stl_interactor)

        self.setCentralWidget(self.frame)

        self.ren_left.SetBackground(self.bkcolor[0], self.bkcolor[1], self.bkcolor[2])
        self.ren_right.SetBackground(self.bkcolor[0], self.bkcolor[1], self.bkcolor[2])
        
        # adjust camera
        self.init_camera()

        self.iren_left.Initialize()
        self.iren_right.Initialize()

    def init_stl(self, file):
        # reader = Reader.read_data(file)
        reader = vtk.vtkSTLReader()
        if "upper" in file or "Upper" in file:
            reader.SetFileName(file)
            reader.Update()

            self.mapper_left = vtk.vtkPolyDataMapper()
            self.mapper_left.SetInputConnection(reader.GetOutputPort())
            self.actor_left = vtk.vtkActor()
            self.actor_left.SetMapper(self.mapper_left)
            self.ren_left.AddActor(self.actor_left)
        else:
            reader.SetFileName(file)
            reader.Update()

            self.mapper_right = vtk.vtkPolyDataMapper()
            self.mapper_right.SetInputConnection(reader.GetOutputPort())
            self.actor_right = vtk.vtkActor()
            self.actor_right.SetMapper(self.mapper_right)
            self.ren_right.AddActor(self.actor_right)

    def init_camera(self):
        self.ren_left.GetActiveCamera().SetFocalPoint(0, 0, 0)
        self.ren_left.GetActiveCamera().SetPosition(0, 0, -150)
        self.ren_left.GetActiveCamera().Roll(90)
        self.ren_left.GetActiveCamera().ParallelProjectionOn()
        self.ren_left.GetActiveCamera().SetParallelScale(40)

        self.ren_right.GetActiveCamera().SetFocalPoint(0, 0, 0)
        self.ren_right.GetActiveCamera().SetPosition(0, 0, 150)
        self.ren_right.GetActiveCamera().Roll(270)
        self.ren_right.GetActiveCamera().ParallelProjectionOn()
        self.ren_right.GetActiveCamera().SetParallelScale(40)

    def shot_screen(self):
        if os.path.exists("./temp/input/upper.png"): os.remove("./temp/input/upper.png")
        elif os.path.exists("./temp/input/lower.png"): os.remove("./temp/input/lower.png")

        filter = vtk.vtkRenderLargeImage()
        filter.SetMagnification(1)
        filter.SetInput(self.ren_left)
        writer = vtk.vtkPNGWriter()
        writer.SetFileName("./temp/input/upper.png")
        writer.SetInputConnection(filter.GetOutputPort())
        writer.Write()
        
        filter = vtk.vtkRenderLargeImage()
        filter.SetMagnification(1)
        filter.SetInput(self.ren_right)
        writer = vtk.vtkPNGWriter()
        writer.SetFileName("./temp/input/lower.png")
        writer.SetInputConnection(filter.GetOutputPort())
        writer.Write()




if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    window = MainWindow()
    sys.exit(app.exec_())

How to screen shot in def _init_? I need to complete this function?

答案1

得分: 1

你需要等待主事件循环已经启动并且GUI已完全渲染完成后才能进行屏幕截图。这意味着app.exec_(),它启动主事件循环,应该在MainWindow.shot_screen之前执行。从MainWindow.__init__内部实现此目标的一种方法是使用单次触发的QTimer来延迟执行MainWindow.shot_screen,直到事件循环启动后,例如:

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        ...
        QtCore.QTimer.singleShot(10, self.shot_screen)

    ...
英文:

You need to wait until the main event loop has started and the GUI has been fully rendered before taking the screen shot. This means that that app.exec_(), which starts the main event loop, should be executed before MainWindow.shot_screen. One way to achieve this from within MainWindow.__init__ is to use a single shot QTimer to delay the execution of MainWindow.shot_screen until after the event loop has started, e.g.

class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
...
QtCore.QTimer.singleShot(10, self.shot_screen)
...

答案2

得分: 0

在您的__init__方法末尾添加:

QtWidgets.QApplication.instance().processEvents()
self.shot_screen()

QtWidgets.QApplication.instance()获取您的app.processEvents()执行事件循环一次,允许在@Heike在他的回答中提到的必要初始化发生。app.exec_()大致相当于:

while 窗口仍然打开着:
app.processEvents()
英文:

At the end of your __init__ method put:

QtWidgets.QApplication.instance().processEvents()
self.shot_screen()

The QtWidgets.QApplication.instance() gets your app. The .processEvents() excecutes the event loop once which allows the required initialisations to occur that @Heike mentions in his answer. app.exec_() is roughly equivalent to:

while windows_are_open:
app.processEvents()

huangapple
  • 本文由 发表于 2020年1月3日 17:40:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/59576178.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定