Python & VTK & PyQt5: Как да направя екранна снимка на vtk рендиране в def __init__?

Какво съм направил:

  1. Създайте приложение с PyQt5 и VTK, добавям 2 VTK рендиране в 2 QTWidget.

  2. Във всеки рендер на VTK чета .stl файл и го показвам.

  3. Добавям функция screen_shot, тя може да изобрази VTK екранна снимка и да запази тези снимки.

  4. Добавям лента с менюта, мога да щракна върху лента с менюта и VTK рендиране на екранна снимка.

Какво искам да направя:

Искам да изпълня screen_shot след зареждане на VTK рендиране, записвам го в def __init__. Но по този начин получавам само снимка 100 * 30 (всъщност 900 * 900). Когато щракна върху менюто, ще получа снимка 900 * 900.

Може би моето описание отвъд разбирането, това е моят код:

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_())

Как да направя екранна снимка в def __init__? Трябва ли да изпълня тази функция?


person Zhengfei Xin    schedule 03.01.2020    source източник


Отговори (2)


Трябва да изчакате, докато цикълът на основното събитие започне и графичният потребителски интерфейс бъде напълно изобразен, преди да направите екранна снимка. Това означава, че този 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)

    ...
person Heike    schedule 03.01.2020

В края на вашия __init__ метод поставете:

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

QtWidgets.QApplication.instance() получава вашето app. .processEvents() изпълнява цикъла на събитието веднъж, което позволява извършването на необходимите инициализации, които @Heike споменава в своя отговор. app.exec_() е приблизително еквивалентен на:

while windows_are_open:
    app.processEvents()
person pullmyteeth    schedule 09.01.2020