You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于FIFO的C主程序与Python GUI(matplotlib绘图)IPC对接技术问询

Got it, let's work through this IPC setup between your C telemetry controller and Python GUI. I'll break this down into actionable steps with code examples that fit your exact requirements:

整体架构 Overview

Here's the core flow we'll implement:

  1. Your C main controller processes frame-based telemetry, then sends frame numbers to Python via a FIFO (named pipe).
  2. The Python GUI runs in a separate process, listens for incoming frame numbers via the FIFO.
  3. The GUI lets users load/run custom matplotlib plotting scripts, and passes received frame numbers to these scripts to trigger real-time updates.

1. FIFO Implementation (C ↔ Python)

FIFOs are perfect here because they're lightweight, POSIX-compliant, and work seamlessly between C and Python. We'll use a single FIFO for C → Python frame number transmission (add a second FIFO later if you need Python to send commands back to C).

C Side (Main Controller)

This code creates the FIFO (if it doesn't exist) and writes frame numbers to it as your telemetry system generates frames:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>

#define FIFO_PATH "/tmp/telemetry_frame_fifo"

int main() {
    // Create FIFO with read/write permissions for all users
    if (mkfifo(FIFO_PATH, 0666) == -1) {
        perror("mkfifo failed (might already exist, that's okay)");
    }

    int fifo_fd = open(FIFO_PATH, O_WRONLY);
    if (fifo_fd == -1) {
        perror("Failed to open FIFO for writing");
        exit(EXIT_FAILURE);
    }

    // Simulate telemetry frame generation (replace with your actual frame logic)
    int frame_counter = 0;
    while (1) {
        frame_counter++;
        // Format frame number as a string (add more data here if needed)
        char buffer[64];
        snprintf(buffer, sizeof(buffer), "%d\n", frame_counter);
        
        // Write to FIFO
        ssize_t bytes_written = write(fifo_fd, buffer, strlen(buffer));
        if (bytes_written == -1) {
            perror("Failed to write to FIFO");
        }

        sleep(1); // Simulate 1Hz frame rate
    }

    close(fifo_fd);
    unlink(FIFO_PATH); // Clean up FIFO on exit
    return 0;
}

Python Side (GUI Listener)

We'll use a background thread to listen to the FIFO so it doesn't block the GUI's main loop:

import os
import threading

FIFO_PATH = "/tmp/telemetry_frame_fifo"

class FIFOFramelistener(threading.Thread):
    def __init__(self, frame_update_callback):
        super().__init__(daemon=True)
        self.callback = frame_update_callback
        self.running = True

    def run(self):
        # Ensure FIFO exists before opening
        if not os.path.exists(FIFO_PATH):
            os.mkfifo(FIFO_PATH)
        
        # Open FIFO in read mode (blocks until C process opens it for writing)
        with open(FIFO_PATH, "r") as fifo:
            while self.running:
                line = fifo.readline()
                if line:
                    try:
                        frame_num = int(line.strip())
                        self.callback(frame_num)
                    except ValueError:
                        print(f"Invalid frame data received: {line}")

    def stop(self):
        self.running = False

2. Python GUI with Script Loading & Plot Updates

We'll use Tkinter (built-in, no extra installs) for the GUI, and matplotlib for plotting. The GUI will let users load custom .py scripts, initialize their plots, and pass frame numbers to trigger updates.

Core GUI Code

import tkinter as tk
from tkinter import filedialog
import importlib.util
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

class TelemetryPlotGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("Telemetry Plot Manager")
        
        # State variables
        self.plot_script = None
        self.figure = None
        self.canvas = None

        # UI Elements
        self.load_btn = tk.Button(root, text="Load Custom Plot Script", command=self.load_plot_script)
        self.load_btn.pack(pady=10)

        # Start FIFO listener
        self.fifo_listener = FIFOFramelistener(self.on_frame_received)
        self.fifo_listener.start()

        # Cleanup on window close
        self.root.protocol("WM_DELETE_WINDOW", self.shutdown)

    def load_plot_script(self):
        # Let user select a .py file
        script_path = filedialog.askopenfilename(filetypes=[("Python Scripts", "*.py")])
        if not script_path:
            return
        
        # Dynamically import the script
        spec = importlib.util.spec_from_file_location("custom_plot", script_path)
        self.plot_script = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(self.plot_script)

        # Initialize plot from the script
        if hasattr(self.plot_script, "init_plot"):
            self.figure = self.plot_script.init_plot()
            # Embed matplotlib figure into Tkinter
            self.canvas = FigureCanvasTkAgg(self.figure, master=self.root)
            self.canvas.draw()
            self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
        else:
            print("Error: Script must have an `init_plot()` function")

    def on_frame_received(self, frame_num):
        # Pass frame number to the plot script (run on GUI main thread to avoid crashes)
        if self.plot_script and hasattr(self.plot_script, "update_plot"):
            self.root.after(0, self.plot_script.update_plot, frame_num, self.figure)
        else:
            print("No plot script loaded, or missing `update_plot()` function")

    def shutdown(self):
        self.fifo_listener.stop()
        self.root.destroy()

if __name__ == "__main__":
    root = tk.Tk()
    app = TelemetryPlotGUI(root)
    root.mainloop()

3. Example Custom Plot Script

Users need to write scripts that follow a simple interface:

  • init_plot(): Returns a matplotlib figure object (sets up axes, labels, initial lines)
  • update_plot(frame_num, figure): Updates the plot using the received frame number

Here's a sample script:

import matplotlib.pyplot as plt
import numpy as np

# Store historical data for plotting
frame_history = []
telemetry_data = []

def init_plot():
    fig, ax = plt.subplots(figsize=(8, 4))
    ax.set_xlabel("Frame Number")
    ax.set_ylabel("Simulated Telemetry Value")
    ax.set_title("Real-Time Telemetry Feed")
    # Initialize an empty line to update later
    global plot_line
    plot_line, = ax.plot([], [], "b-", linewidth=2)
    return fig

def update_plot(frame_num, figure):
    # Simulate fetching telemetry data for this frame (replace with your logic)
    # In practice, you could load data from a file/database using the frame_num
    data_point = np.random.randn() * 5 + 20

    # Update history
    frame_history.append(frame_num)
    telemetry_data.append(data_point)

    # Update plot line
    plot_line.set_data(frame_history, telemetry_data)

    # Auto-scale axes to fit new data
    ax = figure.gca()
    ax.relim()
    ax.autoscale_view()

    # Refresh the plot
    figure.canvas.draw_idle()

Key Notes & Best Practices

  • FIFO Blocking: The C process will block on write() if the Python GUI isn't running and listening. To fix this, open the FIFO in non-blocking mode (O_WRONLY | O_NONBLOCK) in C, and handle EAGAIN errors by retrying later.
  • Thread Safety: Always update matplotlib plots from the GUI's main thread (we use root.after(0, ...) to do this) to avoid crashes.
  • Extendable Data: If you need to send more than just frame numbers, serialize data to JSON (use cJSON in C, json module in Python) for easy parsing.
  • Script Validation: Add checks in the GUI to ensure loaded scripts have the required init_plot() and update_plot() functions before allowing execution.

内容的提问来源于stack exchange,提问作者csandy83

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 03:26:57