|
|
I've never seen one, but you can always try Google.
|
|
|
|
|
you'll have to use a library (like Gecko[^], for example)
|
|
|
|
|
I have a class called curves and an stl::list of them called curveList.
class curve {
int getValue();
.
.
.
} ;
typedef std::list<curve>::iterator curveItr;
class curveList
{
public:
curveItr getCurveByID(ID i);
.
private:
std::list<curve> _myList
.
}
in my code, I declare
curveItr iter;
iter = myCurveList.getCurveByID(1); // this returns the correct iterator
I get the right value back. Now I want to use iter to call a function of curve to return a value (I'm trying to avoid casting as pointers, list of pointers, etc). Everything I do throws a LINK error LNK2019 unresolved external. I have tried
iter->getValue();
*iter.getValue();
&(*iter)->getValue();
p = static_cast<curve *>(iter)
but everything throws back the same error. Is there a correct way to do this or do i have to resort to lists of pointers?
BTW, the curve/type declarations are in a separate file from getCurveByID is called.
|
|
|
|
|
mjackson11 wrote: Everything I do throws a LINK error LNK2019 unresolved external.
If it is a linker error, then it means that the code compiles fine. Besides, your first approach is the correct one (iter->getValue() ). The error specifies that you have decleared a function but you didn't provide any definition for it. I guess it is for the getValue function, am I right ?
To fix the problem, simply provide a function body.
|
|
|
|
|
Stupid programming tricks -- the method was defined in a file that was in the directory but not included in the project.
Two wasted days.
|
|
|
|
|
I have a long running program (written in C++ using MFC) that consists of one thread to handle the GUI interface and one or more worker threads to do the actual computation / simulation. The worker threads are simulating a stochastic process over and over again. When the worker thread completes one simulation, it notifies the GUI thread by reporting either a pass or fail. This notification is done by calling a member function in the GUI object.
The user interface displays the data entered by the user and every time a run is completed, I would like the user interface to be updated. However, I only want the single line that has changed. The line that has changed looks like:
Passed: <number> Failed: <number>
Currently, the member function that handles the reporting in the GUI object calls the routine CWnd::Invalidate(). This results in (my version of) OnPaint being called. This results in the entire screen being redrawn. Since, I am getting about 10 updates per seond, the screen appears to flicker. I only want to update the single line that has changed. If that line appears to flicker it is ok. What is the best way to do this?
Thanks
Bob
|
|
|
|
|
Hi,
IMO you should only invalidate that portion of the window that needs repainting, have a look
at CWnd::InvalidateRect
That will restrict the region that gets erased, and the information will also be passed to the
paint handler. For optimal performance and minimal flicker, make sure your OnPaint
only repaints the region it is told to repaint.
|
|
|
|
|
Luc,
I think I am starting to get this. Would the following approach work? First, for all calls to OnPaint, the routine will call GetUpdateRect with the address of a struct of type RECT. Recall that the struct RECT has four fields, top, bottom, left and right. These are not pointers but of type integer. Now, if all four fields are set to 0, then my code will redraw the entire window. It does this by executing a a bunch of calls to TextOut. I have a feeling that a more sophisticated MFC programmer would be using a bitmap and then selecting into the device context. However, I am not doing this.
Now, if I just want to redraw that one line, I could set top = bottom = 45 where 45 would be interpreted by my program to just redraw that one line. For that matter, I believe that I could choose any number as long as my program was consistent. Maybe even -45. Now, when my program wants to just update that one line, it would call Invalidate with the address of a struct of type RECT. The fields top and bottom in that struct would be set to 45. Does my approach makes sense?
Thanks
Bob
|
|
|
|
|
Hi Bob,
I don't believe what you suggest would result in correct behavior under all circumstances.
You can't just go and change the semantics of GetUpdateRect[^]; it will return the rectangle (in logical coordinates) that needs updating. It is provided by Windows; e.g. if another window partially obscures your window, then gets moved away, I expect GetUpdateRect to return the area you should repaint, using coordinates, not line numbers.
I suggest you try and look at the normal return values of GetUpdateRect when moving around the window, or moving something else on top and away again.
This is what I would try: whenever you modify a line of text, calculate its rectangular outline; in a first approximation you only need to calculate its vertical position, I would simplify and set the left and right to the entire window's left (=0) and right (=width). In a simple case, when no vertical scrolling is involved, all text lines use the same font and line distance, etc, that would be very simple. Then call InvalidateRect with that rectangle. I expect this normally will cause GetUpdateRect to return the rectangle you just invalidated, and hence OnPaint should now act on that information. One exception would be when several invalidates are still unprocessed in the message queue, Windows may and should optimize them by turning them into a single, possible larger,
invalidation.
BTW: I am a .NET programmer, I did only limited Win32 GUI stuff long ago, that is why I am not
completely sure about the above. However I think the logic behind it all is basically always the same.
|
|
|
|
|
Luc, or anybody else that knows, here is what I wrote:
<br />
LRESULT<br />
CMainWin::ReportAEvent(WPARAM wParam, LPARAM lParam)<br />
{<br />
failCount ++;<br />
RECT rect1;<br />
rect1.top = m_X;<br />
rect1.bottom = m_X + 20;<br />
rect1.left = 2;<br />
rect1.right = 20;<br />
this->InvalidateRect( &rect1 );<br />
this->UpdateWindow();<br />
return 0;<br />
}<br />
Now, my OnPaint rountine looks like the following:
<br />
afx_msg void CMainWin::OnPaint()<br />
{<br />
CPaintDC DC(this);<br />
<br />
RECT rec1;<br />
this->GetUpdateRect( &rec1 );<br />
if ( rec1.top != 0 || rec1.bottom != 0 || rec1.left != 0 || rec1.right != 0 ) {<br />
int i = 10;<br />
int j = 20;<br />
int k = i + j;<br />
}<br />
.<br />
.<br />
.<br />
Originally, I was calling Invalidate method rather than InvalidateRect and Update. When I was calling Invalidate method, the window was being updated from ReportAEvent via a call to OnPaint. Now, when I have the call to InvalidateRect and Update I am not getting a call to OnPaint from the method ReportAEvent. In addition, as far as I can tell, the body of the if statement is never executed.
This does not make a lot of sense to me. I would expect the call to UpdateWindow to be generating a WM_PAINT request and therefore a call to OnPaint. In addition, I have tried running the program where part of the window is covered by anther window and then the original window is brought to the foreground. In this case, I would expect the body of the if statement to be executed. It is not.
Please tell me what I am missing.
Thanks
Bob
|
|
|
|
|
Hi Bob,
if I understand you correctly, there are two issues:
1. the body of OnPaint is never executed
I don't know how that is possible. Is OnPaint called at all?
However I am a bit suspicious about m_X, sounds like a strange name for a vertical coordinate.
2. OnPaint is not called without UpdateWindow().
That may be normal, the WM_PAINT processing has lower priority, if your app is busy all the time
it may never paint a thing.
FYI: you might check the return value of GetUpdateRect instead of the full rect checking.
suggestion: if OnPaint is called, but your rect is wrong somehow, nothing new might be painted;
I would then experiment with a much larger rect (e.g. screen/logical coordinate confusion).
Hope this helps.
|
|
|
|
|
Luc,
Thanks for the response. You were right, m_X is the wrong name and wrong variable for
a vertical coordinate. That was a problem and I fixed it. Now, I am getting calls to OnPaint.
However, GetUpdateRect is still not returning a rectangle. I have two calls to Sleep in the
processing routine, so there is time for the GUI interface to update the window. Sleep
is called with 50, meaning 50 milliseconds. Here are my routines:
<br />
LRESULT<br />
CMainWin::ReportAFailure(WPARAM wParam, LPARAM lParam)<br />
{<br />
failCount ++;<br />
RECT rect1;<br />
rect1.top = m_Y;<br />
rect1.bottom = m_Y + 200000;<br />
rect1.left = m_X;<br />
rect1.right = m_X + 200000;<br />
this->InvalidateRect( &rect1 );<br />
this->UpdateWindow();<br />
return 0;<br />
}<br />
<br />
afx_msg void CMainWin::OnPaint()<br />
{<br />
CPaintDC DC(this);<br />
<br />
RECT rec1;<br />
int retValue = this->GetUpdateRect( &rec1 );<br />
if ( retValue != 0 || rec1.top != 0 || rec1.bottom != 0 || rec1.left != 0 || rec1.right != 0 ) {<br />
int i = 10;<br />
int j = 20;<br />
int k = i + j;<br />
}<br />
.<br />
.<br />
.<br />
The body of the if statement is never executed. The return value from GetUpdateRect is always 0. I do not understand why.
Bob
|
|
|
|
|
Hi Bob,
1.
I am not sure where you have put the Sleep stuff; if it is inside a handler (button click, paint,...)
then it won't help you, since that would cause the GUI thread to sleep, i.e. not do anything for
as long as it takes.
2.
many years ago screen coordinates were only 15 or 16 bits effectively (even when their variables could hold more); I don't know what GDI currently accepts. so a wild guess could be your big rect is invalid, InvalidateRect accepts or maybe rejects it (look at its return value!),
and somehow Windows decides there still is nothing to update due to the invalid rect parameters.
If you doubt your coordinates, just use 0 for top and left, 30000 for bottom and right.
BTW, you should always test the return values, and especially when your code does not do what you would like to get...
|
|
|
|
|
Luc,
I put the Sleep calls in the thread that did the computation, not in the GUI thread. The idea was
to give the GUI thread a chance to catch up if it was behind. I also tried setting top and left to 0
with 30000 for bottom and right as you suggested. That did not help either. The methods InvalidateRect and UpdateWindow are void so there are no return values to check.
However, I now believe I understand what the problem was. I was defining a local variable of
type CPaintDC. When the constructor for CPaintDC is called, it was clearing the invalid region. In addition, sitting bottom past the bottom of the screen results in Windows setting it to the bottom of the screen. Now, I can solve my problem of my screen flickering.
Luc, thanks for your help in this matter.
Bob
|
|
|
|
|
Hi Bob,
You're welcome; I am glad I could be of some help.
Could you please show us the relevant code now it is working, similar to what you had here.[^] Thanks.
|
|
|
|
|
Luc,
Here is my working code:
LRESULT
CMainWin::ReportASuccess(WPARAM wParam, LPARAM lParam)
{
successCount ++;
static RECT rect1;
rect1.top = 300;
rect1.bottom = 30000;
rect1.left = 0;
rect1.right = 30000;
this->InvalidateRect( &rect1, 0 );
this->UpdateWindow();
return 0;
}
LRESULT
CMainWin::ReportAFailure(WPARAM wParam, LPARAM lParam){
failCount ++;
static RECT rect1;
rect1.top = 200;
rect1.bottom = 30000;
rect1.left = 0;
rect1.right = 30000;
this->InvalidateRect( &rect1, 0 );
this->UpdateWindow();
return 0;
}
afx_msg void CMainWin::OnPaint()
{
RECT rec1;
int retValue = this->GetUpdateRect( &rec1 );
if ( rec1.top == 200 ) {
char buffer1[512];
CPaintDC DC(this);
sprintf_s( buffer1, "Failed: %d ", failCount );
DC.TextOut( m_X, 200, buffer1 );
return;
}
if ( rec1.top == 300 ) {
char buffer1[512];
CPaintDC DC(this);
sprintf_s( buffer1, "Passed: %d ", successCount );
DC.TextOut( m_X, 300, buffer1 );
return;
}
CPaintDC DC(this);
DC.TextOutW( m_X, 50, TEXT("Other Text"), 11 );
DC.TextOutW( m_X, 100, TEXT("Other Text"), 11 );
.
.
.
You will notice that I do not erase the background when I call InvalidateRect. In addition, I checked in the debugger that the routine OnPaint was taking the right path.
Bob
|
|
|
|
|
Hi Bob,
thanks for sharing the code.
I do still have some remarks:
1. IMO not erasing the background will result in artefacts, such as old text still showing
under new text.
2. you still are not using the updaterect the way it is intended:
your OnPaint should not test for boundaries being equal, it should basically consist of a
series of this pseudocode:
if (theRectThing1Affects.overlaps.with.updateRect) paint.thing1.in.theRectThing1Affects;
if (theRectThing2Affects.overlaps.with.updateRect) paint.thing2.in.theRectThing2Affects;
if (theRectThing3Affects.overlaps.with.updateRect) paint.thing3.in.theRectThing3Affects;
And of course you then should use real heights, i.e. adjust the rect1.bottom values.
The way you have it just may still cause a dirty window when an overlapping window with top==200
(or 300) gets removed.
|
|
|
|
|
Luc,
Your first point is valid, therefore, I am planning on writting some extra spaces accross the
row when I do the update. If I calls InvalidateRect with the erase option, the text appears to
blink. This is something I do not want.
On your second point, I agree with you that my OnPaint routine needs to be a little smarter. I am plan on working on these today and tommorow. I have a small test program that actually works. If you want me to, I can upload it. Just tell me where.
Bob
|
|
|
|
|
Hi Bob,
to reduce flickering, you should make the invalid rect as small as possible, so if the text
is much narrower than your window, try to give reasonable horizontal coordinates as well.
however, if your update frequency is many times per second (if your app is erasing and painting more than it is calculating something), then anything you erase will cause flickering, since erasing takes almost the same time as painting does, hence the text is disappearing and re-appearing all the time, which is always noticeable, unless...
There basically are two rather drastic remedies:
1.
reduce the screen refresh rate; one way I like to apply is perform the calculations without updating the screen; and have a timer periodically (say twice per second) update the screen,
that is sufficient to show progress, but is much less disturbing, since the time your text
is erased now is only a fraction of 1 percent, hardly noticeable. The added advantage is
your performance will be better (more calculations and fewer screen actions per second).
2.
The other approach is to use double-buffering, which basically means that the entire form (or some
part of it) is first erased and painted in memory, then that memory bitmap gets copied to the screen replacing what is there (hence no erase and minimal visual disturbance).
I cannot provide you with any details; I use it quite often in .NET, where it requires no effort
at all, however I do not know how complex it is in GDI.
And it can make sense to combine it all: invalidate only what is needed, apply double-buffering,
and lower the update frequency.
As for code, I don't need it right now; if you make significant further progress, I am still
interested to learn the details; then we can exchange email addresses and send a zip.
Regards,
|
|
|
|
|
Luc,
I have my small program working the way I like it. I would be happy to send it to you, if you
want me to.
Bob
|
|
|
|
|
I now have an easy fixed for this problem. When you are doing the update, you call
CWnd::Invalidate with an argument of 0. In this case, the 0 stands for false and
Windows will not earse the screen. In addition, the window will not flicker.
Bob
modified on Monday, December 29, 2008 3:09 PM
|
|
|
|
|
Write a program that will read a list of numbers and a desired sum,then determine subset of numbers in the list that yield that sum if such a subset exists.
|
|
|
|
|
Hi,
I have two hints for you:
1. A program quite often is just a recipe that explains to a computer how a human person (with
lots of accuracy, courage, and perseverance) would solve the given problem.
2. Recursion normally gets used to replace a single problem by one or several smaller problems.
That should be sufficient.
|
|
|
|
|
Running on Windows XP sp2, with IE7, I notice that when I use the web browser component and call Navigate() with a res url (i.e. something like res://c:\some\path\to\program\program.exe/stuff.html) that it no longer opens the embedded html resource.
Either I get a dialog (I think from IE) complaining about an unknown file type "stuff" and "Navigation to the webpage was canceled" in the browser view, or I just see "This page cannot be displayed" in the browser view. This is used to all work fine, and now it doesn't.
The rc file looks something like this:
STUFF.HTML HTML DISCARDABLE "res\\stuff.html"
The url passed to Navigate (in this example) would be "res://c:\\some\\path\\to\\program\\program.exe/stuff.html". The dialog error (in the first case) makes me think that somehow the stuff.html is getting truncated and the ".html" is getting lost. Is there some new problem with this, as I never had a problem before? I notice that if you change the URL to "res://c:\\some\\path\\to\\program\\program.exe/stuff" (i.e. drop the .html part) then it works.
¡El diablo está en mis pantalones! ¡Mire, mire!
Real Mentats use only 100% pure, unfooled around with Sapho Juice(tm)!
SELECT * FROM User WHERE Clue > 0
0 rows returned
Save an Orange - Use the VCF!
VCF Blog
|
|
|
|
|