Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problems with sip 4.14.3 and PyQt 4.9.6 (was: Slow parsing in editor window) #100

Closed
sverd opened this issue Feb 25, 2013 · 36 comments
Closed

Comments

@sverd
Copy link

sverd commented Feb 25, 2013

Frescobaldi used to work fast! However since some time the parsing in the editor window seems to be so slowwww that it renders Frescobaldi nearly useless...

This is what happens:

  • open a .ly file: seems to be as fast as it used to be but then the GUI "stalls" for some seconds until the actual .ly file is shown in the editor window
  • scrolling through the .ly file is pretty fast but every change, say something like commenting out a line takes a few seconds before it appears. Undoing such a change is fast again, but scrolling down some lines introduces the wait time again. For this reason I thought it may have something to do with parsing.
  • when compiling the .ly file no progress or output is shown before the compilation has finished (this used to be different )

This happens for both the 2.0.8 release as the current git version and with the following versions of the used libraries:
Python: 2.7.3
Qt: 4.8.4
PyQt4: 4.9.6
sip: 4.14.3

Are these library versions not supported or am I overseeing some other things?

@wbsoft
Copy link
Collaborator

wbsoft commented Feb 25, 2013

Did the slowness start right after updating libraries on your system?
Does it happen with every ly file since then?
It is very strange that the GUI stalls so often, even while compiling a LilyPond file.

@sverd
Copy link
Author

sverd commented Feb 26, 2013

Well I'm using openSuse Tumbleweed (a "rolling release") so some libraries may have been updated since the last time i used Frescobaldi without problems (I did not use it for a couple of weeks). An updated library may be the cause of the problem because exactly the same program that used to work flawlessly before started giving those problems when I wanted to use it again about a week ago. Recompiling the stable 2.0.8 or the git version did not help at all.
The problem occurs with every ly file I tried since then (does this also point in the direction of a problem with a certain library?)

After rereading what I just wrote I think you may be right about some problems with the system libraries. However do you have any idea what is the best approach to solve this? Is Frescobaldi maybe incompatible with certain versions of the libraries it used or should I maybe try to create a "statically linked" version or so?

@wbsoft
Copy link
Collaborator

wbsoft commented Feb 26, 2013

What happens if you close the PDF view, re-save the ly file and restart Frescobaldi so that the PDF view does not show (and therefore does not load)?

This looks like a problem with different PyQt libraries being loaded at the same time because e.g. popplerqt4 might link to the wrong SIP or PyQt libraries.

@sverd
Copy link
Author

sverd commented Feb 26, 2013

When I follow the steps you describe I keep the same behavior as before. I did notice however that Frescobaldi takes 100% CPU during the "wait" times, so at starting up and for example when adding a "%" at the beginning of a line to comment it out. So effectively it is not stalling but just very busy at those moments

Also tried to "downgrade" the SIP libraries, but then "Python-Poppler-Qt4" didn't build anymore due to "a wrong version of the implemented sip API". Therefore I upgraded the SIP libraries again.

Is there a way to tell whether different PyQt libraries are being loaded at the same time? (According to the "package manager" there is only a single PyQt installed......)

@wbsoft
Copy link
Collaborator

wbsoft commented Feb 26, 2013

Ok, we can rule our the library/dependency problem per se.

But you made an interesting remark about Frescobaldi working well when all of LilyPond input is on one (or a few) lines. Many code in Frescobaldi iterates over text lines (QTextBlock) instances. E.g. the syntax highlighting/parsing (even if turned off in the View menu), auto-indenting, matching parenthesis highlighting etc etc. There may be a difference in how QTextBlocks are handled by Python in more recent SIP versions. That might cause interesting problems. I'll try to look into it.

@wbsoft
Copy link
Collaborator

wbsoft commented Feb 26, 2013

What happens if you switch off the automatic indent?

@sverd
Copy link
Author

sverd commented Feb 26, 2013

Turning "Automatic indent" off does not make a difference. However turning "Auto-completion" (I hope the translation from "Automatisch aanvullen" is correct) does make a big difference!! When that option is turned off, the editor is fast like it was before (or at least feels like that)

When compiling the "Lilypond-output" window is still not updated until the compilation process is finished. At first it seems the compilation itself is also much slower than it used to be, and also slower than on a different much older and slower computer. Because Lilypond is an external program that is executed from by Frescobaldi I ran a number of compilations while simultaneously looking at the CPU usage of the Frescobaldi and the Lilypond processes. It turned out that the Lilypond process existed for a relatively short period of time and used a relatively small amount of CPU (<40%). Before and after Lilypond is executed the Frescobaldi process takes approximately 100% CPU during a longer period of time. (say Frescobaldi 45% of time before, then Lilypond 10% of time, Frescobaldi 45% of time after). This makes me think there may be a delay in Frescobaldi before and after the actual call to Lilypond is made..but as I'm not familiar with the Frescobaldi internals this is pure guessing...

@wbsoft
Copy link
Collaborator

wbsoft commented Feb 26, 2013

Ok, the autocompletion stuff can be slow and time consuming because it searches files that are \include'd for variable definitions. And it is not yet threaded, meaning that reading a file shortly blocks the GUI. Do you use \include statements? Also when running LilyPond, Frescobaldi searches included files for commands that might alter the name of the generated output file(s).

Does Frescobaldi also block the GUI and use lots of CPU before and after running LilyPond with auto-complete turned off?

@sverd
Copy link
Author

sverd commented Feb 26, 2013

Ok, that makes sense because triggering the auto-complete by hitting Ctrl+Space does still have the delay. Weird that even on the older/slower computer the search seems to be pretty fast (as is the autocompletion) and it used to be fast on this computer as well...
In the files I tested there is not a single \include statement. Does Frescobaldi or Lilypond still include some other files in that case or do I need to specify some search paths?

Frescobaldi does block the GUI and uses lots of CPU before/after running Lilypond with auto-complete turned off. It also just caught my attention that with auto-complete turned off, editing works fine after the file content is shown, but the delay/blocking is still present when opening a ly file. Once the file is opened and shown this delay is gone.

@wbsoft
Copy link
Collaborator

wbsoft commented Feb 27, 2013

Thanks for all the effort you take in helping me to locate the problem.

I'll now try to look in what could cause the delays. Something in autocomplete and something that's done on first load and before LilyPond is run. Maybe, if it's possible, I'd like to have a LilyPond file that demonstrates the problem on your machine.

@sverd
Copy link
Author

sverd commented Feb 27, 2013

That should not be any problem at all. Should I past some file content here? (it seems only images can be attached directly)

@wbsoft
Copy link
Collaborator

wbsoft commented Feb 27, 2013

yeah that could be nice. I'm right now updating one of my systems to newer library versions.

@sverd
Copy link
Author

sverd commented Feb 27, 2013

OK. As things happen with every ly file I just created a rather simple example which shows the mentioned behavior. In case some of the extra layout settings influence the overall behavior I did include the layout settings I normally use (basically they adjust the output size of the music to fit on a marching band size paper while still printing on A4 paper). Hopefully this does not complicate the example too much, otherwise let me know and I'll skip the \paper and the \layout parts for the example or try to create a better example!

**** file begin ****

% small test file to test the usage of frescobaldi
% effectively using rather adjusted \header \paper sections etc in order
% to have the output suitable for a marching-band sized paper (while printing on A4)
\version "2.12.0"
#(set-default-paper-size "a4")
#(set-global-staff-size 18)
\header {
  % use the \markup construct to increase the font size if needed
  title = \markup {\fontsize #+4 "Title"}
  subtitle =  ##f
  subsubtitle = ##f
  instrument = ##f
  arranger = ##f
  opus = ##f
  meter = ##f
  poet = ##f
  tagline =  ##F  % the fesult is "Music engraving by Lilypond....."
  composer = ##F % \markup {\fontsize #+2 "Comp: Trad."}  %##f
  piece = \markup {\fontsize #+1 "Trompet B" \normalsize \flat }
  copyright = ##f
}
\paper {
  %    paper-height = 29.7\cm
  %  paper-width = 21.0\cm
    page-top-space = 0\mm
    page-spacing-weight = #10 % relative importance of vert-hor spacing, default 10
    top-margin = 1.0\cm
    bottom-margin =  15.7\cm %7\cm
    left-margin =  1.0\cm
    right-margin = 3.5\cm % defined but not working correctly according to the manual => use line-width
    line-width = 16.5\cm
    head-separation = 0\mm
    foot-separation = 0\mm
    after-title-space = 0\mm
    before-title-space = 0\mm
    between-title-space = 0\mm
    indent = 0.0\cm
    % vertical spacing of the systems
    between-system-padding = #0.525
    between-system-space = 1.6\cm
    ragged-last-bottom = ##t  %% set to ##t if your score is less than one page
    ragged-bottom = ##f
    page-count = #1
    %system-count = #9  % set the number of systems in the output
    % annotate-spacing = ##t  % debug the spacings  
  
    % JUST SOME HEADER FOR A 2ND PAGE
    evenHeaderMarkup = \markup \fill-line { 
      \left-column { \fromproperty #'header:piece }
      \center-column {\fontsize #-4  \fromproperty #'header:title }
      \right-column {\fromproperty #'page:page-number-string }%"/" \fromproperty #'page:page-count
%         \on-the-fly #print-page-number-check-first \fromproperty #'page:page-number-string
%           " "
%         \fromproperty #'header:title
        }
  }
% layout block to influence the horizontal spacing things
\layout {
  \context {
    \Score
    \override SpacingSpanner
    #'base-shortest-duration = #(ly:make-moment 1 6) % larger values of the last number result in larger music!
    \override SpacingSpanner
    #'common-shortest-duration = #(ly:make-moment 1 6) % larger values of the last number result in larger music!
  }
}
AvoiceAA = \relative c'{
    \set Staff.instrumentName = #""
    \set Staff.shortInstrumentName = #""
    \clef treble
    %staffkeysig
    \key c \major 
    \clef treble
    %bartimesig: 
    \time 4/4 
    c4 \mf d e f |
    g a b c~ |
    c1 \fermata |
    c4 b a g |
    f e d c~ |
    c1 \fermata \bar"||"    
    c4 \mf d e f |
    g a b c~ |
    c1 \fermata |
    c4 b a g |
    f e d c~ |
    c1 \fermata \bar"||"    
    c4 \mf d e f |
    g a b c~ |
    c1 \fermata |
    c4 b a g |
    f e d c~ |
    c1 \fermata \bar"||"    
    c4 \mf d e f |
    g a b c~ |
    c1 \fermata |
    c4 b a g |
    f e d c~ |
    c1 \fermata \bar"||"    
    c4 \mf d e f |
    g a b c~ |
    c1 \fermata |
    c4 b a g |
    f e d c~ |
    c1 \fermata \bar"||"    
    c4 \mf d e f |
    g a b c~ |
    c1 \fermata |
    c4 b a g |
    f e d c~ |
    c1 \fermata \bar"|."    
}% end of last bar in partorvoice
\score { 
    << 
        \context Staff = ApartA << 
            \context Voice = AvoiceAA \AvoiceAA
        >>
      \override MultiMeasureRest #'expand-limit = #1 % always expand multi measure rests!
      \set Score.skipBars = ##t
        \set Score.melismaBusyProperties = #'()
        \set Score.skipBars = ##t
        \set countPercentRepeats = ##t
%         \override Score.BarNumber #'break-visibility = #end-of-line-invisible %%every bar is numbered.!!!
%         \set Score.barNumberVisibility = #all-bar-numbers-visible  % also show the first bar number
%         \set Score.barNumberVisibility = #(every-nth-bar-number-visible 5)  % every nth barnumber is shown
        \override Score.BarNumber #'self-alignment-X = #LEFT %#CENTER   % center the bar numbers
         #(set-accidental-style 'modern-cautionary)
%          \set Score.markFormatter = #format-mark-box-letters %%boxed rehearsal-marks
          \set Score.markFormatter = #format-mark-box-numbers %%boxed rehearsal-marks
%         \set Score.markFormatter = #(lambda (mark  context) (make-circle-markup (format-mark-numbers mark context)))
%         \set Score.markFormatter = #(lambda (mark  context) (make-circle-markup (format-mark-letters mark context)))
%          \set Score.markFormatter = #format-mark-box-alphabet %%boxed rehearsal-marks including I
%         \override Score.TimeSignature #'style = #'() %%makes timesigs always numerical
        %% remove previous line to get cut-time/alla breve or common time 
    >>
}

**** file end *****

@wbsoft
Copy link
Collaborator

wbsoft commented Feb 28, 2013

I am now using Frescobaldi: 2.0.9
Python: 2.7.3
Qt: 4.8.2
PyQt4: 4.9.3
sip: 4.13.3

which is in ubuntu 12.10, with no problems at all. Your libraries are even newer, so there might still be an issue with the new libraries which I'm not yet aware of. (But reading the change logs for SIP, Qt4 and PyQt4 I can't find anything that could explain the problems you're experiencing).

@sverd
Copy link
Author

sverd commented Feb 28, 2013

I tried at a different computer with a recent openSUSE installed and did some experimenting. This other computer was running a 32-bit openSUSE (i586) whereas the first computer was running a 64-bit version.
For the second computer with
Frescobaldi: 2.0.9
Python: 2.7.3
Qt: 4.8.4
PyQt4: 4.9.6
sip: 4.14.3
I experienced the same problems as before.

After downgrading some libraries I tried again, this time with
Frescobaldi: 2.0.9
Python: 2.7.3
Qt: 4.8.1
PyQt4: 4.9.1
sip: 4.13.2
This time Frescobaldi worked as before: flawlessly. All above mentioned problems were gone!
I think it is tempting to conclude that somehow the library versions do matter but I really have no clue what could be the cause of the problems.
Maybe the 32-bit vs 64-bit difference between both computers also matters. So I'll give it a try on the first computer as well and let you know what I find out!

@sverd
Copy link
Author

sverd commented Feb 28, 2013

Just tried downgrading on the first computer (64-bit) as well. Downgrading SIP lead to a downgrade of PyQt as well.
I am now running:
Frescobaldi: 2.0.9
Python: 2.7.3
Qt: 4.8.1
PyQt4: 4.9.1
sip: 4.13.2
all in 64 bit mode and like the i586 approach things work flawlessly like before!!
I therefore think the latest versions of some libraries may introduce some problems, although I have no clue where they originate from. However, at least I know how to get Frescobaldi running smoothly again!!

@wbsoft
Copy link
Collaborator

wbsoft commented Feb 28, 2013

Great. Well I got another report from someone having problems with the latest SIP/PyQt4 versions when using the Score Wizard. As soon as I hit those versions on one of my systems, I'll be able to look into it.

@sverd
Copy link
Author

sverd commented Feb 28, 2013

Great. And many thanks for all your efforts already!

@vgay
Copy link

vgay commented Mar 5, 2013

Similar problem with archlinux (64-bit)
frecobaldi 2.0.8
python2 2.7.3-4
python-sip 4.14.3-1
pyqt 4.9.6-2
qt4 4.8.4-13

Frescobaldi worked fine 1 month ago. After some upgrades it takes 30 secondes or more to open a document. When I try to set the fold/unfold option everything seems frozen (several seconds to display a menu).
When compiling the window remains frozen until the task is completed.

Downgrading to python2-sip-4.14.2-1 solves the problem

@wbsoft
Copy link
Collaborator

wbsoft commented Mar 5, 2013

Ok thanks. So sip-4.14.3 introduces the problems. I tried manually building sip-4.14.3 with PyQt4 but I get lots of segfaults...so I'll have to wait til my xubuntu introduces it before I can see what really happens and if that can be solved on the Frescobaldi side. It certainly looks like an unintended behaviour change in sip.

@pl-gauthier
Copy link

Same critical issue here using Arch Linux

Frescobaldi: 2.0.8
Python: 2.7.3
Qt: 4.8.4
PyQt4: 4.10
sip: 4.14.4
Operating System:
Linux-3.7.10-1-ARCH-x86_64-with-glibc2.2.5

@sebleblanc
Copy link
Contributor

Same setup as pilmusic. I have run a call graph of the issue (http://imgh.us/open2.svg).

This is how I made the callgraph:

Python 2.7.3 (default, Dec 22 2012, 21:14:12) 
[GCC 4.7.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import frescobaldi_app.main
<wait until frescobaldi is loaded>
>>> import pycallgraph
>>> pycallgraph.start_trace()
<Open a file. Wait with 100% for about 5 minutes. Then ^C once.>
>>> Traceback (most recent call last):
  File "/usr/lib/python2.7/site-packages/frescobaldi_app/highlighter.py", line 232, in highlightBlock
    cursortools.data(self.currentBlock()).tokens = tokens
  File "/usr/lib/python2.7/site-packages/frescobaldi_app/cursortools.py", line 234, in data
    def data(block):
  File "/usr/lib/python2.7/site-packages/pycallgraph.py", line 265, in tracer
    func_name, full_name)
  File "/usr/lib/python2.7/site-packages/pycallgraph.py", line 155, in __call__
    from fnmatch import fnmatch
KeyboardInterrupt
pycallgraph.save_dot("/tmp/blabla/open2.dot")
EOF

From the call graph, we can see that much time is spent in highlighter.Highlighter.highlightBlock (322 sec) and tokeniter.tokens (316 sec). (Roughly 5 minutes both)

I hope this can be of use. Is there a way to completely remove highlighting? Something like a command-line flag, or a simple line that can be commented / uncommented...

Warning: The SVG file is a bit large (544 kB), although Chromium here can open it fairly easily.

@wbsoft
Copy link
Collaborator

wbsoft commented Mar 11, 2013

Many thanks for the callgraph! I will try to make one myself, on my correctly functioning system, but I think I already see the problem.

The problem is that all calls to tokeniter.tokens() result in a regeneration of the highlighting for that textblock, while the tokens should be cached in the QTextBlock().userData() objects. These cached tokens seem to be lost when using SIP 4.14.3 and PyQt 4.9.6, causing the lexer to run thousands of times, instead of just once!

This looks like a bug in the PyQt 4.9.6 / sip 4.14.3 combo. Maybe it can be circumvented.

Unfortunately there is (currently) no way to turn the parser off, even with highlighting turned off, the parser runs.

@wbsoft
Copy link
Collaborator

wbsoft commented Mar 11, 2013

Who is able to debug this somewhat further?

Go to a python shell:

>>> import frescobaldi_app.main

then open one document (that contains some text on the first line) and be sure to have the document in the display.

>>> import app
>>> doc = app.documents[0]
>>> doc.firstBlock().userData().tokens

This should print a tuple of tokens, e.g.

(u'\\version', u' ', u'"', u'2.14.0', u'"')

If this raises an AttributeError, then we know the tokens are forgotten each time.

@wbsoft
Copy link
Collaborator

wbsoft commented Mar 11, 2013

Well, I found the difference in Pyqt4/Sip behaviour.

When you set an instance attribute to a QTextBlockUserData instance after having the QTextBlockUserData instance assigned to a QTextBlock with QTextBlock.setUserData(), the python copy of QTextBlockUserData is recreated (forgetting the python instance attribute) when the userdata is requested again later on a new instance of the "same" QTextBlock (QTextBlock instances are recreated always).

In sip 4.13.2 (and maybe even 4.14.2), sip would recognize the userdata object and the python instance attribute is not forgotten.

I have posted a question regarding this on the PyQt mailing list.

@sebleblanc
Copy link
Contributor

I have run your commands and I get this output:

Python 2.7.3 (default, Dec 22 2012, 21:14:12) 
[GCC 4.7.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import frescobaldi_app.main
>>> import app
>>> doc = app.documents[0]
>>> doc
<document.Document object at 0x2417b00>
>>> doc.firstBlock()
<PyQt4.QtGui.QTextBlock object at 0x242bf80>
>>> doc.firstBlock().userData()
<PyQt4.QtGui.QTextBlockUserData object at 0x244e050>
>>> doc.firstBlock().userData().tokens
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'QTextBlockUserData' object has no attribute 'tokens'

@wbsoft
Copy link
Collaborator

wbsoft commented Mar 11, 2013

Yes, I also tested with a manually built sip/pyqt4 combo. This is the posting to the PyQt list.

http://www.riverbankcomputing.com/pipermail/pyqt/2013-March/032457.html

SIP apparently doesn't support anymore python instance attributes on wrapped C++ types, at least not of the QTextBlockUserData type (not a subclass of QObject). In sip-4.14.3, the C++ objects are rewrapped on each request and thus they forget python instance attributes.

Whether this is a SIP bug or an intentional change, the problem can be circumvented in a satisfying way. I can derive from QTextBlockUserData and then set the tokens attribute to the instance. But there are some more places in Frescobaldi where this technique is used.

@wbsoft
Copy link
Collaborator

wbsoft commented Mar 11, 2013

Funny to see the tokenizer (State.tokens()) in the callgraph being called 500000+ times :-) It should only run once for each text line in a document....

@wbsoft
Copy link
Collaborator

wbsoft commented Mar 11, 2013

Confirmation of the PyQt4 author that this is a PyQt4 bug. The upcoming nightly snapshot contains the fix!

@wbsoft
Copy link
Collaborator

wbsoft commented Mar 12, 2013

I tried to work around the bug, but it is not possible, unfortunately. Even when deriving from QTextBlockUserData, the Python wrapper of the C++ object is garbage collected and lost. It works for a few seconds but then the tokens are lost.

I also see other PyQt4-based IDE's suffering from this problem (e.g. Ninja). I could simply disable the parsing but that would destroy almost all features like syntax highlighting, autocomplete, coloring matching braces, automatic indent, but also the music functions transpose, translate, etc etc.

I could manually run the parser for such important music functions, but also in that case the tokens are expected to be cached, as some functions iterate backwards over the tokens (e.g. checking whether we are in a \relative music or not) and that will break as well when the tokens are forgotten each time.

So I think I must state that Frescobaldi cannot be used with the SIP-4-14.3 / PyQt4-4.9.6 combo due to this bug.
Hopefully a fix is released soon.

@wbsoft
Copy link
Collaborator

wbsoft commented Mar 13, 2013

Finally I was able to make a workaround. When the highlighter is run, it saves state for everyblock using the userState() integer value. So we use that state and only parse the tokens for that line of text. It is still much slower than caching the tokens, but at least the highlighter does not re-run on the whole document on every request. Frescobaldi now remains usable I hope. Please test current git!

@donarturo
Copy link

Frescobaldi: 2.0.9

Python: 2.7.3 -- Qt: 4.8.4 -- PyQt4: 4.10 -- sip: 4.14.4
OS: Linux-3.8.0-gentoo-i686-Genuine_Intel-R-CPU_T2400@_1.83GHz-with-gentoo-2.2

Traceback (most recent call last):
File "/home/art/src/frescobaldi/frescobaldi_app/pitch/init.py", line 69, in transpose
pitch.transpose(cursor, self.mainwindow())
File "/home/art/src/frescobaldi/frescobaldi_app/pitch/pitch.py", line 413, in transpose
source = tokeniter.Source.selection(cursor, True)
File "/home/art/src/frescobaldi/frescobaldi_app/tokeniter.py", line 402, in selection
state = globals()'state'
File "/home/art/src/frescobaldi/frescobaldi_app/tokeniter.py", line 69, in state
if block.previous().userState() == -1 and block.blockNumber() > 0:
AttributeError: 'QTextCursor' object has no attribute 'previous'

@wbsoft
Copy link
Collaborator

wbsoft commented Mar 14, 2013

ah thanks! actually that was caused by commit 5ecb5b3, fixed!

@pl-gauthier
Copy link

Thanks a million wbsoft! Frescobaldi is working again! Let's get on with the music shall we :-)

@wbsoft
Copy link
Collaborator

wbsoft commented Mar 15, 2013

Great! Please test everything thouroughtly, as we're approaching the 2.0.9 release :-)

Thanks to all for helping to alleviate this issue!

@wbsoft wbsoft closed this as completed Mar 15, 2013
@juanalexei
Copy link

Too bad I posted my report on GCode instead, it would have saved you some search! Thanks a lot!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants