Skip to content

Add animation support for maps #51

Description

@bnlawrence

Generate frames in the worker:

  • cf-python worker generates contour maps as matplotlib figures
  • Convert each to an image (PNG bytes or QPixmap)
  • Store them in a queue or list that the main PySide thread can access

Animate in PySide:

  • Use a QTimer to cycle through frames at a set interval
  • Update the image frame's pixmap on each timer tick
  • (This gives smooth animation without blocking the UI)

Basic implementation pattern could be based on

from PySide6.QtWidgets import QLabel, QVBoxLayout, QWidget
from PySide6.QtGui import QPixmap, QImage
from PySide6.QtCore import QTimer, Qt
from io import BytesIO
import matplotlib.pyplot as plt

class AnimatedContourWidget(QWidget):
    def __init__(self, frames_queue):
        super().__init__()
        self.frames_queue = frames_queue
        self.frames = []
        self.current_frame = 0
        
        # Image label
        self.image_label = QLabel()
        self.image_label.setAlignment(Qt.AlignCenter)
        
        layout = QVBoxLayout()
        layout.addWidget(self.image_label)
        self.setLayout(layout)
        
        # Animation timer
        self.timer = QTimer()
        self.timer.timeout.connect(self.show_next_frame)
        self.timer.setInterval(100)  # 100ms between frames = 10 FPS
        
    def set_frames(self, frames_list):
        """Called once all frames are ready from worker"""
        self.frames = frames_list
        self.current_frame = 0
        self.timer.start()
        
    def show_next_frame(self):
        if not self.frames:
            return
        
        # Get current frame (matplotlib figure as bytes)
        fig_bytes = self.frames[self.current_frame]
        
        # Convert to QPixmap
        pixmap = QPixmap()
        pixmap.loadFromData(fig_bytes, 'PNG')
        self.image_label.setPixmap(pixmap)
        
        # Next frame
        self.current_frame = (self.current_frame + 1) % len(self.frames)

with server side:

def generate_contours(field_data, num_frames=50):
    """Worker function that generates 50 contour maps"""
    frames = []
    
    for i in range(num_frames):
        fig, ax = plt.subplots(figsize=(8, 6))
        
        # Generate/slice your field data for this frame
        data_slice = field_data[i]  # or however you're iterating
        
        contour = ax.contourf(data_slice, levels=20, cmap='viridis')
        ax.set_title(f'Frame {i+1}/{num_frames}')
        plt.colorbar(contour, ax=ax)
        
        # Convert to PNG bytes
        buf = BytesIO()
        fig.savefig(buf, format='png', dpi=100, bbox_inches='tight')
        buf.seek(0)
        frames.append(buf.getvalue())
        
        plt.close(fig)
    
    return frames

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    Status
    Backlog

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions