求问:是否有人成功将Matplotlib图表嵌入FLTKHS GUI中?
Hey there! Great question—combining these two libraries does feel tricky at first given their different underlying implementations (C++ for FLTK, Python for matplotlib), but there are solid, battle-tested approaches folks in the Haskell community have used successfully. Directly passing chart handles won't work (since they're tied to separate runtime environments), but rendering matplotlib plots as images and displaying those in FLTKHS is a reliable workaround.
Approach 1: Save Plot to Temporary File (Simple & Straightforward)
This method generates a matplotlib plot, saves it to a temporary PNG file, then loads and displays that image in an FLTKHS window. It's easy to implement and requires minimal setup.
Step 1: Add Dependencies
First, update your Cabal file with these required packages:
build-depends: base >=4.14 && <5, fltkhs >=0.8.0, matplotlib >=0.9.0, temporary >=1.3, directory >=1.3
Step 2: Example Code
import Graphics.UI.FLTK.LowLevel.FLTKHS (run, Window(..), Box(..), imageDisplayNew, boxSetImage, windowShow, defaultWindow, defaultBox, size, position) import Graphics.Matplotlib (plot, scatter, title, xlabel, ylabel, savefig) import System.IO.Temp (withSystemTempFile) import System.Directory (removeFile) import Graphics.UI.FLTK.LowLevel.Image (PngImage(..), loadPngImage) main :: IO () main = do -- Generate sample data and configure the matplotlib plot let sampleData = [(x, x^2) | x <- [0..10]] plotConfig = plot (scatter sampleData) <> title "Quadratic Plot Example" <> xlabel "X Value" <> ylabel "X Squared" -- Use a temporary file to avoid cluttering the filesystem withSystemTempFile "matplotlib_plot.png" $ \tempPath _ -> do -- Save the plot to the temporary PNG savefig tempPath plotConfig -- Load the PNG into an FLTK-compatible image maybeImg <- loadPngImage tempPath case maybeImg of Just img -> do -- Create the main application window mainWindow <- defaultWindow (size (600, 500)) (position (100, 100)) "FLTKHS + Matplotlib" -- Create a box to display the image imgDisplay <- imageDisplayNew (defaultBox (size (580, 480)) (position (10, 10))) Nothing boxSetImage imgDisplay (Just img) -- Show the window and start the FLTK event loop windowShow mainWindow run Nothing -> putStrLn "Error: Failed to load the plot image" -- Clean up the temporary file (handled automatically by withSystemTempFile, but explicit here) removeFile tempPath
Approach 2: In-Memory Image Rendering (Faster, No File IO)
If you need dynamic updates or want to avoid writing to disk, you can render the matplotlib plot directly to in-memory PNG data, then decode and display it in FLTKHS. This uses the hs-python library to interact with Python directly.
Step 1: Add Extra Dependency
Add hs-python to your Cabal build-depends:
build-depends: ..., hs-python >=0.1.0, bytestring >=0.10, JuicyPixels >=3.3
Step 2: Simplified Example Code
import Foreign.Python (withPython, runPython, pyImport, pyCallMethod, pyGetAttrString, (.=)) import Data.ByteString.Lazy (toStrict) import Codec.Picture (decodePng, DynamicImage(..), convertRGBA8) import Graphics.UI.FLTK.LowLevel.FLTKHS (run, Window(..), Box(..), imageDisplayNew, boxSetImage, windowShow, defaultWindow, defaultBox, size, position) import Graphics.UI.FLTK.LowLevel.Image (RgbImage(..), rgbImageNew) main :: IO () main = withPython $ do runPython $ do -- Import matplotlib.pyplot plt <- pyImport "matplotlib.pyplot" -- Create a simple plot _ <- pyCallMethod plt "plot" [1, 3, 2, 4, 5] _ <- pyCallMethod plt "title" ["In-Memory Dynamic Plot"] -- Render plot to in-memory PNG buffer buf <- pyCallMethod plt "savefig" ["-", "format" .= "png"] pngByteString <- toStrict <$> pyGetAttrString buf "getvalue" -- Decode PNG to a format FLTK can use case decodePng pngByteString of Right (ImageRGBA8 img) -> do fltkImage <- rgbImageNew (convertRGBA8 img) -- Create window and display the image (same as Approach 1) mainWindow <- defaultWindow (size (600, 500)) (position (100, 100)) "In-Memory Plot Example" imgDisplay <- imageDisplayNew (defaultBox (size (580, 480)) (position (10, 10))) Nothing boxSetImage imgDisplay (Just fltkImage) windowShow mainWindow run _ -> putStrLn "Error: Failed to decode in-memory PNG" -- Clean up matplotlib resources _ <- pyCallMethod plt "close" []
Key Notes
- Direct handle sharing is impossible because FLTK and matplotlib run on separate runtime stacks (C++ vs Python). Image rendering is the only practical cross-runtime bridge here.
- For dynamic plots (e.g., updating data in real-time), you can re-run the plot generation and image loading code on a timer in FLTKHS.
内容的提问来源于stack exchange,提问作者James Strieter

