Paralellschleife zur Hauptschleife (Fenster)

Alles rund um die Programmierung mit Qt
Antworten
Gapa
Beiträge: 63
Registriert: 4. Juli 2008 14:33
Kontaktdaten:

Paralellschleife zur Hauptschleife (Fenster)

Beitrag von Gapa »

Hallöle,

ich hab nun den grooooßen Sprung geschafft (hoffe ich :P ) und hab einen kleinen Server mit einer hübschen Oberfläche programmiert.
Meine Frage wäre nun, wie ich denn einen Parallelablauf zu der Hauptschleife erstellen kann.
Muss ich da einen extra Thread erstellen oder hat Qt dafür spezielle Tricks?

Um mein Vorhaben etwas genauer zu erläutern:
Ich habe eine Oberfläche (ein Fenster), in welches ich die Serverdaten eintragen kann und welche ich mit einem Klick auf einen Button auswerten lassen kann.
Sobald ich diesen Button klicke, fährt der Server hoch.
Danach kehrt er jedoch einfach wieder zurück. Doch ich möchte ja, dass er zurückkehrt, und parallel dazu eine Schleife startet, welche immer die Sockets nach eingehenden Daten überprüft und diese nach Bedarf verarbeitet.

Wie stelle ich das am besten an (vielleicht gibt es ja eine Möglichkeit, um Funktionen wie "CreateThread" zu vermeiden :wink: )?

Viele Grüße
Gapa
Gestern war heute noch morgen!
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

Qt Sockets erzeugen ein paar Signals. Und wenn das nicht reicht -> QThread
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
Gapa
Beiträge: 63
Registriert: 4. Juli 2008 14:33
Kontaktdaten:

QThread-Problem

Beitrag von Gapa »

Hallo,

ich hab mir nun die QThread-Funktion mit einincludiert, treffe jedoch schon auf die ersten Probleme.

Sehe ich das richtig, dass mit (2 oder mehr) Parallelabläufe nur gelingen, wenn ich mindestens 2 QThread-Objekte erstelle und diese mit ihrer "start"-Funktion aufrufe?

Eigentlich hatte ich mir das mit QThread so vorgestellt, dass ich, sobald ich in meinem Fenster auf "Upload" klicke, dieser Parallelthread gestartet wird und das Programm unabhängig von diesem Ablauf wieder zu meinem Fenster zurückspringt.

Bei mir ist das aber nicht so, denn mein Fenster bleibt nach dem Klicken so lange "disabled", bis der Thread wie eine normale Funktion wieder returned ist.

Wie kann ich es aber machen, dass mein Fenster wieder den Focus bekommt und dennoch der Paralellthread läuft??

Grüße
Gapa
Gestern war heute noch morgen!
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

Wenn dem so ist, hast Du was falsch gemacht. Aber ohne code... :roll:
Sinnvoll zum einstieg wäre sich auch ein wenig mit Threads auseinander zusetzen. Bzw. die Qt-Beispiele (http://doc.trolltech.com/4.4/threads-mandelbrot.html) anzuschauen.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
Gapa
Beiträge: 63
Registriert: 4. Juli 2008 14:33
Kontaktdaten:

...

Beitrag von Gapa »

Hallo,

also ich möchte nur ungern mein komplettes Programm posten, deshalb zeige ich einen Codeabschnitt, welcher in etwa dem Teil in meinem Programm entspricht:

Gui.h

Code: Alles auswählen

//Gui.h

#ifndef GUI_H
#define GUI_H

#include <QApplication>
#include <QWidget>
#include <QThread>
#include <QVBoxLayout>
#include <QGroupBox>
#include <QLabel>
#include <QPushButton>
#include <QLineEdit>
#include <QMessageBox>

#include "windows.h"
#include "winsock.h"


////////////////////////////////////////////////////////////////////////////////
int WinSockStart();

class Widget_Server_Load : public QWidget
{
  Q_OBJECT
  private:
    QVBoxLayout *vbl_main;
    QGridLayout *gl_address;
    QHBoxLayout *hbl_security;
    QGroupBox *gb_address, *gb_security;
    QLabel *l_port, *l_clients_max, *l_password;
    QLineEdit *le_port, *le_clients_max, *le_password;
    QPushButton *pb_load;

  public:
    Widget_Server_Load();
    ~Widget_Server_Load();

  public slots:
    void UpLoad();
};

////////////////////////////////////////////////////////////////////////////////

class Transfer : public QThread
{
  private:
    char pswd[20];
    int port;
    int clients_max;
    SOCKET sock_main;
  public:
    Transfer(char t_password[20], int t_port, int t_clients_max, SOCKET t_sock_main); 
    ~Transfer();
    void run();
};

struct Client
{
  SOCKET sock;
  char name[30];
  bool logmode;
};

#endif
Gui.cpp

Code: Alles auswählen

//Gui.cpp

#include "Gui.h"
#include "Strings.h"
#include "File.h"

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//---Konstruktor---

Widget_Server_Load::Widget_Server_Load()
{
  vbl_main = new QVBoxLayout(this);
  gl_address = new QGridLayout;
  hbl_security = new QHBoxLayout;
  
  gb_address = new QGroupBox("Adresse");
  gb_security = new QGroupBox("Sicherheit");

  l_port = new QLabel("Port: ");
  l_clients_max = new QLabel("Maximale Benutzerzahl: ");
  l_password = new QLabel("Passwort: ");

  le_port = new QLineEdit;
  le_clients_max = new QLineEdit;
  le_password = new QLineEdit;
  
  le_password->setEchoMode(QLineEdit::Password);

  pb_load = new QPushButton("Hochladen");
  
  gl_address->addWidget(l_port, 0, 0, 1, 1);
  gl_address->addWidget(l_clients_max, 1, 0, 1, 1);
  gl_address->addWidget(le_port, 0, 2, 1, 1);
  gl_address->addWidget(le_clients_max, 1, 2, 1, 1);

  hbl_security->addWidget(l_password);
  hbl_security->addWidget(le_password);
  
  gb_address->setLayout(gl_address);
  gb_security->setLayout(hbl_security);
  
  vbl_main->addWidget(gb_address);
  vbl_main->addWidget(gb_security);
  vbl_main->addWidget(pb_load);
  
  connect(pb_load, SIGNAL( clicked() ), this, SLOT( UpLoad() ) );
  setWindowTitle("Server hochfahren");
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//---Destruktor---

Widget_Server_Load::~Widget_Server_Load()
{
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//---UpLoad---

void Widget_Server_Load::UpLoad()
{
  QString port1 = le_port->text(), clients_max1 = le_clients_max->text(), pswd1 = le_password->text();
  bool ok;
  int port = port1.toInt(&ok, 10);
  if((!ok) || (port < 1) || (port > 65000) || (!port))
  {
    QMessageBox::information(this, "Fehler", "Es wurde ein ungültiger Port eingegeben!", QMessageBox::Ok);
   return;
  }
  int clients_max = clients_max1.toInt(&ok, 10);
  if((!ok) || (clients_max < 1) || (!clients_max))
  {
    QMessageBox::information(this, "Fehler", "Ungültige Eingabe!", QMessageBox::Ok);
   return;
  }
  char pswd[20];
  strncpy(pswd, pswd1.toLatin1(), strlen(pswd1.toLatin1()));
  if(strlen(pswd) >= 20)
  {
    QMessageBox::information(this, "Fehler", "Das Passwort ist zu lang (Max. 20 Zeichen)!", QMessageBox::Ok);
   return;
  }
  pswd[strlen(pswd) + 1] = '\0';


  SOCKADDR_IN server;
  server.sin_family = AF_INET;
  server.sin_port = htons(port);
  server.sin_addr.s_addr = ADDR_ANY;
  SOCKET sock_main;
  long handle;

  handle = WinSockStart();
  if(handle == SOCKET_ERROR)
  {
    QMessageBox::information(this, "Fehler", "WinSock konnte nicht gestartet werden (Code: 0001)!", QMessageBox::Ok);
   return;
  }
  
  sock_main = socket(AF_INET, SOCK_STREAM, 0);
  if(sock_main == INVALID_SOCKET)
  {
    QMessageBox::information(this, "Fehler", "Socket konnte nicht erstellt werden (Code: 0002)!", QMessageBox::Ok);
   return;
  }
  
  handle = bind(sock_main, (SOCKADDR*)&server, sizeof(SOCKADDR));
  if(handle == SOCKET_ERROR)
  {
    QMessageBox::information(this, "Fehler", "Socket konnte nicht an Port gebunden werden (Code: 0003)!", QMessageBox::Ok);
   return;
  }

  handle = listen(sock_main, clients_max);
  if(handle == SOCKET_ERROR)
  {
    QMessageBox::information(this, "Fehler", "Fehler beim Aufruf von Funktion 'listen' (Code: 0004)!", QMessageBox::Ok);
   return;
  }
  QMessageBox::information(this, "Upload", "Server wurde erfolgreich gestartet!", QMessageBox::Ok);
  Transfer trans_loop(pswd, port, clients_max, sock_main);
  trans_loop.start();
  //--------->hier ist die alles entscheidende Zeile von der alles abhängt! (**)
 return;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//---WinSockStart---

int WinSockStart()
{
  WSADATA wsa;
 return WSAStartup(MAKEWORD(2,2), &wsa);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//---Transfer---

Transfer::Transfer(char t_password[20], int t_port, int t_clients_max, SOCKET t_sock_main)
{
  strncpy(pswd, t_password, strlen(t_password));
  pswd[strlen(pswd) + 1] = '\0';
  port = t_port;
  clients_max = t_clients_max;
  sock_main = t_sock_main;
 return;
}

Transfer::~Transfer()
{
}

void Transfer::run()
{
  while(1)
  {
    QMessageBox::information(0, "Thread", "Schleifendurchlauf..", QMessageBox::Ok);
  }
 return;
}
Wenn ich nun das Programm so komile und in die auskommentierte Zeile (**) nichts schreibe, so gibt er beim Klicken des Hochladen-Buttons die Erfolgsmeldung aus, und sobald man dann mit "Ok" bestätigt, kommt eine Windows-Fehlermeldung: "Es ist ein Programmfehler aufgetreten...blablabla ... Problembericht senden - nicht senden..." - Man kennt diese Fehlermeldung ja.

Wenn ich stattdessen in die auskommentierte Zeile (**) den Befehl "trans_loop.wait()" schreibe, so reagiert er nach dem Klicken des Buttons "Hochladen" + anschließendem Bestätigen der MessageBox nicht mehr. Die Erfolgsmessagebox sowie das Hauptfenster bleiben disalbed und man muss das Programm mit dem Task-Manager schließen....

Wenn ich jedoch in der auskommentierten Zeile (**) eine weitere QMessageBox ausgeben lasse, ganz egal was für eine, so erscheint diese auch nach der Erfolgsmessagebox, und wenn ich diese dann mit "Ok" bestätige, so läuft das Programm zum ersten Mal so wie ich es wollte (naja fast!). Es kommen dann ständig die MessageBoxen aus der Thread-Schleife, jedoch kann ich immernochnicht zum Hauptfenster zurückkehren, denn dieses, sowie die willkürlich gewählte 2. Messagebox sind weiterhin disabled.


Kann mir das jemand erklären??

Grüße
Gapa
Gestern war heute noch morgen!
pfid
Beiträge: 535
Registriert: 22. Februar 2008 16:59

Beitrag von pfid »

Keine Zeit mir alles durchzulesen, aber nur kurz was mir auffällt:

1) Deine runloop ist eine Endlosschleife. Wie willst du da wieder rauskommen ohne was entsprechendes zu tun?
QThread::start() ruft die runloop auf, ich sehe keine Bedingung bei welcher diese wieder verlassen wird.

2) Du benutzt UI Objekte (QMessageBox) im QThread. UI Objekte dürfen nur im Mainthread verwendet werden.
Gapa
Beiträge: 63
Registriert: 4. Juli 2008 14:33
Kontaktdaten:

endlosschleife

Beitrag von Gapa »

Hallo,

erstmal Danke für den Post.
Deine runloop ist eine Endlosschleife. Wie willst du da wieder rauskommen ohne was entsprechendes zu tun?
QThread::start() ruft die runloop auf, ich sehe keine Bedingung bei welcher diese wieder verlassen wird.
Ja klar es ist eine Endlosschleife, aber das war ja jetzt auch nur ein Beispiel um einen Thread zu testen...
Die Aufgabe eines seperaten Threads ist es doch schließlich, ungestört und unabhängig vom Hauptthread (dem Hauptfenster) zu laufen...
Also müsste in meinem Progrämmchen jetzt eigentlich das Fenster wieder enabled sein und parallel dazu müssten ständig Messageboxen kommen...

Oder habe ich was grundlegendes im Bezug auf Thread missverstanden?? Kann ich mir eig nicht vorstellen, da ich in C++ schon öfters mit Threads zu tun hatte.
Du benutzt UI Objekte (QMessageBox) im QThread. UI Objekte dürfen nur im Mainthread verwendet werden.
Hm...und wieso? Das ist ja blöd! Ich möchte schließlich in meinem Parallelthread auch mal Text ausgeben können...Wie stelle ich das dann an - und vor allem, warum motzt der Compiler nicht???

Grüße
Gapa
Gestern war heute noch morgen!
pfid
Beiträge: 535
Registriert: 22. Februar 2008 16:59

Re: endlosschleife

Beitrag von pfid »

Gapa hat geschrieben:
Deine runloop ist eine Endlosschleife. Wie willst du da wieder rauskommen ohne was entsprechendes zu tun?
QThread::start() ruft die runloop auf, ich sehe keine Bedingung bei welcher diese wieder verlassen wird.
Ja klar es ist eine Endlosschleife, aber das war ja jetzt auch nur ein Beispiel um einen Thread zu testen...
Die Aufgabe eines seperaten Threads ist es doch schließlich, ungestört und unabhängig vom Hauptthread (dem Hauptfenster) zu laufen...
Also müsste in meinem Progrämmchen jetzt eigentlich das Fenster wieder enabled sein und parallel dazu müssten ständig Messageboxen kommen...

Oder habe ich was grundlegendes im Bezug auf Thread missverstanden?? Kann ich mir eig nicht vorstellen, da ich in C++ schon öfters mit Threads zu tun hatte.
Ja sicher, das passt schon. Nur sehe ich, wie gesagt, nicht was du im Code machst, da du uns nur eine Endlosschleife zeigst, und dich irgendwo oben beschwerst, dass dein Thread nicht zurück kommt (o.ä.).
Gapa hat geschrieben:
Du benutzt UI Objekte (QMessageBox) im QThread. UI Objekte dürfen nur im Mainthread verwendet werden.
Hm...und wieso? Das ist ja blöd! Ich möchte schließlich in meinem Parallelthread auch mal Text ausgeben können...Wie stelle ich das dann an - und vor allem, warum motzt der Compiler nicht???

Grüße
Gapa
http://doc.trolltech.com/4.4/threads.ht ... d-qobjects
Gapa
Beiträge: 63
Registriert: 4. Juli 2008 14:33
Kontaktdaten:

Verflixter Fehler!!!

Beitrag von Gapa »

Hallo nochmal,

also ich habe nun wirklich alles versucht, jedoch komme ich bei meinem Problem einfach nicht weiter (vor allem da es ein mysteriöses Problem ist!!)...

Hier nun mal mein komplettes Projekt:

http://www.autohotkey.net/~Gapa/Files/Server.zip

Keine Sorge, es ist nicht viel Code. Es wäre wirklich nett, wenn sich jemand die Zeit nehmen würde man reinzuschauen und die auskommentierten Zeilen zu lesen, denn dieses Problem ist wirklich etwas seltsam. Ein Programmabsturz (nach dem Befehl thrd.start() ) hängt davon ab, ob ich die MessageBox nach dem thrd.start() bzw. davor öffnen lasse!!!!!!!!!!!!!!!!!!
Wenn ich sie danach öffnen lasse stürzt das Programm zwar nicht ab, jedoch wird der Thread dann behandelt wie eine normale Funktion, sprich, mein Fenster bleibt solange blockiert, bis der Thread zurückkehrt!!!
Wenn ich sie davor öffnen lasse (also so wie in der ZIP-Datei), so stürzt das Programm durch den Befehl thrd.start() ab!!!!

Ich wäre für brauchbare Lösungsvorschläge wirklich dankbar!!!

Viele Grüße und VIELEN DANK schonmal im Voraus!!
Gapa
Gestern war heute noch morgen!
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

Ich habe auch ein mysteriöses Problem.. ich hämmere mit dem Kopf gegen die Tischplatte und kriege komischerweise Kopfschmerzen...

Denn:
Connect.h:15:21: error: windows.h: No such file or directory
Connect.h:16:21: error: winsock.h: No such file or directory
Transfer.h:17: error: ‘SOCKET’ does not name a type
Transfer.h:29: error: ‘SOCKET’ does not name a type
Connect.cpp: In member function ‘void Server_Connect::Connect()’:
Connect.cpp:85: error: ‘SOCKADDR_IN’ was not declared in this scope
Connect.cpp:85: error: expected `;' before ‘server’
Connect.cpp:86: error: ‘server’ was not declared in this scope
Connect.cpp:86: error: ‘AF_INET’ was not declared in this scope
Warum nicht QTcpServer?!?

und:

Code: Alles auswählen

 [...]
  Transfer thrd(pswd, port, clients_max, sock_main);
  thrd.start(); 
}
Wie lange lebt denn die Variabel "thrd"?
Antworten