Small Panel to Set a Graph's Size, Margins, Magnifcation and Font Size

See title. This panel also makes it possible to easily check and change the total graph size (= plot area + margin). Please let me know if you find any bugs or have a suggestion for improvement.

  • Start by going to Graph => Graph Size and Margins Panel.
  • If you input 0 for a value this size setting will switch back to 'Auto' mode.

UPDATE

  • version 1.1: Added buttons to copy & paste all settings between graphs and fine control to the values via up/down arrows.
  • version 1.2: Added a checkbox to keep the graph in the Auto scaling mode even after setting a new width or height. Unchecking this box will lock graph into Absolute mode (no manual resizing via dragging) upon changing the size. Locked values are indicated by bold numbers.
#pragma TextEncoding = "UTF-8"
#pragma rtGlobals=3     // Use modern global access method and strict wave access.
#pragma version = 1.22

// https://www.wavemetrics.com/user/chozo
// a small panel to modify a graph's size, margin, font and magnification - ver. 2020-07-10
// always works with the top graph and updates automatically upon activation of the panel

Menu "Graph"
    "Graph Size and Margins Panel ...", /Q, MakeGraphSizePanel()
End
//################################
Function MakeGraphSizePanel()
    if (WinType("GraphSizePanel"))
        DoWindow/F GraphSizePanel
        return 0
    endif
    if (ItemsinList(WinList("*", ";", "WIN:1")) == 0)
        Abort "No graphs available. Create a graph first."
    endif
    NewPanel/K=1/W=(500,60,810,285)/N=GraphSizePanel as "Set Graph Size"
    ModifyPanel /W=GraphSizePanel ,fixedSize= 1 ,noEdit=1
    SetWindow GraphSizePanel hook(UpdateHook)=SizePanelUpdateFunc
   
    SetDrawLayer UserBack
    SetDrawEnv linethick= 2,fillpat= 0
    DrawRect 20,10,287,195
    SetDrawEnv linethick= 2,dash= 3,fillpat= 0
    DrawRect 50,40,255,165
    SetDrawEnv linethick= 2,linefgc= (0,0,65535),arrow= 3
    DrawLine 53,70,252,70
    SetDrawEnv linethick= 2,linefgc= (0,0,65535),arrow= 3
    DrawLine 205,43,205,163
    SetDrawEnv linethick= 2,linefgc= (1,39321,19939),arrow= 3
    DrawLine 28,135,280,135
    SetDrawEnv linethick= 2,linefgc= (1,39321,19939),arrow= 3
    DrawLine 105,13,105,193
   
    SetVariable VLeftMargin     ,pos={5 ,95}    ,value=_NUM:0
    SetVariable VBottomMargin       ,pos={127   ,170}   ,value=_NUM:0
    SetVariable VRightMargin        ,pos={245   ,95}    ,value=_NUM:0
    SetVariable VTopMargin          ,pos={127   ,15}    ,value=_NUM:0
    SetVariable VGraphHeight        ,pos={75    ,95}    ,value=_NUM:0
    SetVariable VPlotWidth          ,pos={125   ,60}    ,value=_NUM:0
    SetVariable VGraphWidth     ,pos={125   ,125}   ,value=_NUM:0
    SetVariable VPlotHeight     ,pos={175   ,95}    ,value=_NUM:0
    SetVariable VMagnification  ,pos={215   ,170}   ,value=_NUM:0
    SetVariable VGraphFont          ,pos={37    ,170}   ,value=_NUM:0
   
    ModifyControlList ControlNameList("GraphSizePanel",";","*") ,limits={0,2000,1}  ,size={60.00,18.00} ,bodyWidth=60 ,proc=SizeVarControl
   
    SetVariable VMagnification  ,title="exp:"   ,limits={0,8,0}         ,bodyWidth=40   ,format="%g x"
    SetVariable VGraphFont          ,title="font:"  ,limits={0,100,0}   ,bodyWidth=40
   
    TitleBox GraphTitle             ,pos={25    ,15}    ,size={60,23}       ,title="none"               ,frame=0
    TitleBox SwapTitle              ,pos={140   ,80}    ,size={60,23}       ,title="\JCswap:"           ,frame=0
    CheckBox SwapAxes               ,pos={148   ,98}    ,size={23,23}       ,title=""                       ,proc=SwapGraphControl ,value=0
    CheckBox KeepAutoMode           ,pos={12    ,203}   ,size={23,23}       ,title="keep auto mode" ,value=1    ,help={"Automatically resets the mode to Auto after changing the size."}
    PopupMenu SizeUnit              ,pos={190   ,15}    ,size={70,20}       ,title="unit: "             ,proc=SizePopControl
    PopupMenu SizeUnit              ,mode=1         ,popvalue="points"  ,value= #"\"points;inches;cm\""
   
    Button CopyAllButton            ,pos={120   ,200}   ,size={85,20}       ,title="copy values"        ,proc=CopyPasteControl
    Button PasteAllButton           ,pos={215   ,200}   ,size={85,20}       ,title="paste copied"       ,proc=CopyPasteControl
   
    FetchLatestGraphSizeValues()
End
//################################
Function SizePopControl(s) : PopupMenuControl
    STRUCT WMPopupAction &s
    if (s.eventCode != 2)
        return 0
    endif
    String CtrlList = ControlNameList("GraphSizePanel",";","V*")
    CtrlList = RemoveFromList("VMagnification;VGraphFont;",CtrlList)
    Variable Increment = 1
    if (s.popNum == 2)      // in inches
        Increment = 0.02
    elseif (s.popNum == 3)  // in cm
        Increment = 0.05
    endif
    ModifyControlList CtrlList ,win=GraphSizePanel  ,limits={0,2000,Increment}
    FetchLatestGraphSizeValues()
End
//################################
Function SwapGraphControl(cb) : CheckBoxControl
    STRUCT WMCheckboxAction& cb
    if (cb.eventCode != 2)
        return 0
    endif
    if (ItemsinList(WinList("*", ";", "WIN:1")) == 0)   // make sure there is a graph to work with
        return 0
    endif
    String gName = StringFromList(0,WinList("*", ";", "WIN:1"))
    ModifyGraph/W=$gName swapXY=cb.checked
    FetchLatestGraphSizeValues()
End
//################################
Function CopyPasteControl(s) : ButtonControl
    STRUCT WMButtonAction &s
    if (s.eventCode != 2)
        return 0
    endif
   
    String CtrlList = "", CtrlName = "", ValList = "", Others
    Variable i, Value = 0
    StrSwitch(s.ctrlName)
        case "CopyAllButton":
            CtrlList = ControlNameList("GraphSizePanel", ";", "V*")
            CtrlList = RemoveFromList("VGraphWidth;VGraphHeight;",CtrlList)
            for (i = 0; i < ItemsInList(CtrlList); i += 1)
                CtrlName = StringFromList(i,CtrlList)
                ControlInfo/W=GraphSizePanel $CtrlName
                ValList += num2str(V_Value) + ";"
            endfor
           
            Others = ""
            ControlInfo/W=GraphSizePanel SwapAxes
            Others +=  "SwapAxes:" + num2str(V_Value) + ";"
            ControlInfo/W=GraphSizePanel SizeUnit
            Others +=  "SizeUnit:" + num2str(V_Value) + ";"
           
            SetWindow GraphSizePanel ,userdata(CtrlSet)=CtrlList
            SetWindow GraphSizePanel ,userdata(ValSet)=ValList
            SetWindow GraphSizePanel ,userdata(OtherSet)=Others
        break
        case "PasteAllButton":
            CtrlList    = GetUserData("GraphSizePanel", "", "CtrlSet")
            ValList = GetUserData("GraphSizePanel", "", "ValSet")
            Others      = GetUserData("GraphSizePanel", "", "OtherSet")
            if (strlen(CtrlList) == 0)
                break
            endif
            CheckBox SwapAxes  ,win=GraphSizePanel  ,value=str2num(StringByKey("SwapAxes", Others))
            PopupMenu SizeUnit ,win=GraphSizePanel  ,mode=str2num(StringByKey("SizeUnit", Others))
            for (i = 0; i < ItemsInList(CtrlList); i += 1)
                CtrlName = StringFromList(i,CtrlList)
                Value = str2num(StringFromList(i,ValList))
                SetVariable $CtrlName ,win=GraphSizePanel ,value=_NUM:Value
                ExecuteVarChange(CtrlName, Value)
            endfor
        break
    EndSwitch
       
    FetchLatestGraphSizeValues()
End
//################################
Function SizeVarControl(struct WMSetVariableAction & s) : SetVariableControl
    if (s.eventCode != 1 && s.eventCode != 2)
        return 0
    endif
    ExecuteVarChange(s.ctrlName, s.dval)
End
//################################
Function ExecuteVarChange(String ctrlName, Variable val)
    if (ItemsinList(WinList("*", ";", "WIN:1")) == 0)   // make sure there is a graph to work with
        return 0
    endif
    String gName = StringFromList(0,WinList("*", ";", "WIN:1"))
   
    Variable Modifier = 1
    ControlInfo/W=GraphSizePanel SizeUnit
    if (V_Value == 2)       // in inches
        Modifier = 72
    elseif (V_Value == 3)   // in cm
        Modifier = 72/2.54
    endif
   
    ControlInfo/W=GraphSizePanel KeepAutoMode
    Variable Auto = V_Value
   
    StrSwitch (ctrlName)
        case "VLeftMargin":
            ModifyGraph/W=$gName margin(left) = val*Modifier
        break
        case "VBottomMargin":
            ModifyGraph/W=$gName margin(bottom) = val*Modifier
        break
        case "VRightMargin":
            ModifyGraph/W=$gName margin(right) = val*Modifier
        break
        case "VTopMargin":
            ModifyGraph/W=$gName margin(top) = val*Modifier
        break
        case "VGraphHeight":
            ControlInfo/W=GraphSizePanel VTopMargin
            Variable mTop = V_Value
            ControlInfo/W=GraphSizePanel VBottomMargin
            Variable mBot = V_Value
            Variable pHeight = val-mTop-mBot
            if (val == 0)
                ModifyGraph/W=$gName margin(top) = 0
                ModifyGraph/W=$gName margin(bottom) = 0
                ModifyGraph/W=$gName height = 0
            elseif (pHeight > 0)
                ModifyGraph/W=$gName height = pHeight*Modifier
                if (Auto)
                    DoUpdate/W=$gName
                    ModifyGraph/W=$gName height = 0
                endif
            endif
        break
        case "VPlotWidth":
            ModifyGraph/W=$gName width = val*Modifier
            if (Auto)
                DoUpdate/W=$gName
                ModifyGraph/W=$gName width = 0
            endif
        break
        case "VGraphWidth":
            ControlInfo/W=GraphSizePanel VLeftMargin
            Variable mLeft = V_Value
            ControlInfo/W=GraphSizePanel VRightMargin
            Variable mRight = V_Value
            Variable pWidth = val-mLeft-mRight
            if (val == 0)
                ModifyGraph/W=$gName margin(left) = 0
                ModifyGraph/W=$gName margin(right) = 0
                ModifyGraph/W=$gName width = 0
            elseif (pWidth > 0)
                ModifyGraph/W=$gName width = pWidth*Modifier
                if (Auto)
                    DoUpdate/W=$gName
                    ModifyGraph/W=$gName width = 0
                endif
            endif
        break
        case "VPlotHeight":
            ModifyGraph/W=$gName height = val*Modifier
            if (Auto)
                DoUpdate/W=$gName
                ModifyGraph/W=$gName height = 0
            endif
        break
        case "VMagnification":
            Variable Mag = val == 1 ? 0 : val
            ModifyGraph/W=$gName expand = Mag
        break
        case "VGraphFont":
            ModifyGraph/W=$gName gfSize = val
        break
    EndSwitch
   
    DoUpdate/W=$gName
    FetchLatestGraphSizeValues()
end
//################################ panel update helper function ##################################
Function SizePanelUpdateFunc(s)
    STRUCT WMWinHookStruct &s
    If (s.eventCode == 0)               // Activate
        FetchLatestGraphSizeValues()    // fetch graph properties on panel activation
        return 1
    endif
    return 0                                // 0 if nothing done, else 1
End
//################################
Function FetchLatestGraphSizeValues()
    if (ItemsinList(WinList("*", ";", "WIN:1")) == 0)   // make sure there is a graph to work with
        TitleBox GraphTitle ,win=GraphSizePanel ,title="No Graph"
        return 0
    endif
    String gName = StringFromList(0,WinList("*", ";", "WIN:1"))
   
    Variable Modifier = 1
    ControlInfo/W=GraphSizePanel SizeUnit
    if (V_Value == 2)       // in inches
        Modifier = 1/72
    elseif (V_Value == 3)   // in cm
        Modifier = 2.54/72
    endif
   
    // values from getwindow (sometimes off by a fraction?)
    GetWindow $gName gsize
    //print V_left, V_right, V_top, V_bottom        // debug
    Variable gLeft  = V_left
    Variable gTop   = V_top
    Variable gWidth = V_right-V_left   
    Variable gHeight    = V_bottom-V_top   
    GetWindow $gName psize
    //print V_left, V_right, V_top, V_bottom        // debug
    Variable mLeft  = round(V_left - gLeft)
    Variable mTop   = round(V_top - gTop)
    Variable mRight = round(gWidth - V_right)
    Variable mBottom    = round(gHeight - V_bottom)
    Variable pWidth = round(V_right - V_left)
    Variable pHeight    = round(V_bottom - V_top)
    gHeight = round(gHeight)
    gWidth      = round(gWidth)
   
    GetWindow $gName expand
    Variable gMag   = V_Value == 0 ? 1 : V_Value
   
    // if available, replace with values from recreation macro (these are exact)
    String GraphSettings = GrepList(WinRecreation(gName, 0), "(?i)(height|width|margin|gfsize|swapXY)", 0 , "\r")
    GraphSettings = ReplaceString("\tModifyGraph ",GraphSettings,"")
    GraphSettings = ReplaceString("\r",GraphSettings,",")
    Variable SetmLeft   = str2num(StringByKey("margin(left)", GraphSettings, "=", ","))
    Variable SetmTop        = str2num(StringByKey("margin(top)", GraphSettings, "=", ","))
    Variable SetmRight  = str2num(StringByKey("margin(right)", GraphSettings, "=", ","))
    Variable SetmBottom = str2num(StringByKey("margin(bottom)", GraphSettings, "=", ","))
    Variable SetpWidth  = str2num(StringByKey("width", GraphSettings, "=", ","))
    Variable SetpHeight = str2num(StringByKey("height", GraphSettings, "=", ","))
    Variable setgHeight = SetpHeight + SetmTop + SetmBottom
    Variable setgWidth  = SetpWidth + SetmRight + SetmLeft

    Variable setSwap        = str2num(StringByKey("swapXY", GraphSettings, "=", ","))
    Variable gFont      = str2num(StringByKey("gfSize", GraphSettings, "=", ","))
    setSwap = numtype(setSwap) == 0 ? setSwap : 0
    gFont       = numtype(gFont) == 0 ? gFont : 10
   
    mLeft       = numtype(SetmLeft) == 0 ? SetmLeft : mLeft
    mBottom = numtype(SetmBottom) == 0 ? SetmBottom : mBottom
    mRight      = numtype(SetmRight) == 0 ? SetmRight : mRight
    mTop        = numtype(SetmTop) == 0 ? SetmTop : mTop
    pWidth      = numtype(SetpWidth) == 0 ? SetpWidth : pWidth
    pHeight = numtype(SetpHeight) == 0 ? SetpHeight : pHeight
    gHeight = numtype(SetgHeight) == 0 ? SetgHeight : gHeight
    gWidth      = numtype(SetgWidth) == 0 ? SetgWidth : gWidth
   
    Variable temp = 0
    if (setSwap)        // swap all values
        temp        = mLeft
        mLeft       = mBottom
        mBottom = temp
        temp        = mRight
        mRight      = mTop
        mTop        = temp
        temp        = gHeight
        gHeight = gWidth
        gWidth      = temp
        temp        = pWidth
        pWidth      = pHeight
        pHeight = temp
    endif
   
    // recalculate into inch or cm if needed
    if (Modifier != 1)
        mLeft       = round((mLeft*Modifier)*100)/100
        mBottom = round((mBottom*Modifier)*100)/100
        mRight      = round((mRight*Modifier)*100)/100
        mTop        = round((mTop*Modifier)*100)/100
        gHeight = round((gHeight*Modifier)*100)/100
        pWidth      = round((pWidth*Modifier)*100)/100
        gWidth      = round((gWidth*Modifier)*100)/100
        pHeight = round((pHeight*Modifier)*100)/100
    endif

    TitleBox GraphTitle             ,win=GraphSizePanel ,title=gName
    CheckBox SwapAxes               ,win=GraphSizePanel ,value=setSwap
    SetVariable VLeftMargin     ,win=GraphSizePanel ,value=_NUM:mLeft       ,fstyle=(numtype(SetmLeft)==0)
    SetVariable VBottomMargin       ,win=GraphSizePanel ,value=_NUM:mBottom     ,fstyle=(numtype(SetmBottom)==0)
    SetVariable VRightMargin        ,win=GraphSizePanel ,value=_NUM:mRight      ,fstyle=(numtype(SetmRight)==0)
    SetVariable VTopMargin          ,win=GraphSizePanel ,value=_NUM:mTop            ,fstyle=(numtype(SetmTop)==0)
    SetVariable VGraphHeight        ,win=GraphSizePanel ,value=_NUM:gHeight     ,fstyle=(numtype(SetgHeight)==0)
    SetVariable VPlotWidth          ,win=GraphSizePanel ,value=_NUM:pWidth      ,fstyle=(numtype(SetpWidth)==0)
    SetVariable VGraphWidth     ,win=GraphSizePanel ,value=_NUM:gWidth      ,fstyle=(numtype(SetgWidth)==0)
    SetVariable VPlotHeight     ,win=GraphSizePanel ,value=_NUM:pHeight     ,fstyle=(numtype(SetpHeight)==0)
    SetVariable VMagnification  ,win=GraphSizePanel ,value=_NUM:gMag
    SetVariable VGraphFont          ,win=GraphSizePanel ,value=_NUM:gFont
End

 

Screenshot of the Panel GraphSizePanel_v122.ipf

I just had a quick play with this in IP8 on macOS. It works well. Thanks for developing this and sharing. Getting plots looking the same is one thing I always struggle with in Igor, particularly when I have several in a Layout and the y-axes have different numbers of integers.

I tried putting my Graph into a Layout and found that the switch to absolute settings (when changing e.g. vertical plot area, with GraphSizePanel) changes how I normally work with plots in a Layout, but I like the control that this panel gives to the graph window.

Thank you very much for testing! Great that it seems to work on mac. I updated the panel a bit to include copy and paste buttons, which makes it possible to share the same settings between many graphs easily (something I do very often).

Yes, if you dial in a size it will change to 'Absolute' mode. But you can dial in 0 (zero) for any value to switch back to 'Auto' mode. For example, you could change the plot width/height (blue arrows) and then directly afterwards input in 0. This will leave the size the same, but switches back to Auto for additional adjustments. Note that the margins will change size if you switch to auto unlike the width and height. Do you think it would be useful if you could choose whether or not the mode should switch to Auto after input on its own? Maybe I could add a checkbox for that. By the way if you input 0 for the total graph width/height (green arrows) then both this setting and the margins in this direction reset to Auto mode. This way you get a graph with the correct total size but all other values are freely adjustable.

OK, I went ahead and included a checkbox to keep the graph in Auto scaling mode after making any changes (see version 1.2 above). Obviously, this will not affect the margins, as they will not keep their size.

This is great. The copy/paste works nicely between two windows as I tested it. I think that's a useful addition. Thanks also for adding the checkbox. That definitely helps the way I work with Layouts (maybe this isn't the right way - who knows!). FYI on Mac the text on the "paste copied" button is very slightly clipped but other than that the panel looks good. Thanks!

In reply to by sjr51

@sjr51: In case you don't already know, you can select multiple graphs in a layout and use the Layout main menu to:

  • Make Same Width
  • Make Same Height
  • Make Same Height and Width
  • Make Plot Areas Uniform

If none of those is sufficient maybe you can post an example.

In reply to by sjr51

Thanks for the feedback, Stephen. I have slightly increased the button size in version 1.21 to make it fit (hopefully).

@Adam: I think the problem was that my panel was locking the graph into Absolute values, which prevents scaling in the layout. This way, most of the options you have suggested will not work. But that is fixed with the latest version, which leaves the graph in the Auto scaling mode. And yes, I know that you wont really *need* to scale the graphs when working with layouts, but I think there are valid cases (for example, you would like to know where the annotations end up).

A few thoughts:

* Rather than killing the panel when no graph exists, perhaps just show an alert

* Don't you also need to handle the different Windows / macOS pixel/point conversion factors (96 / 72) for the inches or cm conversions?

* Could the rounding that you do when you work with the m... values be done only until the end?

SetVariable VLeftMargin,win=GraphSizePanel,value=_NUM:round(mLeft)

* I have found it useful to make structure functions as read-from / write-to routines panels, akin to this ...

Static StrConstant k_PanelName = "GraphSizer"

Structure S_PanelValues

    variable paramA
    //variable ...

EndStructure

Function Sf_ReadPanel(svp)
    STRUCT S_PanelValues &svp
   
    ControlInfo/W=$k_PanelName setvarA
    svp.paramA = v_value
    //...
   
    return 0
end

Function Sf_WritetoPanel(svp)
    STRUCT S_PanelValues &svp

    SetVariable setvarA, win=$k_PanelName, value=_NUM:svp.paramA
    //...
   
    return 0
end

* In addition to indicating locked values with bold, would it help also to disable the SetVariable?

Function f_IsLocked(value)
    variable value
   
    return (numtype(value)==0)
endif

// within the Sf_WritetoPanel(...) function

SetVariable VLeftMargin, ... value=_NUM:svp.mLeft,fstyle=(f_IsLocked(svp.mLeft)),disable=(2*f_IsLocked(svp.mLeft))

 

In reply to by jjweimer

Jeffrey, thank you very much for your suggestions.

* Rather than killing the panel when no graph exists, perhaps just show an alert

OK, there was no particular reason to kill the panel. It can just as well stay open. I have replaced the kill command. Now there is just 'No Graph' displayed. I think an alert or print message will get annoying fast.

* Don't you also need to handle the different Windows / macOS pixel/point conversion factors (96 / 72) for the inches or cm conversions?

Actually, I was not sure about this one. But there was an example in the Igor help somewhere were just 72 was used. Also, the inch values match the ones in the Modify Graph panel here on windows. So I guess its fine as long as no pixels are involved (points != pixels)? Would be great to know for sure, but there is not much I can find about this in the help.

* Could the rounding that you do when you work with the m... values be done only until the end?

Somehow I need rounding before the values are converted to inches/cm or it will be slightly off from the 'official' values.

* I have found it useful to make structure functions as read-from / write-to routines panels, akin to this ...

Yes, I should use more structures and sub-functions in general. I didn't really make it a habit yet. In this snippet, everything is mostly handled inside the one function FetchLatestGraphSizeValues(). I'm not sure the overhead from structures and sub-functions makes any difference here. I of course am very much open to improve my coding style. If you would like to hack away at the code I'd be interested to see what you would come up with.

* In addition to indicating locked values with bold, would it help also to disable the SetVariable?

This would defeat the point of the panel, no? The values should be always editable to adjust the size settings.

Forum

Support

Gallery

Igor Pro 8

Learn More

Igor XOP Toolkit

Learn More

Igor NIDAQ Tools MX

Learn More