C++ Konsoleneingabe mit paraller Ausgabe der Uhrzeit

N4p5t3r

Lieutenant
Registriert
Juli 2006
Beiträge
539
Hi zusammen,

die Ausgabe der Uhrzeit stellt mittels ctime.h kein Problem dar.
Ist es möglich in der Konsole diese Uhrzeit dauerhaft in der ersten Zeile
auszugeben, nebenher in der nächsten Zeile normale Textausgaben zu machen
und auf Benutzereingaben zu warten?

Sprich während mittels "cin" gewartet wird, bis die Eingabe mit "Enter" abgeschlossen wird
soll nebenher die ganze Zeit die stets aktuelle Uhrzeit im Format hh.mm.ss angezeigt werden.

Danke & Gruß
N4p5t3r
 
Das Programm ist zwar sinnlos, schau dir es aber mal an. Vielleicht hilfts.
Code:
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>
void gotoxy(int x, int y) {
	HANDLE hStdout;
	COORD coordScreen = { 0, 0 };
	hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
	coordScreen.X = x;
	coordScreen.Y = y;
	SetConsoleCursorPosition( hStdout, coordScreen );
	printf("x=%d , y=%d",x,y);
}
int main()
{
    int x,y;
    for(x=50;x>0;x--)
    {
        for(y=50;y>0;y--)
        {
            gotoxy(x,y);
            //Sleep(1);
        }

    }
}
 
Bringt ihm nur nix, das cin blockiert weitere Ein-/Ausgabe. Dafür müsste man imo einen eigenen Thread nebenher laufen lassen, aber ich wage zu bezweifeln dass dies so einfach geht.
 
Und wie siehts mit anderen Eingabemöglichkeiten aus?
 
Threads erstellen und laufen lassen ist einfach. Der Rest davon kann kompliziert werden. Es gibt im Netz genügend verständliche Anleitungen. Habe auch schon selber mit solchen Sachen in der Konsole herum experimentiert. Du könntest auch mal den Code, den Du bis jetzt hast, hier posten.
 
Nun ja, bis jetzt habe ich dazu keinen. Der Code, in den das integriert werden soll ist zu umfangreich. Es geht mir momentan nur darum, wie ich möglichst einfach die aktuelle Uhrzeit ausgeben und parallel auf eine Eingabe warten kann.
 
Yuuri schrieb:
Bringt ihm nur nix, das cin blockiert weitere Ein-/Ausgabe. Dafür müsste man imo einen eigenen Thread nebenher laufen lassen, aber ich wage zu bezweifeln dass dies so einfach geht.

Entweder das, oder er müßte einen nicht-blockierenden Weg finden, etwas von der Konsole zu lesen. Also pro Schleifendurchlauf schauen, sind neue Eingaben auf der Konsole; wenn ja, dann lesen.
Aber ich halte beide Ansätze für unschön.

@OP: Warum brauchst du so etwas? Würde es nicht ausreichen, in jeder neuen Zeile einmal am Anfang die aktuelle Uhrzeit auszugeben, die dann aber in dieser Zeile nicht mehr zu verändern?
 
Klar wärs so einfacher, aber das Programm soll dazu da sein, die ganze Zeit während es läuft auf Benutzereingaben zu warten, da es eben über eine entsprechende Menüführung verfügt. Von daher ist dies so leider nicht möglich.

Hat vielleicht jemand Ahnung von den Threads? Also das ich parallel zum Warten auf eine Eingabe im gleichen Konsolenfenster ständig die Zeitausgabe aktualisieren kann.
 
Also da du wahrscheinlich sowieso unter Windows unterwegs bist (sonst halt pthreads):
Code:
DWORD thread(void*para){
while(true){
POS oldpos=getCursorPos();
SetCursorPos(0,0);
cout<<hh<<mm<<ss;
SetCursorPos(oldpos);
Sleep(1000);
}
return 0;
}
in main(){
CreateThread(0,0,0,(LPTHREAD_ROUTINE)thread,0,0,0);//Ich hoff die NULLEN sind in der richtigen Anzahl und Reihenfolge
}
SetCursorPos und GetCursorPos muss mann natürlich noch imlementieren und es ist nicht Threadsicher. Das heißt, wenn jemand genau in dem Moment nen Buchstaben eingibt, wo du das Datum ausgibst, dann landet dieser im Datum.
 
Code:
#include <iostream>
#include <string>
#include <time.h>

#include <Windows.h>

using namespace std;

DWORD WINAPI ThreadFunction( LPVOID Params );

void MoveCursor( int x, int y );
void MoveCursorRelative( int dx, int dy );
COORD GetCursorPosition( void );

int main( int argc, char **argv )
{
  HANDLE threadTime = NULL;

  threadTime = CreateThread( NULL, 0, ThreadFunction, NULL, 0, NULL );
  if( threadTime == NULL )
    return 1;

  WaitForSingleObject( threadTime, INFINITE );

  string s = "";

  while( s.compare( "exit" ) != 0 )
  {
    cout << " > ";
    cin >> s;
  }

  CloseHandle( threadTime );
  
  system( "pause" );
  return EXIT_SUCCESS;
}

DWORD WINAPI ThreadFunction( LPVOID Params )
{
  while( true )
  {
    COORD g = GetCursorPosition();
    MoveCursor( 0, g.Y );
    
    time_t t = time( 0 );
    tm *n = localtime( &t );
    cout << "[" << n->tm_hour << ":" << n->tm_min << ":" << n->tm_sec << "] ";

    MoveCursor( g.X, g.Y );
  }
}

void MoveCursor( int x, int y )
{
  HANDLE hStdout;
  COORD Coordinates = {0, 0};
  hStdout = GetStdHandle( STD_OUTPUT_HANDLE );
  Coordinates.X = x;
  Coordinates.Y = y;
  SetConsoleCursorPosition( hStdout, Coordinates );
}

void MoveCursorRelative( int dx, int dy )
{
  COORD p = GetCursorPosition();
  MoveCursor( p.X + dx, p.Y + dy );
}

COORD GetCursorPosition( void )
{
  HANDLE hStdout = GetStdHandle( STD_OUTPUT_HANDLE );
  CONSOLE_SCREEN_BUFFER_INFO csbi;
  if( GetConsoleScreenBufferInfo( hStdout, &csbi ) )
    return csbi.dwCursorPosition;
  return COORD();
}
Funktioniert nur nicht wirklich mit der Eingabe...
 
Code:
int main( int argc, char **argv )
{
  while( true )
  {
    COORD g = GetCursorPosition();
    MoveCursor( 0, g.Y );
    
    time_t t = time( 0 );
    tm *n = localtime( &t );
    cout << "[" << n->tm_hour << ":" << n->tm_min << ":" << n->tm_sec << "] ";

    getchar();

    Sleep( 1000 );
  }
  
  system( "pause" );
  return EXIT_SUCCESS;
}
Hätte den gleichen Effekt (ohne Thread). Im ersten Lauf bleibt die Zeit stehen, danach läuft sie, hängt sich aber beim dritten, vierten oder fünften Update auf und anscheinend lockt die Eingabe ab dem Moment (wie vorausgesagt).
 
Schonmal ein Blick auf die curses-Bibliothek geworfen?
Kann genau sowas leisten (und noch viel mehr). Zusätzlich wäre dein Programm plattformunabhängig und du bräuchtest dich nicht mit den Tiefen der WinAPI beschäftigen.
 
@Yuuri
Darf ich fragen was Zeile 5 & 6 da zu Suchen haben?

Meine damaligen Schulkenntnisse reichen aus um zu verstehen was du machst, ist ja nix anderes als PHP mit anderem Syntax, nur was bringt dir die CursorPosition?

Ich mein es muss doch nur die Zeit im Objekt n gespeichert und ausgegeben werden.
Dann kommt ne Eingabe und ne Pause von einer Sekunde.
Und wegen while TRUE wiederholt sich das Ganze immer und immer wieder.

Würd mich echt interessieren was für eine Relevanz die Zeilen 5 & 6 haben. :)
 
Damit geht er an den ANFANG der aktuellen Zeile zurück, denn der OP wollte die Zeitausgabe immer vorn in der Zeile haben und nicht irgend wo hinten dran.
 
Hab ich die Sache mit der kritischen Zeit so richtig verstanden?

Wenn die aktualisierte Zeit ausgegeben wird, und in diesem Moment ein Tastendruck kommt, dann kanns passieren das der oben in der Zeit landet?

Hab mal ein wenig mit dem geposteten Code rumprobiert, schaut bis jetzt ganz gut aus :)

Edit:

Könnt ihr mir erklären, was genau dieser Teil hier macht?

Code:
COORD GetCursorPosition( void )
{
HANDLE hStdout = GetStdHandle( STD_OUTPUT_HANDLE );
CONSOLE_SCREEN_BUFFER_INFO csbi;
if( GetConsoleScreenBufferInfo( hStdout, &csbi ) )
return csbi.dwCursorPosition;
return COORD();
}

Edit2: Ist es möglich den Thread zu blockieren, während das Hauptprogramm eine Ausgabe macht?
 
Zuletzt bearbeitet:
Die aktuelle Position des Cursors zurückgeben (was auch der Name der Funktion schon aussagt). Den Thread anhalten kannst du mittels SuspendThread, weiter laufen lassen mit ResumeThread. Nur dann hast du die aktuelle Uhrzeit nicht mehr vorn, was ja denke ich ein Kriterium deinerseits war.
 
Jap, das ist klar, aber was sagt mir

Code:
HANDLE hStdout = GetStdHandle( STD_OUTPUT_HANDLE );

Was genau ist ein Handle? Werd aus dem englischen Text von msdn net so wirklich schlau.

Wozu dient die if Abfrage?
Code:
if( GetConsoleScreenBufferInfo( hStdout, &csbi ) )
return csbi.dwCursorPosition;

Jap, das stimmt. Wenn während einer Eingabe mal versehentlich ein Zeichen in der obersten Zeile mit der Uhrzeut landet, dann verschwindet das spätestens nach einer Sekunde wieder (was für mich ok ist).
Aber wenn das Hauptprogramm gerade eine Ausgabe macht, die durchaus etwas länger sein kann, dann soll darin nach Möglichkeit nicht irgendwo die Uhrzeit mit reinkommen. Daher soll der Thread nur solange unterbrochen werden, wie das HP eine Ausgabe macht.


Edit: Top, danke, du warst mir eine große Hilfe :)
 
Zuletzt bearbeitet:
N4p5t3r schrieb:
Daher soll der Thread nur solange unterbrochen werden, wie das HP eine Ausgabe macht.


Dann brauchst du einen Mutex, oder wie Microsoft es bescheuerterweise nennt, eine critical section.

Kurze Erläuterung: Ein Mutex ist ein Synchronisierungsmechanismus, um den man 2 Threads sozusagen "kämpfen" lassen kann. Hat man Codeabschnitte, die stets nur von einem Thread gleichzeitig ausgeführt werden dürfen (wie z.B. deine Ausgaben auf die Konsole), schützt man diese Abschnitte mit einem Mutex. Jeder Thread muß, bevor er in solch einen Codeabschnitt eintritt, erst erfolgreich den Mutex in Beschlag nehmen. Hat den der zweite Thread bereits in Beschlag genommen, schläft der erste solange, bis der zweite Thread den Mutex wieder freigibt und beginnt erst dann mit der Ausführung des so geschützten Codeabschnitts.
Ergänzung ()

Yuuri schrieb:
Den Thread anhalten kannst du mittels SuspendThread, weiter laufen lassen mit ResumeThread.

Von SuspendThread und ResumeThread würde ich ehrlich gesagt die Finger lassen. Die sind kein Ersatz für gescheite Threadsynchronisationsmechanismen (wie auch in den verlinkten Artikeln selbst schon gesagt wird).
 
Okay, zum Thema Mutex:

Könntest du mal nen kurzen Code posten, in dem es das Hauptprogramm und einen Thread inklusive Kontrolle per Mutex vorhanden ist? Iwie mit ein paar Ein-/Ausgaben oder so.
 
Zurück
Oben