class QtTrajectoryViewer(QMainWindow): """Bases: `PySide.QtGui.QMainWindow` Interface for viewing trajectory. It provides interface elements to play/pause and set the speed of the animation. **Example** To set up a QtTrajectoryViewer you have to add renderers to the scene, set the number of frames present in the animation by calling ;py:meth:`~chemlab.graphics.QtTrajectoryViewer.set_ticks` and define an update function. Below is an example taken from the function :py:func:`chemlab.graphics.display_trajectory`:: from chemlab.graphics import QtTrajectoryViewer # sys = some System # coords_list = some list of atomic coordinates v = QtTrajectoryViewer() sr = v.add_renderer(AtomRenderer, sys.r_array, sys.type_array, backend='impostors') br = v.add_renderer(BoxRenderer, sys.box_vectors) v.set_ticks(len(coords_list)) @v.update_function def on_update(index): sr.update_positions(coords_list[index]) br.update(sys.box_vectors) v.set_text(format_time(times[index])) v.widget.repaint() v.run() .. warning:: Use with caution, the API for this element is not fully stabilized and may be subject to change. """ def __init__(self): super(QtTrajectoryViewer, self).__init__() self.controls = QDockWidget() self._timer = QtCore.QTimer(self) self._timer.timeout.connect(self.do_update) # Eliminate the dock titlebar title_widget = QtGui.QWidget(self) self.controls.setTitleBarWidget(title_widget) vb = QtGui.QVBoxLayout() hb = QtGui.QHBoxLayout() # For controls containerhb2 = QtGui.QWidget(self) hb2 = QtGui.QHBoxLayout() # For settings containerhb2.setLayout(hb2) containerhb2.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum) vb.addWidget(containerhb2) vb.addLayout(hb) self.vb = vb # Settings buttons hb2.addWidget(QtGui.QLabel('Speed')) self._speed_slider = QtGui.QSlider(Qt.Horizontal) self._speed_slider.resize(100, self._speed_slider.height()) self._speed_slider.setSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) self.speeds = np.linspace(15, 250, 11).astype(int) self.speeds = self.speeds.tolist() self.speeds.reverse() self._speed_slider.setMaximum(10) self._speed_slider.setValue(7) self._speed_slider.valueChanged.connect(self.on_speed_changed) hb2.addWidget(self._speed_slider) hb2.addStretch(1) wrapper = QtGui.QWidget() wrapper.setLayout(vb) # Molecular viewer self.widget = QChemlabWidget(self) self.setCentralWidget(self.widget) # Control buttons self.play_stop = PlayStopButton() hb.addWidget(self.play_stop) self.slider = AnimationSlider() hb.addWidget(self.slider, 2) self._label_tmp = '<b><FONT SIZE=30>{}</b>' self.timelabel = QtGui.QLabel(self._label_tmp.format('0.0')) hb.addWidget(self.timelabel) self._settings_button = QtGui.QPushButton() self._settings_button.setStyleSheet(''' QPushButton { width: 30px; height: 30px; }''') icon = QtGui.QIcon(os.path.join(resources_dir, 'settings_icon.svg')) self._settings_button.setIcon(icon) self._settings_button.clicked.connect(self._toggle_settings) hb.addWidget(self._settings_button) self.controls.setWidget(wrapper) self.addDockWidget(Qt.DockWidgetArea(Qt.BottomDockWidgetArea), self.controls) self._settings_pan = containerhb2 self.show() self.speed = self.speeds[self._speed_slider.value()] # Connecting all the signals self.play_stop.play.connect(self.on_play) self.play_stop.pause.connect(self.on_pause) self.slider.valueChanged.connect(self.on_slider_change) self.slider.sliderPressed.connect(self.on_slider_down) self.play_stop.setFocus() vb.setSizeConstraint(QtGui.QLayout.SetMaximumSize) containerhb2.setVisible(False) def set_ticks(self, number): '''Set the number of frames to animate. ''' self.max_index = number self.current_index = 0 self.slider.setMaximum(self.max_index-1) self.slider.setMinimum(0) self.slider.setPageStep(1) def set_text(self, text): '''Update the time indicator in the interface.''' self.timelabel.setText(self._label_tmp.format(text)) def on_play(self): if self.current_index == self.max_index - 1: # Restart self.current_index = 0 self._timer.start(self.speed) def do_update(self): if self.current_index >= self.max_index: self.current_index = self.max_index - 1 self._timer.stop() self.play_stop.set_pause() else: self.current_index += 1 self.slider.setSliderPosition(self.current_index) def on_pause(self): self._timer.stop() def on_slider_change(self, value): #print 'Slider moved', value self.current_index = value self._update_function(self.current_index) def on_slider_down(self): self._timer.stop() self.play_stop.set_pause() def on_speed_changed(self, index): self.speed = self.speeds[index] if self._timer.isActive(): self._timer.stop() self._timer.start(self.speed) def add_renderer(self, klass, *args, **kwargs): '''The behaviour of this function is the same as :py:meth:`chemlab.graphics.QtViewer.add_renderer`. ''' renderer = klass(self.widget, *args, **kwargs) self.widget.renderers.append(renderer) return renderer def add_ui(self, klass, *args, **kwargs): '''Add an UI element for the current scene. The approach is the same as renderers. .. warning:: The UI api is not yet finalized ''' ui = klass(self.widget, *args, **kwargs) self.widget.uis.append(ui) return ui def add_post_processing(self, klass, *args, **kwargs): pp = klass(self.widget, *args, **kwargs) self.widget.post_processing.append(pp) return pp def run(self): app.exec_() def update_function(self, func): '''Set the function to be called when it's time to display a frame. *func* should be a function that takes one integer argument that represents the frame that has to be played:: def func(index): # Update the renderers to match the # current animation index ''' self._update_function = func def _toggle_settings(self): self._settings_pan.setVisible(not self._settings_pan.isVisible())
class QtTrajectoryViewer(QMainWindow): """Bases: `PySide.QtGui.QMainWindow` Interface for viewing trajectory. It provides interface elements to play/pause and set the speed of the animation. **Example** To set up a QtTrajectoryViewer you have to add renderers to the scene, set the number of frames present in the animation by calling ;py:meth:`~chemlab.graphics.QtTrajectoryViewer.set_ticks` and define an update function. Below is an example taken from the function :py:func:`chemlab.graphics.display_trajectory`:: from chemlab.graphics import QtTrajectoryViewer # sys = some System # coords_list = some list of atomic coordinates v = QtTrajectoryViewer() sr = v.add_renderer(AtomRenderer, sys.r_array, sys.type_array, backend='impostors') br = v.add_renderer(BoxRenderer, sys.box_vectors) v.set_ticks(len(coords_list)) @v.update_function def on_update(index): sr.update_positions(coords_list[index]) br.update(sys.box_vectors) v.set_text(format_time(times[index])) v.widget.repaint() v.run() .. warning:: Use with caution, the API for this element is not fully stabilized and may be subject to change. """ def __init__(self): super(QtTrajectoryViewer, self).__init__() self.controls = QDockWidget() # Eliminate the dock titlebar title_widget = QtGui.QWidget(self) self.controls.setTitleBarWidget(title_widget) traj_controls = TrajectoryControls(self) self.controls.setWidget(traj_controls) # Molecular viewer self.widget = QChemlabWidget(self) self.setCentralWidget(self.widget) self.addDockWidget(Qt.DockWidgetArea(Qt.BottomDockWidgetArea), self.controls) self.show() # Replace in this way traj_controls.frame_changed.connect(self.on_frame_changed) self.traj_controls = traj_controls def set_ticks(self, number): self.traj_controls.set_ticks(number) def set_text(self, text): '''Update the time indicator in the interface. ''' self.traj_controls.timelabel.setText(self.traj_controls._label_tmp.format(text)) def on_frame_changed(self, index): self._update_function(index) def on_pause(self): self._timer.stop() def on_slider_change(self, value): self.current_index = value self._update_function(self.current_index) def on_slider_down(self): self._timer.stop() self.play_stop.set_pause() def on_speed_changed(self, index): self.speed = self.speeds[index] if self._timer.isActive(): self._timer.stop() self._timer.start(self.speed) def add_renderer(self, klass, *args, **kwargs): '''The behaviour of this function is the same as :py:meth:`chemlab.graphics.QtViewer.add_renderer`. ''' renderer = klass(self.widget, *args, **kwargs) self.widget.renderers.append(renderer) return renderer def add_ui(self, klass, *args, **kwargs): '''Add an UI element for the current scene. The approach is the same as renderers. .. warning:: The UI api is not yet finalized ''' ui = klass(self.widget, *args, **kwargs) self.widget.uis.append(ui) return ui def add_post_processing(self, klass, *args, **kwargs): pp = klass(self.widget, *args, **kwargs) self.widget.post_processing.append(pp) return pp def run(self): app.exec_() def update_function(self, func, frames=None): '''Set the function to be called when it's time to display a frame. *func* should be a function that takes one integer argument that represents the frame that has to be played:: def func(index): # Update the renderers to match the # current animation index ''' # Back-compatibility if frames is not None: self.traj_controls.set_ticks(frames) self._update_function = func def _toggle_settings(self): self._settings_pan.setVisible(not self._settings_pan.isVisible())
class QtTrajectoryViewer(QMainWindow): """Bases: `PySide.QtGui.QMainWindow` Interface for viewing trajectory. It provides interface elements to play/pause and set the speed of the animation. **Example** To set up a QtTrajectoryViewer you have to add renderers to the scene, set the number of frames present in the animation by calling ;py:meth:`~chemlab.graphics.QtTrajectoryViewer.set_ticks` and define an update function. Below is an example taken from the function :py:func:`chemlab.graphics.display_trajectory`:: from chemlab.graphics import QtTrajectoryViewer # sys = some System # coords_list = some list of atomic coordinates v = QtTrajectoryViewer() sr = v.add_renderer(AtomRenderer, sys.r_array, sys.type_array, backend='impostors') br = v.add_renderer(BoxRenderer, sys.box_vectors) v.set_ticks(len(coords_list)) @v.update_function def on_update(index): sr.update_positions(coords_list[index]) br.update(sys.box_vectors) v.set_text(format_time(times[index])) v.widget.repaint() v.run() .. warning:: Use with caution, the API for this element is not fully stabilized and may be subject to change. """ def __init__(self): super(QtTrajectoryViewer, self).__init__() self.controls = QDockWidget() self._timer = QtCore.QTimer(self) self._timer.timeout.connect(self.do_update) # Eliminate the dock titlebar title_widget = QtGui.QWidget(self) self.controls.setTitleBarWidget(title_widget) vb = QtGui.QVBoxLayout() hb = QtGui.QHBoxLayout() # For controls containerhb2 = QtGui.QWidget(self) hb2 = QtGui.QHBoxLayout() # For settings containerhb2.setLayout(hb2) containerhb2.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum) vb.addWidget(containerhb2) vb.addLayout(hb) self.vb = vb # Settings buttons hb2.addWidget(QtGui.QLabel('Speed')) self._speed_slider = QtGui.QSlider(Qt.Horizontal) self._speed_slider.resize(100, self._speed_slider.height()) self._speed_slider.setSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) self.speeds = np.linspace(15, 250, 11).astype(int) self.speeds = self.speeds.tolist() self.speeds.reverse() self._speed_slider.setMaximum(10) self._speed_slider.setValue(7) self._speed_slider.valueChanged.connect(self.on_speed_changed) hb2.addWidget(self._speed_slider) hb2.addStretch(1) wrapper = QtGui.QWidget() wrapper.setLayout(vb) # Molecular viewer self.widget = QChemlabWidget(self) self.setCentralWidget(self.widget) # Control buttons self.play_stop = PlayStopButton() hb.addWidget(self.play_stop) self.slider = AnimationSlider() hb.addWidget(self.slider, 2) self._label_tmp = '<b><FONT SIZE=30>{}</b>' self.timelabel = QtGui.QLabel(self._label_tmp.format('0.0')) hb.addWidget(self.timelabel) self._settings_button = QtGui.QPushButton() self._settings_button.setStyleSheet(''' QPushButton { width: 30px; height: 30px; }''') icon = QtGui.QIcon(os.path.join(resources_dir, 'settings_icon.svg')) self._settings_button.setIcon(icon) self._settings_button.clicked.connect(self._toggle_settings) hb.addWidget(self._settings_button) self.controls.setWidget(wrapper) self.addDockWidget(Qt.DockWidgetArea(Qt.BottomDockWidgetArea), self.controls) self._settings_pan = containerhb2 self.show() self.speed = self.speeds[self._speed_slider.value()] # Connecting all the signals self.play_stop.play.connect(self.on_play) self.play_stop.pause.connect(self.on_pause) self.slider.valueChanged.connect(self.on_slider_change) self.slider.sliderPressed.connect(self.on_slider_down) self.play_stop.setFocus() vb.setSizeConstraint(QtGui.QLayout.SetMaximumSize) containerhb2.setVisible(False) def set_ticks(self, number): '''Set the number of frames to animate. ''' self.max_index = number self.current_index = 0 self.slider.setMaximum(self.max_index - 1) self.slider.setMinimum(0) self.slider.setPageStep(1) def set_text(self, text): '''Update the time indicator in the interface.''' self.timelabel.setText(self._label_tmp.format(text)) def on_play(self): if self.current_index == self.max_index - 1: # Restart self.current_index = 0 self._timer.start(self.speed) def do_update(self): if self.current_index >= self.max_index: self.current_index = self.max_index - 1 self._timer.stop() self.play_stop.set_pause() else: self.current_index += 1 self.slider.setSliderPosition(self.current_index) def on_pause(self): self._timer.stop() def on_slider_change(self, value): #print 'Slider moved', value self.current_index = value self._update_function(self.current_index) def on_slider_down(self): self._timer.stop() self.play_stop.set_pause() def on_speed_changed(self, index): self.speed = self.speeds[index] if self._timer.isActive(): self._timer.stop() self._timer.start(self.speed) def add_renderer(self, klass, *args, **kwargs): '''The behaviour of this function is the same as :py:meth:`chemlab.graphics.QtViewer.add_renderer`. ''' renderer = klass(self.widget, *args, **kwargs) self.widget.renderers.append(renderer) return renderer def add_ui(self, klass, *args, **kwargs): '''Add an UI element for the current scene. The approach is the same as renderers. .. warning:: The UI api is not yet finalized ''' ui = klass(self.widget, *args, **kwargs) self.widget.uis.append(ui) return ui def add_post_processing(self, klass, *args, **kwargs): pp = klass(self.widget, *args, **kwargs) self.widget.post_processing.append(pp) return pp def run(self): app.exec_() def update_function(self, func): '''Set the function to be called when it's time to display a frame. *func* should be a function that takes one integer argument that represents the frame that has to be played:: def func(index): # Update the renderers to match the # current animation index ''' self._update_function = func def _toggle_settings(self): self._settings_pan.setVisible(not self._settings_pan.isVisible())