7
$\begingroup$

In a demonstration, I would like to display a PopupMenu with a list of small PDF images, each weighing in at approximately 50k. The code of interest is below:

PopupMenu

After hitting Shift+Enter, the demo requires 25 seconds before being ready to operate. However, once it is ready, it works responsively, without hesitation.

A simple list of numbers in the PopupMenu works much better:

{{s, 2, ""}, Range[12], PopupMenu, ControlPlacement -> Bottom}

One second or so after hitting Shift+Enter, the demo is ready to go.

I realize that I could save some memory by using images with smaller footprints (e.g. moving to 8-bit or even 1-bit images), but there may be other things I should be considering to increase the efficiency of PopupMenu. Any suggestions would be appreciated.


Update

Image file size was not the culprit in this case. It turns out that some of my PDF images were not in a format fully acceptable to Mathematica.

When halirutan was able to quickly import and display large PDF files using my code (but adding the additional burden of importing), I decided to use his code to import my large files.

Mathematica refused to import several of my files as Images, using the routine Halirutan included in his response. However, Mathematica does allow those same files to be imported via the menu via "Insert/Picture/From file".

Because MMA had accepted all my images when I manually inserted them into the Popupmenu control (as shown above), and because it displayed them correctly (albeit after an initial delay), I did not suspect that some of the files (but not others) were in a format uncongenial to MMA. It accepted the corrupt files and "reluctantly" figured out how to display them properly in the PopupMenu control.

So halirutan provided the essential resources that turned out to be instrumental for solving the issue.


Update 2

Neither .pdf nor .png graphics files produced from OmniGraffle Pro (v. 5.3.6) can be successfully imported by Mathematica via the Import[..., "Images"] command.

However GraphicConverter (v. 7.2) can import either of these. While in GraphicConverter, simply "saving as" a .png file from OmniGraffle to .pdf will produce a file that can then be imported by Import into Mathematica. That file will have a white background even if the original .png file had a transparent background. But the important thing is that MMA will accept it readily. By the way, by making my PDF's this way, they turn out to be 7k in size.

$\endgroup$
5
  • $\begingroup$ Hard to say without seeing the code, but wrapping it in a DynamicModule might help - particularly if you've stored the images in variable names inside the Manipulate. $\endgroup$ May 2, 2012 at 15:05
  • $\begingroup$ @Mark I originally stored the images in variable names but later moved them directly into PopupMenu (as shown in the snippet), on the hunch that the lookup was the bottleneck. Turns out, it made no difference. But I kept the images in popup menu anyway. $\endgroup$
    – DavidC
    May 2, 2012 at 15:14
  • $\begingroup$ @MarkMcClure The full code (without images) is here: snippets.dzone.com/posts/show/15285 $\endgroup$
    – DavidC
    May 2, 2012 at 15:48
  • $\begingroup$ I posted my comment from my iPhone just before going out for the day. The code snippet you point to seems to work smoothly and it looks like you figured out your issue. $\endgroup$ May 2, 2012 at 21:16
  • $\begingroup$ Yes, thanks. I just tested the graphics files further and will update information about OmniGraffle and GraphicConverter. $\endgroup$
    – DavidC
    May 2, 2012 at 21:19

1 Answer 1

8
$\begingroup$

First, let me note that 50kB for such a simple graphics is unbelievably much. In fact, I had to create large and randomized raster icons to achieve such a size. Your things on the other hand look really simple and should be extremely small as vector-graphics.

So here is what I tried:

n = 200;
MapIndexed[Export[StringJoin["tmp/", ToString[#2[[1]]], ".pdf"], 
       Colorize[
    Blur[ImageAdd[RandomImage[0.6, {n, n}], 
      Rasterize[Style[#1, 24, White, 
                 Background -> Gray], "Image", ImageSize -> {n, n}, 
       Background -> Gray]], 5]]] & , 
   {"=", "<", "\[LessEqual]", ">", "\[GreaterEqual]", "\[NotEqual]"}]

This gives you icons in pdf format which are about 56k big and look like this

enter image description here enter image description here

Using these icons and including them with With into your Manipulate the execution of the code takes under a second until the content is displayed and ready. The thing which is expensive here, is only the loading of pdf files from disk. Two points:

  • I use With since than the icons are planted directly into the Manipulate and it works even after restarting the kernel
  • I use the parameter "Image" inside Import because usually images are a lot faster than Graphics objects.

blub

With[{srules = (#1 -> 
       First[Import[StringJoin["tmp/", ToString[#1], ".pdf"], 
         "Images"]] & ) /@ 
         Range[6]}, 
 Manipulate[If[newProblem, {op2, a2, b2, newProblem} = 
          {RandomInteger[{1, 6}], RandomInteger[{-5, 5}], 
     RandomInteger[4], False}]; 
      If[a >= 0, a = Min[a, 10 - Abs[b]], a = Max[a, -10 + Abs[b]]]; 
      solution = solutions[op2, a2, b2]; attempt = solutions[s, a, b]; 
      If[problemDisplay != 3 && solution === attempt, success, 
   plunk[]]; 
      Pane[Grid[DeleteCases[{If[problemDisplay == 1, 
                {Panel[
         Style[solution /. {(b7_) || (a7__) :> 
             Row[Riffle[{b7, a7}, Style["  Or  ", 
                                  Gray]]]}, 19, 
          FontFamily -> "Times"]]}], If[problemDisplay == 2, 
                {Panel[absValueEquation[op2, a2, b2]]}], 
              {Show[{axes[{{-10.9, 10.9}, {-0.5, 
            If[MemberQ[display, 3] || MemberQ[display, 4], 2.5, 
                            1]}}], arrows[a, b], 
         segments[s, Blue, a, b]}, BaseStyle -> 16, 
                  ImageSize -> 550, AspectRatio -> Automatic]}, 
      If[MemberQ[display, 1], 
                {Style[
         attempt /. {(b_) || (a__) :> 
            Row[Riffle[{b, a}, Style["  Or  ", Gray]]]}, 19, 
                    FontFamily -> "Times"]}], 
      If[MemberQ[display, 2], {absValueEquation[s, a, b]}], 
              Null}, Null], Spacings -> {2, 1}], 540, 
   Alignment -> Center], 
     {{problemDisplay, 3, "problem:"}, {1 -> "solutions", 
    2 -> "equation or inequality", 
         3 -> "none"}, ControlType -> RadioButtonBar, 
   ControlPlacement -> Top}, 
     {{newProblem, False}, {False, True}, 
   Enabled -> problemDisplay != 3, 
       ControlPlacement -> Top}, {{s, 2, ""}, srules, PopupMenu, 
   ControlPlacement -> Bottom}, 
     {{display, {1}, "display:"}, {1 -> "solutions", 
    2 -> "equation or inequality", 3 -> "a", 
         4 -> "b  "}, ControlType -> CheckboxBar, 
   ControlPlacement -> Bottom}, 
     {{a, 1}, -10, 10, 1, Appearance -> "Labeled", ImageSize -> 500, 
       ControlPlacement -> Bottom}, {{b, 2}, -10, 10, 1, 
   Appearance -> "Labeled", 
       ImageSize -> 500, ControlPlacement -> Bottom}, {{a, 1}, -10, 
   10, 1, ControlType -> None}, 
     {{b, 2}, -10, 10, 1, ControlType -> None}, {{a2, 3}, -10, 10, 1, 
   ControlType -> None}, 
     {{b2, 4}, -10, 10, 1, ControlType -> None}, {{op2, 1}, 1, 6, 1, 
   ControlType -> None}, 
     AutorunSequencing -> {1, {2, 3}, {3, 3}}, 
  TrackedSymbols :> Manipulate, 
     Initialization :> {axes[plotRange_] := 
     Plot[0, {x, -10, 10}, Axes -> {True, False}, 
              Ticks -> {Range[-10, 10, 1], None}, 
      PlotRange -> plotRange, BaseStyle -> 16, 
              ImageSize -> {550, 55}, AspectRatio -> Automatic]; 
          absValueEquation[operator_, center_, span_] := 

     Tooltip[Style[
       If[MemberQ[display, 3], 
        Row[{"|", Style["x", Italic], " - (", center, 
                      ")|", 
          operator /. {1 -> " = ", 2 -> " < ", 3 -> " \[LessEqual] ", 
            4 -> " > ", 5 -> " \[GreaterEqual] ", 
                          6 -> " \[NotEqual] "}, span}], 
        Row[{"|", Style["x", Italic], Which[center < 0, " + ", 
                        center == 0, "", center > 0, " - "], 
          Which[center < 0, Abs[center], center == 0, 
                        "", center > 0, center], "|", 
          operator /. {1 -> " = ", 2 -> " < ", 
            3 -> " \[LessEqual] ", 
                          4 -> " > ", 5 -> " \[GreaterEqual] ", 
            6 -> " \[NotEqual] "}, span}]], 19, 
       FontFamily -> "Times"], 

      Row[{Style["|", 14], Style["x-a", 12, Italic], Style["| ", 14], 
        operator /. opRules, 
                  Style["b", 12, Italic]}]]; 
    arrows[center_, span_] := 

     Graphics[{If[
        MemberQ[display, 
         3], {{AbsoluteThickness[2], Gray, Arrowheads[0.03], 
                      Arrow[{{center, 2.2}, {center, 0}}, 0.25]}, 
         Text[Style["a", Italic], 
                      {center, 2.2}]}, Black], 
       If[MemberQ[display, 4] && span != 0, 
                  {{Brown, AbsoluteThickness[1], Arrowheads[{0.02}], 
          Arrow[{{center, 1.25}, 
                          {center + span, 1.25}}, 0.05]}, 
         Text[Style["+b", Italic], 
                      {center + span/2, 1.65}]}, Black], 
       If[MemberQ[display, 4] && span != 0, 
                  {{Brown, AbsoluteThickness[1], Arrowheads[{0.02}], 
          Arrow[{{center, 1.25}, 
                          {center - span, 1.25}}, 0.05]}, 
         Text[Style["-b", Italic], 
                      {center - span/2, 1.65}]}, Black], 
       If[MemberQ[display, 3] && 
                    MemberQ[display, 4] && 
         span != 0, {{AbsoluteThickness[2], Gray, Arrowheads[0.03], 

          Arrow[{{span + center, 2.2}, {span + center, 0}}, 0.25]}, 

         Text[Style["a+b", Italic], {span + center, 2.2}]}, Black], 

       If[MemberQ[display, 3] && MemberQ[display, 4] && span != 0, 
                  {{AbsoluteThickness[2], Gray, Arrowheads[0.03], 
          Arrow[{{center - span, 2.2}, 
                          {center - span, 0}}, 0.25]}, 
         Text[Style["a-b", Italic], {center - span, 2.2}]}, 
                  Black], 
       If[MemberQ[display, 4] && 
         span == 0, {Text[Style["b=0", Italic], 
                      {center, 1.65}]}, Black]}]; 
    solutions[op_, center_, span_] := 

     Module[{operator = 
        op /. {1 -> Equal, 2 -> Less, 3 -> LessEqual, 4 -> Greater, 
                      5 -> GreaterEqual, 6 -> Unequal}}, 
      Reduce[operator[Abs[x - center], span], x, 
                Reals]]; 
    plunk[n_: 0] := EmitSound[Sound[SoundNote[n, 0.25, "Woodblock"]]]; 
          success := EmitSound[Sound[SoundNote["F", 1, 99]]]; 
    radius = 0.2; 

    opRules = {1 -> Style["=  ", 14], 2 -> Style["<  ", 14], 
      3 -> Style["\[LessEqual]  ", 14], 
              4 -> Style[">  ", 13], 
      5 -> Style["\[GreaterEqual] ", 14], 
      6 -> Style["\[NotEqual] ", 14]}; 

    pt[loc_, type_: "Closed"] := 
     If[type == "Open", Circle[loc, radius], Disk[loc, radius]]; 

    segments[o_, c_, a1_, b1_] := 
     Graphics[{{If[(o == 2 || o == 4 || o == 6) && b1 >= 0, 
                    {c, pt[{b1 + a1, 0}, "Open"], 
          pt[{-b1 + a1, 0}, "Open"]}, c], 

        If[(o == 1 || o == 3 || o == 5) && b1 >= 0, {c, 
          pt[{b1 + a1, 0}], 
                      pt[{-b1 + a1, 0}]}, c], AbsoluteThickness[4], 
        If[(o == 2 || o == 3 || o == 6) && 

          b1 > 0, {Line[{{Abs[b1] + a1 - radius, 
             0}, {-Abs[b1] + a1 + radius, 0}}]}, Black], 

        If[(o == 4 || o == 5 || o == 6) && b1 >= 0, {c, 
          Arrow[{{Abs[b1] + a1 + radius, 0}, 
                          {10.9, 0}}], 
          Arrow[{{-Abs[b1] + a1 - radius, 0}, {-10.9, 0}}]}, Black], 
                  If[(o == 4 || o == 5 || o == 6) && b1 < 0, 
                    {c, Line[{{a1 - radius, 0}, {a1 + radius, 0}}], 
          Arrow[{{a1 + radius, 0}, 
                          {10.9, 0}}], 
          Arrow[{{a1 - radius, 0}, {-10.9, 0}}]}, Black]}}]}]]
$\endgroup$
3
  • $\begingroup$ Thanks. Yes, the images were indeed large. I drew them in OmniGraffle and cropped them in GraphConverter. But since they were in the Manipulate to begin with, wouldn't they, even so, be loaded into memory immediately? I'll try my pics with your suggestion of With and get back shortly. $\endgroup$
    – DavidC
    May 2, 2012 at 18:18
  • $\begingroup$ Yes, I think so. With does effectively the same as you did, by using the graphics directly. But we need a common ground to start with. I guess the long loading time comes from the structure of your icon-graphics which we will see shortly by comparing the timing of your icons with the above ones. $\endgroup$
    – halirutan
    May 2, 2012 at 18:27
  • $\begingroup$ I imported my large files using your With code. Mathematica refused to load three of the .pdf files. The command First complained: "{} has zero length and no first element." When I removed the three defective files, everything worked fine. Conclusion: The issue was not file size but rather file corruption. Mathematica was savvy enough to use the corrupt files if they had been inserted manually into Manipulate. However, it took a lot of time to do this. So I strongly the error arose in GraphicConverter when I changed MMA's .png files into PDF in order to achieve background transparency $\endgroup$
    – DavidC
    May 2, 2012 at 20:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.