Making a StopWatch

Pages: 12
I was making a Stop Watch in the console.

The part I have already done is:
It waits until the user presses a Key to start the stop-watch.
Then it begins to output the time in the format
HH:MM:SS
And updates it every second (Clearing the screen and then printing the text in the same place with the new data after an interval of one second [using the Sleep function from Windows.h]).

This happens in an infinite loop.

What I wanted to do was to stop the timer as soon as the user press any key on the keyboard.

After giving it a couple of tries I concluded that I would need at-least 2 Threads to do this. One to keep the program updating the time which will be shown on the screen and the second one to wait for the Key Press.

I was reading on making Threads from the documentation in MSDN.

This is a part of the code:
but this doesn't work properly (As I guessed it wouldn't since I was confused with the concept of using threads...)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
//..................
typedef struct MyData {
	int val1;
	int val2;
} MYDATA, *PMYDATA;

bool A = true;

void Ask(bool A)
{
	if (readkey())
		A = false;
}

int main ()
{
	ClearScreen();

	bool A = new bool;
	
	A = true;

	 PMYDATA pDataArray;

	 pDataArray = (PMYDATA) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,sizeof(MYDATA));

	  pDataArray->val1 = 0;
	  pDataArray->val2 = 100;

	CreateThread(NULL,0,MyThreadFunction,pDataArray,NULL,NULL);
	SetColor(Blue, White);
	
	CONSOLE_CURSOR_INFO cci;

	cci.bVisible = FALSE;
	cci.dwSize = 1;

	if (!SetConsoleCursorInfo(Global::hStdout,&cci))
		cout << GetLastError();
	cout << endl << endl << endl << endl << endl;
	cout << "\t\tPressing any key when the STOP WATCH is running will stop it.\n\n";
	
	PressAnyKey("\t\tPress any Key to start timer");
	
	ClearScreen();
	
	while (A)
	{
		gotoxy(27,12);
		clock_t TICKS = clock();
		cout << Time(TICKS);
		Sleep(1000);
		ClearScreen();		
	}

	PressAnyKey();
}


//From the Example in MSDN. Have yet to go through it multiple times to 
//understand any thing from it
DWORD WINAPI MyThreadFunction( LPVOID lpParam ) 
{ 
	HANDLE hStdout;
	PMYDATA pDataArray;

	TCHAR msgBuf[BUF_SIZE];
	size_t cchStringSize;
	DWORD dwChars;

	// Make sure there is a console to receive output results. 

	hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
	if( hStdout == INVALID_HANDLE_VALUE )
		return 1;

	// Cast the parameter to the correct data type.
	// The pointer is known to be valid because 
	// it was checked for NULL before the thread was created.

	pDataArray = (PMYDATA)lpParam;

	// Print the parameter values using thread-safe functions.

	StringCchPrintf(msgBuf, BUF_SIZE, TEXT("Parameters = %d, %d\n"), 
		pDataArray->val1, pDataArray->val2); 
	StringCchLength(msgBuf, BUF_SIZE, &cchStringSize);
	WriteConsole(hStdout, msgBuf, (DWORD)cchStringSize, &dwChars, NULL);

	return 0; 
} 


The thing that is actually confusing me is how will the program know which part of the code to execute in which thread.

EDIT:
Sorry for the long post...
And I forgot to mention that BUF_SIZE is 255.
Last edited on
try a simple change

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

void Ask(bool &A)
{
	if (readkey())
		A = false;
}

// or 

bool ask()
{
       if(readkey())
            return false;
       else
             return true;
}

// in your loop
A= ask();
The thing that is actually confusing me is how will the program know which part of the code to execute in which thread.


See MSDN entry for CreateThread
@Azagaros:
Doing that did one part of the stopwatch. The problem is that now it is not showing the time until I press any key.

@andywestken:
That was where I referred first. And that left me more confused than before...
You don't need multiple threads for this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <windows.h>
#include <iostream>

inline bool IsPressed(int vkey)
{
    return GetAsyncKeyState(vkey) >> 15;
}

int main()
{
    unsigned long seconds_elapsed = 0;

    std::cout << "hit space to start" << std::endl;

    while (!IsPressed(VK_SPACE)) Sleep(25);
    while ( IsPressed(VK_SPACE)) Sleep(25);

    std::cout << "hit space to stop" << std::endl;

    unsigned long start = GetTickCount();

    while (true)
    {
        if (IsPressed(VK_SPACE)) break;

        unsigned long temp = (GetTickCount() - start) / 1000;

        if (seconds_elapsed < temp)
        {
            std::cout << temp << std::endl;
            seconds_elapsed = temp;
        }

        Sleep(25);
    }

    std::cout << "hit esc to quit" << std::endl;

    while (!IsPressed(VK_ESCAPE)) Sleep(25);
}

Useful links:

http://msdn.microsoft.com/en-us/library/ms646293(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/ms724408(v=vs.85).aspx
@m4ster r0shi:
Thanks for this program.
Is there a way to check that if any of the 254 Virtual keys are down?
I was thinking of making it so that if any key is pressed on the key-board, the stopwatch would stop.

That's why I had thought of using the readkey(); function which will wait till the user presses any key and then return the key's ASCII value (which as far as I know is always NonZero), and concluded that I would need a separate thread to run it.
Nisheeth wrote:
Is there a way to check that if any of the 254 Virtual keys are down?

You can use GetAsyncKeyState inside a for loop.
Tried it out.
It worked well enough (Not great because it requires a long press...)!

But thanks. The program now works at least!
I will be trying to make it better.
Last edited on
Nisheeth wrote:
it requires a long press

Hmmm... It shouldn't. Could you post your code?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include "Functions.h"

string Time(clock_t ticks);
bool IsPressed(int vkey);

bool Ask()
{
	for (int i = 1; i < 255;i++)
	{
		if (IsPressed(i))
			return false;
	}
	return true;
}

int main ()
{
	SetColor(Blue, White);
	bool A = true;
	ClearScreen();

	CONSOLE_CURSOR_INFO cci;

	cci.bVisible = FALSE;
	cci.dwSize = 1;

	if (!SetConsoleCursorInfo(Global::hStdout,&cci))
		cout << GetLastError();
	cout << endl << endl << endl << endl << endl;
	cout << "\t\tLong Press Any key to stop the STOP WATCH\n\n";
	
	PressAnyKey("\t\tPress any Key to start the Stop Watch");
	
	ClearScreen();
	clock_t TICKS;
	while (A)
	{
		A = Ask();
		gotoxy(27,12);
		TICKS = clock();
		cout << Time(TICKS);
		Sleep(1000);
		ClearScreen();		
	}
	gotoxy(27,12);
	cout << "Total Time is:  " << Time(TICKS);
	gotoxy(20,13);
	cout << "Press Any key to continue . . .";
	readkey();
}

string Time(clock_t ticks)
{
	time_t time = ticks/CLOCKS_PER_SEC;
	string TheTime = "";
	stringstream buffer;
	if (time >= 3600)
	{
		if (int(time/3600) < 10)
			TheTime += "0";
		buffer << int(time/3600);
		TheTime += buffer.str();
		time = time%3600;
		TheTime += ":";
		buffer.str("");
	}

	else
		TheTime += "00:";

	if (time >= 60 && time < 3600)
	{
		if (int(time/60) < 10)
			TheTime += "0";
		buffer << int (time/60);
		TheTime += buffer.str();
		time = time%60;
		TheTime += ":";
		buffer.str("");
	}

	else
		TheTime += "00:";

	if (time > 0 && time < 60)	
	{
		if (int(time) < 10)
			TheTime += "0";
		buffer << int(time);
		TheTime += buffer.str();
		buffer.str("");
	}

	else
		TheTime += "00";

	return TheTime;
}

bool IsPressed(int vkey)
{
	return GetAsyncKeyState(vkey) >> 15;
}


Here it is.

I guessed it required the long press because it would need time to reach the value of the key in the loop.
Last edited on
Nisheeth wrote:
I guessed it required the long press because it would need time to reach the value of the key in the loop.

Nah, don't worry about that. The reason it requires a long press is this -> Sleep(1000);.

Try doing it like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//...

const int delay   = 25;
      int counter = 0;

while (A)
{
    A = Ask();

    if (counter == 1000)
    {
        ClearScreen();
        gotoxy(27,12);
        TICKS = clock();
        cout << Time(TICKS);

        counter = 0;
    }

    counter += delay;

    Sleep(delay);
}

//... 

EDIT: Sorry, I put ClearScreen right after the cout statement... Fixed it.
Last edited on
Tried this. That way, the Time is being cleared before it can be seen.
I tried moving ClearScreen(); from line 15 to line 23. That way one can get a glimpse of it for 1/40 th of a second...
I think you missed my edit. Check my post above again. I made some changes.
Well, I did miss it. But even after the change, there remains one problem.
It is not updating the Time on the console.
Only after I stop it, does the total time appear.
Nisheeth wrote:
It is not updating the Time on the console.

What do you mean? Does it show the value but
it's not correct, or does it not show the value at all?

Nisheeth wrote:
Only after I stop it, does the total time appear.

If you refer to "Total Time is: ", of course it only appears
when you stop the loop, as you don't print it inside the loop.

What if you try cout << Time(TICKS) << endl; in your loop?

Also, are you sure your Time function works properly?
Last edited on
I am sure the Time function works properly, since it was working before the above suggestion.
What I meant by not updating is:


/*This is happening*/
00:00:01

/*SCREEN CLEARED*/

00:00:01

/*This should happen*/

00:00:01

/*SCREEN CLEARED*/

00:00:02


And no I wasn't referring to the Total Time is: but to the Time itself that was to be printed after the statement...
Aside

I would prob code IsPressed as

1
2
3
4
5
inline bool IsPressed(int vkey)
{
    const USHORT topBit = 0x8000;
    return topBit == (GetAsyncKeyState(vkey) & topBit);
}


Where the topBit == ... test is to shuts up this (level 4) warning:
warning C4800: 'int' : forcing value to bool 'true' or 'false'


Adding an 0x1 == test to the shift version, to shut up the warning, stops it from working for some reason (have not looked into this yet).
Last edited on
@m4ster r0shi:
Sorry. I made a mistake in copying the code. It works perfectly.
I had some-how deleted the counter = 0; line, making the condition forever false.

Sorry again.

@andywestken:
What did you men by
(level 4) warning
?
Ah, right. If you want your program to print only when the total time
changes, you should tell it to. That's what I did in my first post here.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//...

clock_t TICKS = 0;
clock_t START = clock();

while (A)
{
    A = Ask();

    clock_t temp = clock() - START;

    if (TICKS / CLOCKS_PER_SEC < temp / CLOCKS_PER_SEC)
    {
        TICKS = temp

        ClearScreen();
        gotoxy(27,12);
        cout << Time(TICKS);
    }

    Sleep(25);
}

//... 
The previous code you had posted did that work perfectly.
Thanks...
Pages: 12