plot-gtk-ui (empty) → 0.0.1.0
raw patch · 10 files changed
+2136/−0 lines, 10 filesdep +basedep +cairodep +coloursetup-changed
Dependencies added: base, cairo, colour, fixed-vector, gtk, hmatrix, plot, text, vector
Files
- LICENSE +339/−0
- README.md +84/−0
- Setup.lhs +3/−0
- lib/Graphics/Rendering/Plot/Gtk/UI.hs +96/−0
- lib/Graphics/Rendering/Plot/Gtk/UI/Events.hs +224/−0
- lib/Graphics/Rendering/Plot/Gtk/UI/Figure.hs +119/−0
- lib/Graphics/Rendering/Plot/Gtk/UI/PlotWindow.hs +168/−0
- lib/Graphics/Rendering/Plot/Gtk/UI/Settings.hs +96/−0
- plot-gtk-ui.cabal +49/−0
- ui/plot-window.glade +958/−0
+ LICENSE view
@@ -0,0 +1,339 @@+ GNU GENERAL PUBLIC LICENSE+ Version 2, June 1991++ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA+ Everyone is permitted to copy and distribute verbatim copies+ of this license document, but changing it is not allowed.++ Preamble++ The licenses for most software are designed to take away your+freedom to share and change it. By contrast, the GNU General Public+License is intended to guarantee your freedom to share and change free+software--to make sure the software is free for all its users. This+General Public License applies to most of the Free Software+Foundation's software and to any other program whose authors commit to+using it. (Some other Free Software Foundation software is covered by+the GNU Lesser General Public License instead.) You can apply it to+your programs, too.++ When we speak of free software, we are referring to freedom, not+price. Our General Public Licenses are designed to make sure that you+have the freedom to distribute copies of free software (and charge for+this service if you wish), that you receive source code or can get it+if you want it, that you can change the software or use pieces of it+in new free programs; and that you know you can do these things.++ To protect your rights, we need to make restrictions that forbid+anyone to deny you these rights or to ask you to surrender the rights.+These restrictions translate to certain responsibilities for you if you+distribute copies of the software, or if you modify it.++ For example, if you distribute copies of such a program, whether+gratis or for a fee, you must give the recipients all the rights that+you have. You must make sure that they, too, receive or can get the+source code. And you must show them these terms so they know their+rights.++ We protect your rights with two steps: (1) copyright the software, and+(2) offer you this license which gives you legal permission to copy,+distribute and/or modify the software.++ Also, for each author's protection and ours, we want to make certain+that everyone understands that there is no warranty for this free+software. If the software is modified by someone else and passed on, we+want its recipients to know that what they have is not the original, so+that any problems introduced by others will not reflect on the original+authors' reputations.++ Finally, any free program is threatened constantly by software+patents. We wish to avoid the danger that redistributors of a free+program will individually obtain patent licenses, in effect making the+program proprietary. To prevent this, we have made it clear that any+patent must be licensed for everyone's free use or not licensed at all.++ The precise terms and conditions for copying, distribution and+modification follow.++ GNU GENERAL PUBLIC LICENSE+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION++ 0. This License applies to any program or other work which contains+a notice placed by the copyright holder saying it may be distributed+under the terms of this General Public License. The "Program", below,+refers to any such program or work, and a "work based on the Program"+means either the Program or any derivative work under copyright law:+that is to say, a work containing the Program or a portion of it,+either verbatim or with modifications and/or translated into another+language. (Hereinafter, translation is included without limitation in+the term "modification".) Each licensee is addressed as "you".++Activities other than copying, distribution and modification are not+covered by this License; they are outside its scope. The act of+running the Program is not restricted, and the output from the Program+is covered only if its contents constitute a work based on the+Program (independent of having been made by running the Program).+Whether that is true depends on what the Program does.++ 1. You may copy and distribute verbatim copies of the Program's+source code as you receive it, in any medium, provided that you+conspicuously and appropriately publish on each copy an appropriate+copyright notice and disclaimer of warranty; keep intact all the+notices that refer to this License and to the absence of any warranty;+and give any other recipients of the Program a copy of this License+along with the Program.++You may charge a fee for the physical act of transferring a copy, and+you may at your option offer warranty protection in exchange for a fee.++ 2. You may modify your copy or copies of the Program or any portion+of it, thus forming a work based on the Program, and copy and+distribute such modifications or work under the terms of Section 1+above, provided that you also meet all of these conditions:++ a) You must cause the modified files to carry prominent notices+ stating that you changed the files and the date of any change.++ b) You must cause any work that you distribute or publish, that in+ whole or in part contains or is derived from the Program or any+ part thereof, to be licensed as a whole at no charge to all third+ parties under the terms of this License.++ c) If the modified program normally reads commands interactively+ when run, you must cause it, when started running for such+ interactive use in the most ordinary way, to print or display an+ announcement including an appropriate copyright notice and a+ notice that there is no warranty (or else, saying that you provide+ a warranty) and that users may redistribute the program under+ these conditions, and telling the user how to view a copy of this+ License. (Exception: if the Program itself is interactive but+ does not normally print such an announcement, your work based on+ the Program is not required to print an announcement.)++These requirements apply to the modified work as a whole. If+identifiable sections of that work are not derived from the Program,+and can be reasonably considered independent and separate works in+themselves, then this License, and its terms, do not apply to those+sections when you distribute them as separate works. But when you+distribute the same sections as part of a whole which is a work based+on the Program, the distribution of the whole must be on the terms of+this License, whose permissions for other licensees extend to the+entire whole, and thus to each and every part regardless of who wrote it.++Thus, it is not the intent of this section to claim rights or contest+your rights to work written entirely by you; rather, the intent is to+exercise the right to control the distribution of derivative or+collective works based on the Program.++In addition, mere aggregation of another work not based on the Program+with the Program (or with a work based on the Program) on a volume of+a storage or distribution medium does not bring the other work under+the scope of this License.++ 3. You may copy and distribute the Program (or a work based on it,+under Section 2) in object code or executable form under the terms of+Sections 1 and 2 above provided that you also do one of the following:++ a) Accompany it with the complete corresponding machine-readable+ source code, which must be distributed under the terms of Sections+ 1 and 2 above on a medium customarily used for software interchange; or,++ b) Accompany it with a written offer, valid for at least three+ years, to give any third party, for a charge no more than your+ cost of physically performing source distribution, a complete+ machine-readable copy of the corresponding source code, to be+ distributed under the terms of Sections 1 and 2 above on a medium+ customarily used for software interchange; or,++ c) Accompany it with the information you received as to the offer+ to distribute corresponding source code. (This alternative is+ allowed only for noncommercial distribution and only if you+ received the program in object code or executable form with such+ an offer, in accord with Subsection b above.)++The source code for a work means the preferred form of the work for+making modifications to it. For an executable work, complete source+code means all the source code for all modules it contains, plus any+associated interface definition files, plus the scripts used to+control compilation and installation of the executable. However, as a+special exception, the source code distributed need not include+anything that is normally distributed (in either source or binary+form) with the major components (compiler, kernel, and so on) of the+operating system on which the executable runs, unless that component+itself accompanies the executable.++If distribution of executable or object code is made by offering+access to copy from a designated place, then offering equivalent+access to copy the source code from the same place counts as+distribution of the source code, even though third parties are not+compelled to copy the source along with the object code.++ 4. You may not copy, modify, sublicense, or distribute the Program+except as expressly provided under this License. Any attempt+otherwise to copy, modify, sublicense or distribute the Program is+void, and will automatically terminate your rights under this License.+However, parties who have received copies, or rights, from you under+this License will not have their licenses terminated so long as such+parties remain in full compliance.++ 5. You are not required to accept this License, since you have not+signed it. However, nothing else grants you permission to modify or+distribute the Program or its derivative works. These actions are+prohibited by law if you do not accept this License. Therefore, by+modifying or distributing the Program (or any work based on the+Program), you indicate your acceptance of this License to do so, and+all its terms and conditions for copying, distributing or modifying+the Program or works based on it.++ 6. Each time you redistribute the Program (or any work based on the+Program), the recipient automatically receives a license from the+original licensor to copy, distribute or modify the Program subject to+these terms and conditions. You may not impose any further+restrictions on the recipients' exercise of the rights granted herein.+You are not responsible for enforcing compliance by third parties to+this License.++ 7. If, as a consequence of a court judgment or allegation of patent+infringement or for any other reason (not limited to patent issues),+conditions are imposed on you (whether by court order, agreement or+otherwise) that contradict the conditions of this License, they do not+excuse you from the conditions of this License. If you cannot+distribute so as to satisfy simultaneously your obligations under this+License and any other pertinent obligations, then as a consequence you+may not distribute the Program at all. For example, if a patent+license would not permit royalty-free redistribution of the Program by+all those who receive copies directly or indirectly through you, then+the only way you could satisfy both it and this License would be to+refrain entirely from distribution of the Program.++If any portion of this section is held invalid or unenforceable under+any particular circumstance, the balance of the section is intended to+apply and the section as a whole is intended to apply in other+circumstances.++It is not the purpose of this section to induce you to infringe any+patents or other property right claims or to contest validity of any+such claims; this section has the sole purpose of protecting the+integrity of the free software distribution system, which is+implemented by public license practices. Many people have made+generous contributions to the wide range of software distributed+through that system in reliance on consistent application of that+system; it is up to the author/donor to decide if he or she is willing+to distribute software through any other system and a licensee cannot+impose that choice.++This section is intended to make thoroughly clear what is believed to+be a consequence of the rest of this License.++ 8. If the distribution and/or use of the Program is restricted in+certain countries either by patents or by copyrighted interfaces, the+original copyright holder who places the Program under this License+may add an explicit geographical distribution limitation excluding+those countries, so that distribution is permitted only in or among+countries not thus excluded. In such case, this License incorporates+the limitation as if written in the body of this License.++ 9. The Free Software Foundation may publish revised and/or new versions+of the General Public License from time to time. Such new versions will+be similar in spirit to the present version, but may differ in detail to+address new problems or concerns.++Each version is given a distinguishing version number. If the Program+specifies a version number of this License which applies to it and "any+later version", you have the option of following the terms and conditions+either of that version or of any later version published by the Free+Software Foundation. If the Program does not specify a version number of+this License, you may choose any version ever published by the Free Software+Foundation.++ 10. If you wish to incorporate parts of the Program into other free+programs whose distribution conditions are different, write to the author+to ask for permission. For software which is copyrighted by the Free+Software Foundation, write to the Free Software Foundation; we sometimes+make exceptions for this. Our decision will be guided by the two goals+of preserving the free status of all derivatives of our free software and+of promoting the sharing and reuse of software generally.++ NO WARRANTY++ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,+REPAIR OR CORRECTION.++ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE+POSSIBILITY OF SUCH DAMAGES.++ END OF TERMS AND CONDITIONS++ How to Apply These Terms to Your New Programs++ If you develop a new program, and you want it to be of the greatest+possible use to the public, the best way to achieve this is to make it+free software which everyone can redistribute and change under these terms.++ To do so, attach the following notices to the program. It is safest+to attach them to the start of each source file to most effectively+convey the exclusion of warranty; and each file should have at least+the "copyright" line and a pointer to where the full notice is found.++ <one line to give the program's name and a brief idea of what it does.>+ Copyright (C) <year> <name of author>++ This program is free software; you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation; either version 2 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License along+ with this program; if not, write to the Free Software Foundation, Inc.,+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.++Also add information on how to contact you by electronic and paper mail.++If the program is interactive, make it output a short notice like this+when it starts in an interactive mode:++ Gnomovision version 69, Copyright (C) year name of author+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.+ This is free software, and you are welcome to redistribute it+ under certain conditions; type `show c' for details.++The hypothetical commands `show w' and `show c' should show the appropriate+parts of the General Public License. Of course, the commands you use may+be called something other than `show w' and `show c'; they could even be+mouse-clicks or menu items--whatever suits your program.++You should also get your employer (if you work as a programmer) or your+school, if any, to sign a "copyright disclaimer" for the program, if+necessary. Here is a sample; alter the names:++ Yoyodyne, Inc., hereby disclaims all copyright interest in the program+ `Gnomovision' (which makes passes at compilers) written by James Hacker.++ <signature of Ty Coon>, 1 April 1989+ Ty Coon, President of Vice++This General Public License does not permit incorporating your program into+proprietary programs. If your program is a subroutine library, you may+consider it more useful to permit linking proprietary applications with the+library. If this is what you want to do, use the GNU Lesser General+Public License instead of this License.
+ README.md view
@@ -0,0 +1,84 @@+# plot-gtk-ui++An ambitious attempt to provide mathematica like dynamic plotting for free.++## Installation++### Linux++The installation for gtk requires that+[gtk2hs-buildtools](https://hackage.haskell.org/package/gtk2hs-buildtools)+be installed, and the binary be in your $PATH. The below commands take+care of that, without permanently changing the $PATH.++```bash+$ cabal update+$ cabal install gtk2hs-buildtools+$ env PATH="~/.cabal/bin:$PATH" cabal install plot-gtk-ui+```++### Windows++* Install gtk2hs with instructions from+ [here](http://projects.haskell.org/gtk2hs/download/#Windows).++* Then use cabal to install `plot-gtk-ui`.++```batch+cmd> cabal install plot-gtk-ui+```++## Usage++Import `Graphics.Rendering.Plot.Gtk.UI` and follow the examples below. ++```haskell+import Graphics.UI.Gtk+import Graphics.Rendering.Plot.Gtk.UI++main = do+ initGUI+ plotStatic sin (-pi, pi)+ -- Plot sin(x) from -pi to pi+ mainGUI+```++++Another simple example.++```haskell+import Graphics.UI.Gtk+import Graphics.Rendering.Plot.Gtk.UI++main = do+ initGUI+ plotDynamic (\(x, a) -> sin(a * x)) ((-pi, pi), (0, 1))+ -- Plot sin(a * x), where 'x' ranges from -pi to+ -- pi and 'a' ranges between 0 to 1+ mainGUI+```++++## Errors++The error messages might not be completely correct, as there is not a good way to diagnose what caused the error.++Issues that cause errors:++- Invalid ranges, e.g. X-Range = `(1, -1)`+- Automatic-determination of ranges resulting in unplottable ranges, e.g. `(0, 0)`++If you land on the "grey screen of on plots" or any other incorrect error message, please raise an issue here.++## Implementation++The `plotStatic` and `plotDynamic` functions are implemented in a+type-safe manner using `fixed-vector`.++It also allows one to write functions using appropriate tuples as in the+above examples.++Plotting is done using the excellent [plot](https://github.com/amcphail/plot)+package.
+ Setup.lhs view
@@ -0,0 +1,3 @@+#! /usr/bin/env runhaskell+> import Distribution.Simple+> main = defaultMain
+ lib/Graphics/Rendering/Plot/Gtk/UI.hs view
@@ -0,0 +1,96 @@+-----------------------------------------------------------------------------+-- |+-- Module : Graphics.Rendering.Plot.Gtk.UI+-- Copyright : (c) Sumit Sahrawat+-- License : GPL-2+--+-- Maintainer : sumit.sahrawat.apm13@iitbhu.ac.in+-- Stability : provisional+-- Portability : portable+--+-- Quick plotting for functions + dynamic plotting of functions+--+-- 'plotStatic' can be used to plot functions of the form /f(x)=.../+--+-- 'plotDynamic' can be used to plot functions of the form /f(x, a, b) =+-- .../ where /a/ and /b/ are additional parameters. Sliders are+-- provided for manipulating the values of /a/ and /b/, and the plot is+-- animated in real-time.+--+--------------------------------------------------------------------------------+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}++module Graphics.Rendering.Plot.Gtk.UI+ ( plotStatic+ , plotDynamic+ ) where++--------------------------------------------------------------------------------+-- Standard Libraries++import Data.IORef (newIORef)++--------------------------------------------------------------------------------+-- Other Libraries++import Data.Vector.Fixed (Dim, S, Vector, Z)+import qualified Data.Vector.Fixed as V+import Data.Vector.Fixed.Unboxed (Vec)++--------------------------------------------------------------------------------+-- Custom Modules++import Paths_plot_gtk_ui (getDataFileName)+import Graphics.Rendering.Plot.Gtk.UI.PlotWindow+import Graphics.Rendering.Plot.Gtk.UI.Settings++--------------------------------------------------------------------------------++defSet :: FigureSettings+defSet = defaultFigureSettings++--------------------------------------------------------------------------------++-- | Quickly create AdjustmentSettings+argRange :: (Double, Double) -- ^ Lower and upper range+ -> AdjustmentSettings+argRange (x, y) = aSetRange ((x + y) / 2) x y++--------------------------------------------------------------------------------++-- | Plots functions of multiple arguments, provided all arguments+-- have specified ranges.+plot :: ([Double] -> Double -> Double) -- ^ Function+ -> [(Double, Double)] -- ^ Ranges for all but the primary arg+ -> FigureSettings -- ^ Should contain the primary range+ -> IO ()+plot func rangeTail fset = do+ iofset <- newIORef fset+ plotGlade <- getDataFileName "plot-window.glade"+ plotWindow plotGlade iofset (map argRange rangeTail) func++--------------------------------------------------------------------------------++-- | Plot functions that don't depend on extra parameters+plotStatic :: (Double -> Double) -- ^ Function to plot+ -> (Double, Double) -- ^ Range for abscissa (horizontal axis)+ -> IO ()+plotStatic func r =+ let f :: Vec (S Z) Double -> Double+ f = func . V.head+ in plotDynamic f ((V.convert . V.Only $ r) :: Vec (S Z) (Double, Double))++--------------------------------------------------------------------------------++-- | Plot functions that depend on extra parameters+plotDynamic :: (Vector u a, Vector v (a, a),+ a ~ Double, Dim u ~ Dim v, Dim v ~ S n, V.Arity n)+ => (u Double -> Double)+ -> v (Double, Double)+ -> IO ()+plotDynamic func ranges = let (x:xs) = V.toList ranges+ f zs z = func . V.fromList $ (z:zs)+ in plot f xs defSet { xRange = Just x }++--------------------------------------------------------------------------------
+ lib/Graphics/Rendering/Plot/Gtk/UI/Events.hs view
@@ -0,0 +1,224 @@+-----------------------------------------------------------------------------+-- |+-- Module : Graphics.Rendering.Plot.Gtk.UI.Events+-- Copyright : (c) Sumit Sahrawat+-- License : GPL-2+--+-- Maintainer : sumit.sahrawat.apm13@iitbhu.ac.in+-- Stability : provisional+-- Portability : portable+--+-- Attach event handlers for the plot window.+--+--------------------------------------------------------------------------------++module Graphics.Rendering.Plot.Gtk.UI.Events (attachHandlers) where++--------------------------------------------------------------------------------+-- Standard Libraries++import Data.Char (toLower)+import Data.IORef (IORef, modifyIORef,+ readIORef)+import Data.Text (unpack)++--------------------------------------------------------------------------------+-- Other Libraries++import Graphics.Rendering.Plot+import Graphics.UI.Gtk hiding (Circle, Color,+ Cross)++--------------------------------------------------------------------------------+-- Custom Modules++import Graphics.Rendering.Plot.Gtk.UI.Figure (figurePlot)+import Graphics.Rendering.Plot.Gtk.UI.Settings++--------------------------------------------------------------------------------++attachHandlers :: (ComboBoxClass cbox) =>+ Builder+ -> IORef FigureSettings+ -> [Adjustment]+ -> (DrawingArea -> IORef FigureSettings+ -> [Adjustment]+ -> IO ())+ -> ([Double] -> Double -> Double)+ -> cbox+ -> IO ()+attachHandlers builder iofset adjs updateCanvas g combo = do+ -- Canvas+ canvas <- builderGetObject builder castToDrawingArea "Plotting Canvas"+ let redraw = updateCanvas canvas iofset adjs+ _ <- onExpose canvas $ \_ -> redraw >> return False++ -- Adjustments for parameter sliders+ mapM_ (`onValueChanged` redraw) adjs++ -- Plot-Title Entry+ titleEntry <- builderGetObject builder castToEntry "Plot Title Entry"+ _ <- onEntryActivate titleEntry $ do+ titleNew <- entryGetText titleEntry+ modifyIORef iofset $ \f -> f { plotTitle = Just titleNew }+ redraw++ -- Plot-Title Font Size+ titleSize <- builderGetObject builder castToAdjustment "Title Font Size"+ _ <- onValueChanged titleSize $ do+ size <- adjustmentGetValue titleSize+ modifyIORef iofset $ \f -> f { plotTitleSize = size }+ redraw++ -- Subtitle Entry+ subEntry <- builderGetObject builder castToEntry "Subtitle Entry"+ _ <- onEntryActivate subEntry $ do+ titleNew <- entryGetText subEntry+ modifyIORef iofset $ \f -> f { subTitle = Just titleNew }+ redraw++ -- Subtitle Font Size+ subSize <- builderGetObject builder castToAdjustment "Subtitle Font Size"+ _ <- onValueChanged subSize $ do+ size <- adjustmentGetValue subSize+ modifyIORef iofset $ \f -> f { subTitleSize = size }+ redraw++ -- Show X-Axis CheckButton+ showX <- builderGetObject builder castToCheckButton "X-Axis Check"+ _ <- onToggled showX $ do+ state <- toggleButtonGetActive showX+ modifyIORef iofset+ (\f -> f { showXAxis = state })+ redraw++ -- Show Y-Axis CheckButton+ showY <- builderGetObject builder castToCheckButton "Y-Axis Check"+ _ <- onToggled showY $ do+ state <- toggleButtonGetActive showY+ modifyIORef iofset+ (\f -> f { showYAxis = state })+ redraw++ let adjustmentPairValues (a1, a2) = do v1 <- adjustmentGetValue a1+ v2 <- adjustmentGetValue a2+ return (v1, v2)+ -- X-Axis Range+ xLower <- builderGetObject builder castToAdjustment "X-Lower"+ xUpper <- builderGetObject builder castToAdjustment "X-Upper"+ let updateX x = onValueChanged x $ do+ (xl, xu) <- adjustmentPairValues (xLower, xUpper)+ modifyIORef iofset $ \f -> f { xRange = Just (xl, xu) }+ redraw+ in mapM_ updateX [xLower, xUpper]++ -- X-Range Auto-determination CheckBox+ xRangeEntries <- builderGetObject builder castToHBox "X-Range Entries"+ autoXCheck <- builderGetObject builder castToCheckButton "X-Range Check"+ _ <- onToggled autoXCheck $ do+ state <- toggleButtonGetActive autoXCheck+ if state+ then error "Invalid xRange"+ -- do modifyIORef iofset $ \f -> f { xRange = Nothing }+ -- widgetHideAll xRangeEntries+ else do lower <- builderGetObject builder castToAdjustment "X-Lower"+ upper <- builderGetObject builder castToAdjustment "X-Upper"+ (l, u) <- adjustmentPairValues (lower, upper)+ widgetShowAll xRangeEntries+ modifyIORef iofset $ \f -> f { xRange = Just (l, u) }+ redraw++ -- Y-Axis Range+ yLower <- builderGetObject builder castToAdjustment "Y-Lower"+ yUpper <- builderGetObject builder castToAdjustment "Y-Upper"+ let updateY y = onValueChanged y $ do+ (yl, yu) <- adjustmentPairValues (yLower, yUpper)+ modifyIORef iofset $ \f -> f { yRange = Just (yl, yu) }+ redraw+ in mapM_ updateY [yLower, yUpper]++ -- Y-Range Auto-determination CheckBox+ yRangeEntries <- builderGetObject builder castToHBox "Y-Range Entries"+ autoYCheck <- builderGetObject builder castToCheckButton "Y-Range Check"+ _ <- onToggled autoYCheck $ do+ state <- toggleButtonGetActive autoYCheck+ if state+ then do modifyIORef iofset $ \f -> f { yRange = Nothing }+ widgetHideAll yRangeEntries+ else do lower <- builderGetObject builder castToAdjustment "Y-Lower"+ upper <- builderGetObject builder castToAdjustment "Y-Upper"+ (l, u) <- adjustmentPairValues (lower, upper)+ widgetShowAll yRangeEntries+ modifyIORef iofset $ \f -> f { yRange = Just (l, u) }+ redraw++ -- Sampling Rate+ sampleRate <- builderGetObject builder castToAdjustment "Sampling Adj"+ _ <- onValueChanged sampleRate $ do+ rate <- adjustmentGetValue sampleRate+ modifyIORef iofset $ \f -> f { samplingRate = rate }+ redraw++ -- X-Label Entry+ xLabelEntry <- builderGetObject builder castToEntry "X-Label Entry"+ _ <- onEntryActivate xLabelEntry $ do+ label' <- entryGetText xLabelEntry+ modifyIORef iofset $ \f -> f { xLabel = Just label' }+ redraw++ -- X-Label Size+ xlSize <- builderGetObject builder castToAdjustment "X-Label Size"+ _ <- onValueChanged xlSize $ do+ size <- adjustmentGetValue xlSize+ modifyIORef iofset $ \f -> f { xLabelSize = size }+ redraw++ -- Y-Label Entry+ yLabelEntry <- builderGetObject builder castToEntry "Y-Label Entry"+ _ <- onEntryActivate yLabelEntry $ do+ label' <- entryGetText yLabelEntry+ modifyIORef iofset $ \f -> f { yLabel = Just label' }+ redraw++ ylSize <- builderGetObject builder castToAdjustment "Y-Label Size"+ _ <- onValueChanged ylSize $ do+ size <- adjustmentGetValue ylSize+ modifyIORef iofset $ \f -> f { yLabelSize = size }+ redraw++ -- Export Filename extry++ -- Export Button+ export <- builderGetObject builder castToButton "Export Button"+ _ <- onClicked export $ do+ fset <- readIORef iofset+ comboText <- comboBoxGetActiveText combo+ nameEntry <- builderGetObject builder castToEntry "File Entry"+ nameText <- entryGetText nameEntry++ modifyIORef iofset $ \f -> f { fileName = nameText }++ figure <- figurePlot g iofset adjs++ let dimensions = exportSize fset++ parseType txt = case txt of+ "PNG" -> PNG+ "SVG" -> SVG+ "PS" -> PS+ "PDF" -> PDF+ _ -> error "Invalid FileType!"++ filetype = parseType $ maybe (error "Invalid Type!") unpack comboText++ parseExt fileType = case fileType of+ PNG -> "PNG"+ SVG -> "SVG"+ PS -> "PS"+ PDF -> "PDF"++ filename = nameText ++ "." ++ map toLower (parseExt filetype)+ writeFigure filetype filename dimensions figure+ return ()++--------------------------------------------------------------------------------
+ lib/Graphics/Rendering/Plot/Gtk/UI/Figure.hs view
@@ -0,0 +1,119 @@+-----------------------------------------------------------------------------+-- |+-- Module : Graphics.Rendering.Plot.Gtk.UI+-- Copyright : (c) Sumit Sahrawat+-- License : GPL-2+--+-- Maintainer : sumit.sahrawat.apm13@iitbhu.ac.in+-- Stability : provisional+-- Portability : portable+--+-- Functions to build and make changes to 'Figure's+--+--------------------------------------------------------------------------------++module Graphics.Rendering.Plot.Gtk.UI.Figure where++--------------------------------------------------------------------------------+-- Standard Libraries++import Control.Monad (unless, when)+import Data.IORef (IORef, readIORef)+import Data.Maybe (fromJust, isJust)++--------------------------------------------------------------------------------+-- Other Libraries++-- import Data.Colour.SRGB (sRGB24read)+import Graphics.Rendering.Plot+import Graphics.UI.Gtk (Adjustment,+ adjustmentGetValue)+import Numeric.LinearAlgebra (linspace, mapVector)++--------------------------------------------------------------------------------+-- Custom Modules++import Graphics.Rendering.Plot.Gtk.UI.Settings++--------------------------------------------------------------------------------++updateFigureText :: (Text () -> Figure ())+ -> Maybe String -> Double -> Figure ()+updateFigureText withSomething txt size =+ case txt of+ Nothing -> return ()+ Just str -> withSomething . unless (null str) $ do+ setText str+ setFontSize size++--------------------------------------------------------------------------------++updateAxis :: AxisType -> Bool -> (AxisPosn, AxisSide) -> Maybe (Double, Double)+ -> Scale -> Maybe String -> FontSize -> Plot ()+updateAxis axis state location range scale label size = do+ let (position, side) = location++ when state $ addAxis axis position+ (when (isJust label) $ withAxisLabel $ do setText . fromJust $ label+ setFontSize size)++ maybe (setRangeFromData axis side scale)+ (uncurry (setRange axis side scale))+ range++--------------------------------------------------------------------------------++figurePlot :: ([Double] -> Double -> Double)+ -> IORef FigureSettings+ -> [Adjustment]+ -> IO (Figure ())+figurePlot g iofset adjs = do+ fset <- readIORef iofset+ vars <- mapM adjustmentGetValue adjs++ let rate = samplingRate fset+ range = fromJust $ xRange fset+ samples = round $ (\(x, y) -> rate * (y - x)) range+ domain = linspace samples range+ func = g vars+ stype = plotType fset+ dset = [(stype, domain, mapVector func domain)]+ return $ buildFigure dset fset++--------------------------------------------------------------------------------++buildFigure :: Dataset a => a -> FigureSettings -> Figure ()+buildFigure dset fset = do+ withTextDefaults $ setFontFamily (fontFamily fset)++ let str = plotTitle fset+ size = plotTitleSize fset+ in updateFigureText withTitle str size++ let str = subTitle fset+ size = subTitleSize fset+ in updateFigureText withSubTitle str size++ setPlots 1 1+ withPlot (1, 1) $ do+ setDataset dset++ -- X-Axis+ let state = showXAxis fset+ label = xLabel fset+ size = xLabelSize fset+ loc = xLocation fset+ range = xRange fset+ scale = plotScaleX fset+ in updateAxis XAxis state loc range scale label size++ -- Y-Axis+ let state = showYAxis fset+ label = yLabel fset+ size = yLabelSize fset+ loc = yLocation fset+ range = yRange fset+ scale = plotScaleY fset+ in updateAxis YAxis state loc range scale label size++--------------------------------------------------------------------------------
+ lib/Graphics/Rendering/Plot/Gtk/UI/PlotWindow.hs view
@@ -0,0 +1,168 @@+-----------------------------------------------------------------------------+-- |+-- Module : Graphics.Rendering.Plot.Gtk.UI+-- Copyright : (c) Sumit Sahrawat+-- License : GPL-2+--+-- Maintainer : sumit.sahrawat.apm13@iitbhu.ac.in+-- Stability : provisional+-- Portability : portable+--+-- Functions related to the plot window+--+--------------------------------------------------------------------------------++module Graphics.Rendering.Plot.Gtk.UI.PlotWindow where++--------------------------------------------------------------------------------+-- Standard Libraries++import Control.Exception (SomeException (..),+ catch)+import Control.Monad (when)+import Data.IORef (IORef, readIORef)+import Data.Maybe (fromJust, isJust,+ isNothing)+import Data.Text (pack)++--------------------------------------------------------------------------------+-- Other Libraries++import Graphics.Rendering.Plot+import Graphics.UI.Gtk++--------------------------------------------------------------------------------+-- Custom Modules++import Graphics.Rendering.Plot.Gtk.UI.ErrorFig (errorFig)+import Graphics.Rendering.Plot.Gtk.UI.Events (attachHandlers)+import Graphics.Rendering.Plot.Gtk.UI.Figure (figurePlot)+import Graphics.Rendering.Plot.Gtk.UI.Settings++--------------------------------------------------------------------------------++updateCanvas :: ([Double] -> Double -> Double)+ -> DrawingArea+ -> IORef FigureSettings+ -> [Adjustment]+ -> IO ()+updateCanvas g canvas iofset adjs = do+ s@(w, h) <- widgetGetSize canvas+ drw <- widgetGetDrawWindow canvas+ fig <- figurePlot g iofset adjs++ box <- regionRectangle $ Rectangle 0 0 w h+ drawWindowBeginPaintRegion drw box+ catch (renderWithDrawable drw (render fig s))+ (\(SomeException _) -> do+ fset <- readIORef iofset+ renderWithDrawable drw (errorFig fset s))+ drawWindowEndPaint drw++--------------------------------------------------------------------------------++addSlidersToBox :: [AdjustmentSettings] -> VBox -> IO [Adjustment]+addSlidersToBox conf box = do+ let newAdj (ASet a b c d e f) = adjustmentNew a b c d e f+ newAdjList = mapM newAdj+ newHScaleList = mapM hScaleNew++ adjs <- newAdjList conf+ sliders <- newHScaleList adjs+ mapM_ (`scaleSetValuePos` PosRight) sliders+ mapM_ (`scaleSetDigits` 2) sliders+ mapM_ (\x -> boxPackEnd box x PackNatural 0) sliders+ return adjs++--------------------------------------------------------------------------------++plotWindow :: String -> IORef FigureSettings -> [AdjustmentSettings]+ -> ([Double] -> Double -> Double) -> IO ()+plotWindow plotGlade iofset conf fun = do+ builder <- builderNew+ builderAddFromFile builder plotGlade++ plotBox <- builderGetObject builder castToVBox "Plot Region"+ adjs <- addSlidersToBox conf plotBox+ fset <- readIORef iofset++ let updateEntryText entryGlade txt =+ when (isJust txt) $ do entry <- builderGetObject builder+ castToEntry entryGlade+ entrySetText entry $ fromJust txt+ in do updateEntryText "Plot Title Entry" $ plotTitle fset+ updateEntryText "Subtitle Entry" $ subTitle fset+ updateEntryText "X-Label Entry" $ xLabel fset+ updateEntryText "Y-Label Entry" $ yLabel fset+ updateEntryText "File Entry" $ Just (fileName fset)++ let adjApplyValue adjGlade val = do+ adj <- builderGetObject builder castToAdjustment adjGlade+ adjustmentSetValue adj val+ in do adjApplyValue "Title Font Size" $ plotTitleSize fset+ adjApplyValue "Subtitle Font Size" $ subTitleSize fset+ adjApplyValue "Sampling Adj" $ samplingRate fset+ adjApplyValue "X-Label Size" $ xLabelSize fset+ adjApplyValue "Y-Label Size" $ yLabelSize fset++ let checkApplyState checkGlade state = do+ check <- builderGetObject builder castToCheckButton checkGlade+ toggleButtonSetActive check state+ in do checkApplyState "X-Axis Check" $ showXAxis fset+ checkApplyState "Y-Axis Check" $ showYAxis fset+ checkApplyState "Y-Range Check" $ isNothing . yRange $ fset++ when (isJust . yRange $ fset) $ do+ let (l, u) = fromJust . yRange $ fset+ lower <- builderGetObject builder castToAdjustment "Y-Lower"+ upper <- builderGetObject builder castToAdjustment "Y-Upper"+ adjustmentSetValue lower l+ adjustmentSetValue upper u++ -- X-Axis Range+ let range = xRange fset+ in if isNothing range+ then error "Invalid xRange"+ else let (l, u) = fromJust range+ in do lower <- builderGetObject builder castToAdjustment "X-Lower"+ upper <- builderGetObject builder castToAdjustment "X-Upper"+ adjustmentSetValue lower l+ adjustmentSetValue upper u++ -- Export Size+ let (w, h) = exportSize fset+ in do widthAdj <- builderGetObject builder castToAdjustment "Export Width"+ heightAdj <- builderGetObject builder castToAdjustment "Export Height"+ adjustmentSetValue widthAdj $ fromIntegral w+ adjustmentSetValue heightAdj $ fromIntegral h++ -- Export ComboBox+ combo <- comboBoxNewText+ _ <- mapM (\(x, y) -> comboBoxInsertText combo x $ pack y)+ [(1, "PNG"), (2, "SVG"), (3, "PS"), (4, "PDF")]+ comboBoxSetActive combo 0++ comboArea <- builderGetObject builder castToHBox "File Name"+ boxPackEndDefaults comboArea combo++ -- Attach Event Handlers+ _ <- attachHandlers builder iofset adjs (updateCanvas fun) fun combo++ -- Show Widgets+ window <- builderGetObject builder castToWindow "Plot Window"+ widgetShowAll window++ -- Hide the useless checkButton for now+ check <- builderGetObject builder castToCheckButton "X-Range Check"+ widgetHide check++ -- Hide the entries if not required+ let range = yRange fset+ in when (isNothing range) $ do+ entries <- builderGetObject builder castToHBox "Y-Range Entries"+ widgetHideAll entries++ _ <- onDestroy window mainQuit+ return ()++--------------------------------------------------------------------------------
+ lib/Graphics/Rendering/Plot/Gtk/UI/Settings.hs view
@@ -0,0 +1,96 @@+-----------------------------------------------------------------------------+-- |+-- Module : Graphics.Rendering.Plot.Gtk.UI+-- Copyright : (c) Sumit Sahrawat+-- License : GPL-2+--+-- Maintainer : sumit.sahrawat.apm13@iitbhu.ac.in+-- Stability : provisional+-- Portability : portable+--+-- Helper functions and data for the figures and sliders+--+--------------------------------------------------------------------------------+module Graphics.Rendering.Plot.Gtk.UI.Settings+ ( FigureSettings (..)+ , defaultFigureSettings+ , AdjustmentSettings (..)+ , aSetRange+ ) where++--------------------------------------------------------------------------------+-- Datatypes used by plot package for configuration++import Graphics.Rendering.Plot (AxisPosn (..), AxisSide (..),+ Scale (..), SeriesType (..))++--------------------------------------------------------------------------------++data FigureSettings = FigureSettings {+ fontFamily :: String+ , plotTitle :: Maybe String+ , plotTitleSize :: Double+ , subTitle :: Maybe String+ , subTitleSize :: Double+ , xRange :: Maybe (Double, Double)+ , yRange :: Maybe (Double, Double)+ , samplingRate :: Double+ , xLabel :: Maybe String+ , xLabelSize :: Double+ , yLabel :: Maybe String+ , yLabelSize :: Double+ , showXAxis :: Bool+ , xLocation :: (AxisPosn, AxisSide)+ , showYAxis :: Bool+ , yLocation :: (AxisPosn, AxisSide)+ , plotScaleX :: Scale+ , plotScaleY :: Scale+ , exportSize :: (Int, Int)+ , fileName :: String+ , plotType :: SeriesType+ }++--------------------------------------------------------------------------------++defaultFigureSettings :: FigureSettings+defaultFigureSettings = FigureSettings {+ fontFamily = "Sans"+ , plotTitle = Nothing+ , plotTitleSize = 10+ , subTitle = Nothing+ , subTitleSize = 10+ , xRange = Nothing+ , yRange = Nothing+ , samplingRate = 50+ , xLabel = Nothing+ , xLabelSize = 10+ , yLabel = Nothing+ , yLabelSize = 10+ , showXAxis = True+ , xLocation = (Side Lower, Lower)+ , showYAxis = True+ , yLocation = (Side Lower, Lower)+ , plotScaleX = Linear+ , plotScaleY = Linear+ , exportSize = (1000, 1000)+ , fileName = "plot"+ , plotType = Line+ }++--------------------------------------------------------------------------------++data AdjustmentSettings = ASet {+ initVal :: Double+ , minBound :: Double+ , maxBound :: Double+ , stepIncr :: Double+ , pageIncr :: Double+ , pageSize :: Double+ }++--------------------------------------------------------------------------------++aSetRange :: Double -> Double -> Double -> AdjustmentSettings+aSetRange x y z = ASet x y z 0.01 0.01 0.00++--------------------------------------------------------------------------------
+ plot-gtk-ui.cabal view
@@ -0,0 +1,49 @@+Name: plot-gtk-ui+Version: 0.0.1.0+License: GPL-2+License-file: LICENSE+Author: Sumit Sahrawat+Maintainer: sumit.sahrawat.apm13@iitbhu.ac.in+Stability: experimental+Homepage: https://github.com/sumitsahrawat/plot-gtk-ui+Synopsis: A quick way to use Mathematica like Manipulation abilities+Description:+ A pre-built ui for plotting based on plot. Quicker to use than+ plot-gtk, but less configurable, and less versatile.+ .+ Only provides ability to plot mathematical functions. But, also+ allows dynamic plotting functionality similar to Mathematica.++Category: Math++Tested-with: GHC==7.8.3+Cabal-version: >=1.18+Build-type: Simple++Extra-source-files: README.md+Data-dir: ui+Data-files: plot-window.glade++Library+ Build-depends: base >=4.7 && <4.8+ , colour >=2.3 && <2.4+ , plot >=0.2 && <0.3+ , gtk >=0.13 && <0.14+ , cairo >=0.13 && <0.14+ , hmatrix >=0.16 && <0.17+ , text >=1.2 && <1.3+ , vector >=0.10.12.2 && <0.10.13.0+ , fixed-vector >=0.7.0.0+ hs-source-dirs: lib+ exposed-modules: Graphics.Rendering.Plot.Gtk.UI+ other-modules: Paths_plot_gtk_ui+ , Graphics.Rendering.Plot.Gtk.UI.Events+ , Graphics.Rendering.Plot.Gtk.UI.Figure+ , Graphics.Rendering.Plot.Gtk.UI.Settings+ , Graphics.Rendering.Plot.Gtk.UI.PlotWindow+ ghc-options: -Wall+ default-language: Haskell2010++source-repository head+ type: git+ location: https://github.com/sumitsahrawat/plot-gtk-ui.git
+ ui/plot-window.glade view
@@ -0,0 +1,958 @@+<?xml version="1.0" encoding="UTF-8"?>+<interface>+ <requires lib="gtk+" version="2.24"/>+ <!-- interface-naming-policy project-wide -->+ <object class="GtkAdjustment" id="Export Height">+ <property name="lower">1</property>+ <property name="upper">5120</property>+ <property name="value">1</property>+ <property name="step_increment">50</property>+ <property name="page_increment">200</property>+ </object>+ <object class="GtkAdjustment" id="Export Width">+ <property name="lower">1</property>+ <property name="upper">5120</property>+ <property name="value">1</property>+ <property name="step_increment">50</property>+ <property name="page_increment">200</property>+ </object>+ <object class="GtkAdjustment" id="Sampling Adj">+ <property name="lower">1</property>+ <property name="upper">1000</property>+ <property name="value">1</property>+ <property name="step_increment">0.050000000000000003</property>+ <property name="page_increment">0.5</property>+ </object>+ <object class="GtkAdjustment" id="Subtitle Font Size">+ <property name="lower">1</property>+ <property name="upper">72</property>+ <property name="value">1</property>+ <property name="step_increment">0.5</property>+ <property name="page_increment">1</property>+ </object>+ <object class="GtkAdjustment" id="Title Font Size">+ <property name="lower">1</property>+ <property name="upper">72</property>+ <property name="value">1</property>+ <property name="step_increment">0.5</property>+ <property name="page_increment">1</property>+ </object>+ <object class="GtkAdjustment" id="X-Label Size">+ <property name="lower">1</property>+ <property name="upper">72</property>+ <property name="value">1</property>+ <property name="step_increment">0.5</property>+ <property name="page_increment">1</property>+ </object>+ <object class="GtkAdjustment" id="X-Lower">+ <property name="lower">-1.7976931348623157e+308</property>+ <property name="upper">1.7976931348623157e+308</property>+ <property name="step_increment">0.01</property>+ <property name="page_increment">1</property>+ </object>+ <object class="GtkAdjustment" id="X-Upper">+ <property name="lower">-1.7976931348623157e+308</property>+ <property name="upper">1.7976931348623157e+308</property>+ <property name="step_increment">0.01</property>+ <property name="page_increment">1</property>+ </object>+ <object class="GtkAdjustment" id="Y-Label Size">+ <property name="lower">1</property>+ <property name="upper">72</property>+ <property name="value">1</property>+ <property name="step_increment">0.5</property>+ <property name="page_increment">1</property>+ </object>+ <object class="GtkAdjustment" id="Y-Lower">+ <property name="lower">-1.7976931348623157e+308</property>+ <property name="upper">1.7976931348623157e+308</property>+ <property name="step_increment">0.01</property>+ <property name="page_increment">1</property>+ </object>+ <object class="GtkAdjustment" id="Y-Upper">+ <property name="lower">-1.7976931348623157e+308</property>+ <property name="upper">1.7976931348623157e+308</property>+ <property name="step_increment">0.01</property>+ <property name="page_increment">1</property>+ </object>+ <object class="GtkWindow" id="Plot Window">+ <property name="can_focus">False</property>+ <property name="border_width">1</property>+ <property name="title" translatable="yes">Your Plot</property>+ <child>+ <object class="GtkHBox" id="Vertical Division">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <child>+ <object class="GtkVBox" id="Plot Region">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="border_width">5</property>+ <child>+ <object class="GtkAspectFrame" id="Canvas Aspect">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="label_xalign">0</property>+ <property name="shadow_type">in</property>+ <child>+ <object class="GtkDrawingArea" id="Plotting Canvas">+ <property name="width_request">500</property>+ <property name="height_request">500</property>+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="events">GDK_EXPOSURE_MASK | GDK_STRUCTURE_MASK</property>+ </object>+ </child>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="pack_type">end</property>+ <property name="position">0</property>+ </packing>+ </child>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">0</property>+ </packing>+ </child>+ <child>+ <object class="GtkVSeparator" id="Separator">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ </object>+ <packing>+ <property name="expand">False</property>+ <property name="fill">True</property>+ <property name="position">1</property>+ </packing>+ </child>+ <child>+ <object class="GtkScrolledWindow" id="Settings Palette Scroll">+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="hscrollbar_policy">never</property>+ <property name="vscrollbar_policy">automatic</property>+ <child>+ <object class="GtkViewport" id="Settings Palette Viewport">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="shadow_type">none</property>+ <child>+ <object class="GtkVBox" id="Settings Palette">+ <property name="width_request">250</property>+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="border_width">10</property>+ <child>+ <object class="GtkFrame" id="Plot Title">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="border_width">3</property>+ <property name="label_xalign">0</property>+ <property name="shadow_type">in</property>+ <child>+ <object class="GtkHBox" id="Plot Title Input Widget">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="border_width">3</property>+ <property name="spacing">2</property>+ <child>+ <object class="GtkEntry" id="Plot Title Entry">+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="invisible_char">●</property>+ <property name="invisible_char_set">True</property>+ <property name="primary_icon_activatable">False</property>+ <property name="secondary_icon_activatable">False</property>+ <property name="primary_icon_sensitive">True</property>+ <property name="secondary_icon_sensitive">True</property>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">0</property>+ </packing>+ </child>+ <child>+ <object class="GtkSpinButton" id="Plot Title Size">+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="invisible_char">●</property>+ <property name="progress_pulse_step">0.5</property>+ <property name="primary_icon_activatable">False</property>+ <property name="secondary_icon_activatable">False</property>+ <property name="primary_icon_sensitive">True</property>+ <property name="secondary_icon_sensitive">True</property>+ <property name="adjustment">Title Font Size</property>+ <property name="digits">1</property>+ <property name="snap_to_ticks">True</property>+ <property name="numeric">True</property>+ <property name="update_policy">if-valid</property>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">1</property>+ </packing>+ </child>+ </object>+ </child>+ <child type="label">+ <object class="GtkLabel" id="Plot Title Frame Label">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="label" translatable="yes"><b>Plot Title</b></property>+ <property name="use_markup">True</property>+ </object>+ </child>+ </object>+ <packing>+ <property name="expand">False</property>+ <property name="fill">True</property>+ <property name="position">0</property>+ </packing>+ </child>+ <child>+ <object class="GtkFrame" id="Subtitle">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="border_width">3</property>+ <property name="label_xalign">0</property>+ <property name="shadow_type">in</property>+ <child>+ <object class="GtkHBox" id="Subtitle Input Widget">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="border_width">3</property>+ <property name="spacing">2</property>+ <child>+ <object class="GtkEntry" id="Subtitle Entry">+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="invisible_char">●</property>+ <property name="truncate_multiline">True</property>+ <property name="invisible_char_set">True</property>+ <property name="primary_icon_activatable">False</property>+ <property name="secondary_icon_activatable">False</property>+ <property name="primary_icon_sensitive">True</property>+ <property name="secondary_icon_sensitive">True</property>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">0</property>+ </packing>+ </child>+ <child>+ <object class="GtkSpinButton" id="Subtitle Size">+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="invisible_char">●</property>+ <property name="progress_pulse_step">0.5</property>+ <property name="primary_icon_activatable">False</property>+ <property name="secondary_icon_activatable">False</property>+ <property name="primary_icon_sensitive">True</property>+ <property name="secondary_icon_sensitive">True</property>+ <property name="adjustment">Subtitle Font Size</property>+ <property name="digits">1</property>+ <property name="snap_to_ticks">True</property>+ <property name="numeric">True</property>+ <property name="update_policy">if-valid</property>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">1</property>+ </packing>+ </child>+ </object>+ </child>+ <child type="label">+ <object class="GtkLabel" id="Sub-Title Frame Label">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="label" translatable="yes"><b>Subtitle</b></property>+ <property name="use_markup">True</property>+ </object>+ </child>+ </object>+ <packing>+ <property name="expand">False</property>+ <property name="fill">True</property>+ <property name="position">1</property>+ </packing>+ </child>+ <child>+ <object class="GtkFrame" id="Axes">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="border_width">3</property>+ <property name="label_xalign">0</property>+ <property name="shadow_type">in</property>+ <child>+ <object class="GtkHBox" id="Axes Box">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="border_width">3</property>+ <child>+ <object class="GtkCheckButton" id="X-Axis Check">+ <property name="label" translatable="yes">Show X-Axis</property>+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="receives_default">False</property>+ <property name="active">True</property>+ <property name="draw_indicator">True</property>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">0</property>+ </packing>+ </child>+ <child>+ <object class="GtkCheckButton" id="Y-Axis Check">+ <property name="label" translatable="yes">Show Y-Axis</property>+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="receives_default">False</property>+ <property name="active">True</property>+ <property name="draw_indicator">True</property>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">1</property>+ </packing>+ </child>+ </object>+ </child>+ <child type="label">+ <object class="GtkLabel" id="Axes Frame Label">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="label" translatable="yes"><b>Axes</b></property>+ <property name="use_markup">True</property>+ </object>+ </child>+ </object>+ <packing>+ <property name="expand">False</property>+ <property name="fill">True</property>+ <property name="position">2</property>+ </packing>+ </child>+ <child>+ <object class="GtkFrame" id="X-Range">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="border_width">3</property>+ <property name="label_xalign">0</property>+ <property name="shadow_type">in</property>+ <child>+ <object class="GtkVBox" id="X-Range Box">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="border_width">3</property>+ <child>+ <object class="GtkCheckButton" id="X-Range Check">+ <property name="label" translatable="yes">Auto-Determine X-Range</property>+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="receives_default">False</property>+ <property name="draw_indicator">True</property>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">0</property>+ </packing>+ </child>+ <child>+ <object class="GtkHBox" id="X-Range Entries">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="spacing">2</property>+ <property name="homogeneous">True</property>+ <child>+ <object class="GtkSpinButton" id="X-Range Lower Entry">+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="invisible_char">●</property>+ <property name="progress_pulse_step">0.25</property>+ <property name="primary_icon_activatable">False</property>+ <property name="secondary_icon_activatable">False</property>+ <property name="primary_icon_sensitive">True</property>+ <property name="secondary_icon_sensitive">True</property>+ <property name="adjustment">X-Lower</property>+ <property name="digits">2</property>+ <property name="numeric">True</property>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">0</property>+ </packing>+ </child>+ <child>+ <object class="GtkSpinButton" id="X-Range Upper Entry">+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="invisible_char">●</property>+ <property name="progress_pulse_step">0.25</property>+ <property name="primary_icon_activatable">False</property>+ <property name="secondary_icon_activatable">False</property>+ <property name="primary_icon_sensitive">True</property>+ <property name="secondary_icon_sensitive">True</property>+ <property name="adjustment">X-Upper</property>+ <property name="digits">2</property>+ <property name="numeric">True</property>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">1</property>+ </packing>+ </child>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">1</property>+ </packing>+ </child>+ </object>+ </child>+ <child type="label">+ <object class="GtkLabel" id="X-Range Frame Label">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="label" translatable="yes"><b>X-Range</b></property>+ <property name="use_markup">True</property>+ </object>+ </child>+ </object>+ <packing>+ <property name="expand">False</property>+ <property name="fill">True</property>+ <property name="position">3</property>+ </packing>+ </child>+ <child>+ <object class="GtkFrame" id="Y-Range">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="border_width">3</property>+ <property name="label_xalign">0</property>+ <property name="shadow_type">in</property>+ <child>+ <object class="GtkVBox" id="Y-Range Box">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="border_width">2</property>+ <child>+ <object class="GtkCheckButton" id="Y-Range Check">+ <property name="label" translatable="yes">Auto-Determine Y-Range</property>+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="receives_default">False</property>+ <property name="draw_indicator">True</property>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">0</property>+ </packing>+ </child>+ <child>+ <object class="GtkHBox" id="Y-Range Entries">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="spacing">2</property>+ <property name="homogeneous">True</property>+ <child>+ <object class="GtkSpinButton" id="Y-Range Lower Entry">+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="invisible_char">●</property>+ <property name="progress_pulse_step">0.25</property>+ <property name="primary_icon_activatable">False</property>+ <property name="secondary_icon_activatable">False</property>+ <property name="primary_icon_sensitive">True</property>+ <property name="secondary_icon_sensitive">True</property>+ <property name="adjustment">Y-Lower</property>+ <property name="digits">2</property>+ <property name="numeric">True</property>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">0</property>+ </packing>+ </child>+ <child>+ <object class="GtkSpinButton" id="Y-Range Upper Entry">+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="invisible_char">●</property>+ <property name="progress_pulse_step">0.25</property>+ <property name="primary_icon_activatable">False</property>+ <property name="secondary_icon_activatable">False</property>+ <property name="primary_icon_sensitive">True</property>+ <property name="secondary_icon_sensitive">True</property>+ <property name="adjustment">Y-Upper</property>+ <property name="digits">2</property>+ <property name="numeric">True</property>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">1</property>+ </packing>+ </child>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">1</property>+ </packing>+ </child>+ </object>+ </child>+ <child type="label">+ <object class="GtkLabel" id="Y-Range Frame Label">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="label" translatable="yes"><b>Y-Range</b></property>+ <property name="use_markup">True</property>+ </object>+ </child>+ </object>+ <packing>+ <property name="expand">False</property>+ <property name="fill">True</property>+ <property name="position">4</property>+ </packing>+ </child>+ <child>+ <object class="GtkFrame" id="Sampling Rate">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="border_width">3</property>+ <property name="label_xalign">0</property>+ <property name="shadow_type">in</property>+ <child>+ <object class="GtkVBox" id="Sampling Rate Box">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="border_width">3</property>+ <child>+ <object class="GtkSpinButton" id="Sampling Rate Entry">+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="invisible_char">●</property>+ <property name="primary_icon_activatable">False</property>+ <property name="secondary_icon_activatable">False</property>+ <property name="primary_icon_sensitive">True</property>+ <property name="secondary_icon_sensitive">True</property>+ <property name="adjustment">Sampling Adj</property>+ <property name="digits">1</property>+ <property name="snap_to_ticks">True</property>+ <property name="numeric">True</property>+ <property name="update_policy">if-valid</property>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">0</property>+ </packing>+ </child>+ </object>+ </child>+ <child type="label">+ <object class="GtkLabel" id="Sampling Frame Label">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="label" translatable="yes"><b>Sampling Rate</b></property>+ <property name="use_markup">True</property>+ </object>+ </child>+ </object>+ <packing>+ <property name="expand">False</property>+ <property name="fill">True</property>+ <property name="position">5</property>+ </packing>+ </child>+ <child>+ <object class="GtkFrame" id="Labels">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="border_width">3</property>+ <property name="label_xalign">0</property>+ <property name="shadow_type">in</property>+ <child>+ <object class="GtkVBox" id="Labels Box">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <child>+ <object class="GtkVBox" id="X-Axis Label Widget">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <child>+ <object class="GtkHBox" id="X-Label Input Widget">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="border_width">2</property>+ <property name="spacing">2</property>+ <child>+ <object class="GtkEntry" id="X-Label Entry">+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="invisible_char">●</property>+ <property name="invisible_char_set">True</property>+ <property name="primary_icon_activatable">False</property>+ <property name="secondary_icon_activatable">False</property>+ <property name="primary_icon_sensitive">True</property>+ <property name="secondary_icon_sensitive">True</property>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">0</property>+ </packing>+ </child>+ <child>+ <object class="GtkSpinButton" id="X-Label Font Input">+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="invisible_char">●</property>+ <property name="progress_pulse_step">0.5</property>+ <property name="primary_icon_activatable">False</property>+ <property name="secondary_icon_activatable">False</property>+ <property name="primary_icon_sensitive">True</property>+ <property name="secondary_icon_sensitive">True</property>+ <property name="adjustment">X-Label Size</property>+ <property name="digits">1</property>+ <property name="snap_to_ticks">True</property>+ <property name="numeric">True</property>+ <property name="update_policy">if-valid</property>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">1</property>+ </packing>+ </child>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">-1</property>+ </packing>+ </child>+ <child>+ <object class="GtkLabel" id="X-Axis Label">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="label" translatable="yes">X-Axis Label</property>+ <property name="use_markup">True</property>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">0</property>+ </packing>+ </child>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">0</property>+ </packing>+ </child>+ <child>+ <object class="GtkVBox" id="Y-Axis Label Widget">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <child>+ <object class="GtkHBox" id="Y-Axis Input Widget">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="border_width">2</property>+ <property name="spacing">2</property>+ <child>+ <object class="GtkEntry" id="Y-Label Entry">+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="invisible_char">●</property>+ <property name="invisible_char_set">True</property>+ <property name="primary_icon_activatable">False</property>+ <property name="secondary_icon_activatable">False</property>+ <property name="primary_icon_sensitive">True</property>+ <property name="secondary_icon_sensitive">True</property>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">0</property>+ </packing>+ </child>+ <child>+ <object class="GtkSpinButton" id="Y-Label Font Input">+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="invisible_char">●</property>+ <property name="progress_pulse_step">0.5</property>+ <property name="primary_icon_activatable">False</property>+ <property name="secondary_icon_activatable">False</property>+ <property name="primary_icon_sensitive">True</property>+ <property name="secondary_icon_sensitive">True</property>+ <property name="adjustment">Y-Label Size</property>+ <property name="digits">1</property>+ <property name="snap_to_ticks">True</property>+ <property name="numeric">True</property>+ <property name="update_policy">if-valid</property>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">1</property>+ </packing>+ </child>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">-1</property>+ </packing>+ </child>+ <child>+ <object class="GtkLabel" id="Y-Axis Label">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="label" translatable="yes">Y-Axis Label</property>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">0</property>+ </packing>+ </child>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">1</property>+ </packing>+ </child>+ </object>+ </child>+ <child type="label">+ <object class="GtkLabel" id="Labels Frame Label">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="label" translatable="yes"><b>Labels</b></property>+ <property name="use_markup">True</property>+ </object>+ </child>+ </object>+ <packing>+ <property name="expand">False</property>+ <property name="fill">True</property>+ <property name="position">6</property>+ </packing>+ </child>+ <child>+ <object class="GtkFrame" id="Export">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="border_width">3</property>+ <property name="label_xalign">0</property>+ <property name="shadow_type">in</property>+ <child>+ <object class="GtkVBox" id="Export Box">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="border_width">1</property>+ <child>+ <object class="GtkVBox" id="Dimensions">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="border_width">2</property>+ <child>+ <object class="GtkLabel" id="Dimensions Label">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="label" translatable="yes">Dimensions</property>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">0</property>+ </packing>+ </child>+ <child>+ <object class="GtkHBox" id="Dimension Entries">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="spacing">2</property>+ <property name="homogeneous">True</property>+ <child>+ <object class="GtkSpinButton" id="Dimensions Width">+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="invisible_char">●</property>+ <property name="progress_pulse_step">0.25</property>+ <property name="primary_icon_activatable">False</property>+ <property name="secondary_icon_activatable">False</property>+ <property name="primary_icon_sensitive">True</property>+ <property name="secondary_icon_sensitive">True</property>+ <property name="adjustment">Export Width</property>+ <property name="numeric">True</property>+ <property name="update_policy">if-valid</property>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">0</property>+ </packing>+ </child>+ <child>+ <object class="GtkSpinButton" id="Dimensions Height">+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="invisible_char">●</property>+ <property name="progress_pulse_step">0.25</property>+ <property name="primary_icon_activatable">False</property>+ <property name="secondary_icon_activatable">False</property>+ <property name="primary_icon_sensitive">True</property>+ <property name="secondary_icon_sensitive">True</property>+ <property name="adjustment">Export Height</property>+ <property name="numeric">True</property>+ <property name="update_policy">if-valid</property>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">1</property>+ </packing>+ </child>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">1</property>+ </packing>+ </child>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">0</property>+ </packing>+ </child>+ <child>+ <object class="GtkVBox" id="File Name & Type">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <child>+ <object class="GtkLabel" id="File Name Label">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="label" translatable="yes">File Name & Type</property>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">0</property>+ </packing>+ </child>+ <child>+ <object class="GtkHBox" id="File Name">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="border_width">1</property>+ <property name="spacing">2</property>+ <child>+ <object class="GtkEntry" id="File Entry">+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="tooltip_text" translatable="yes">File Name</property>+ <property name="invisible_char">●</property>+ <property name="invisible_char_set">True</property>+ <property name="primary_icon_activatable">False</property>+ <property name="secondary_icon_activatable">False</property>+ <property name="primary_icon_sensitive">True</property>+ <property name="secondary_icon_sensitive">True</property>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">0</property>+ </packing>+ </child>+ <child>+ <placeholder/>+ </child>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">1</property>+ </packing>+ </child>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">1</property>+ </packing>+ </child>+ <child>+ <object class="GtkButton" id="Export Button">+ <property name="label" translatable="yes">Export</property>+ <property name="visible">True</property>+ <property name="can_focus">True</property>+ <property name="receives_default">True</property>+ </object>+ <packing>+ <property name="expand">True</property>+ <property name="fill">True</property>+ <property name="position">2</property>+ </packing>+ </child>+ </object>+ </child>+ <child type="label">+ <object class="GtkLabel" id="Export Frame Label">+ <property name="visible">True</property>+ <property name="can_focus">False</property>+ <property name="label" translatable="yes"><b>Export</b></property>+ <property name="use_markup">True</property>+ </object>+ </child>+ </object>+ <packing>+ <property name="expand">False</property>+ <property name="fill">True</property>+ <property name="position">7</property>+ </packing>+ </child>+ </object>+ </child>+ </object>+ </child>+ </object>+ <packing>+ <property name="expand">False</property>+ <property name="fill">True</property>+ <property name="position">2</property>+ </packing>+ </child>+ </object>+ </child>+ </object>+</interface>