IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Contribuez C++ Discussion :

[FAQ][VC++]Comment créer un bouton exécutant une action en continue à l'état enfoncé


Sujet :

Contribuez C++

  1. #1
    Membre éprouvé
    Avatar de Gabrielly
    Inscrit en
    Juin 2004
    Messages
    722
    Détails du profil
    Informations forums :
    Inscription : Juin 2004
    Messages : 722
    Points : 1 128
    Points
    1 128
    Par défaut [FAQ][VC++]Comment créer un bouton exécutant une action en continue à l'état enfoncé
    Nous voulons créer un bouton qui lorsque on le maintient enfoncé, il exécute une action jusqu'à ce que ce dernier soit relaché.
    Pour y parvenir nous allons définir une classe personnalisée de CButton ou bien de CMFCButton (si nous voulons des fonctionnaliés en plus pour notre bouton). Mais CButton suffit pour ce que nous recherchons.

    Voici la classe :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     
    class CStayPressedButton : public CButton
    {
    	DECLARE_DYNAMIC(CStayPressedButton)
     
    public:
    	CStayPressedButton();
    	virtual ~CStayPressedButton();
     
    	LPVOID m_pThreadUserData;
    	AFX_THREADPROC m_pUserActionThreadProc;
     
    public:
    	static UINT CALLBACK ThreadProc(LPVOID lParam);
    	HANDLE m_hFinished;
     
    	DECLARE_MESSAGE_MAP()
    public:
    	afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
    	afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
    	afx_msg void OnMouseMove(UINT nFlags, CPoint point);
    };
    1. Lorsque l'on click sur le bouton la méthode OnLButtonDown() est appelée et dans cette dernière un thread exécutant la tâche en continue est automatique lancé.
    2. Lorsque l'on relache le bouton la méthode OnLButtonUp() signale au thread qu'il doit stopper.
    3. Lorsque la souris se trouvant à l'état enfoncé et sort du rectangle englobant du bouton, la méthode OnMouseMove() signale également que le thread doit stopper.
    4. m_pThreadUserData contient les données que vous voulez passer au thread
    5. m_pUserActionThreadProc pointe sur la méthode qui est le point d'entrée de votre thread
    6. ThreadProc() est le thread lancé en interne par CStayPressedButton
    7. m_hFinished est un handle pour signaler l'évènement d'arrêt du thread

    Dans votre boite de dialogue vous ajoutez un bouton sur votre resource et vous lui mappez une variable CButton que vous remplacer en CStayPressedButton
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    class CMyDlg : public CDialog
    {
    // ...
                 CStayPressedButton m_btnStayPressed;  // bouton resté pressé = action en continue
     
    	int m_i; // déclaration de votre donnée personnelle à passer à pThreadUserData
    	static UINT UserActionThreadProc(LPVOID pThreadUserData);  // votre méthode static s'exécutant en continue
    //...
    8. UserActionThreadProc est la méthode qui s'exécutera en boucle dans le thread de CStayPressedButton::ThreadProc()
    9. m_i est un exemple d'une de vos variables qui sera passé à pThreadUserData dans votre méthode CMyDlg::UserActionThreadProc()
    10. m_btnStayPressed est notre Stay Pressed Button.

    11. Dans le CMyDlg::OnInitDialog() je prépare le CStayPressedButton
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    	m_i = 2;  // votre données personnelles
    	m_btnStayPressed.m_pThreadUserData = (LPVOID) &m_i;
    	m_btnStayPressed.m_pUserActionThreadProc = (AFX_THREADPROC)UserActionThreadProc;
    12. Votre méthode en continue
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    UINT CMyDlg::UserActionThreadProc(LPVOID pThreadUserData)
    {
     
                 int i = *((int*)pThreadUserData);  // exemple d'utilisation
    	// work, work, work still working on stay pressed button state...
    	// put your code here in the thread loop...
    	return 0;
    }
    Et maintenant le code du CStayPressedButton

    13. Code d'initialisation :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    IMPLEMENT_DYNAMIC(CStayPressedButton, CButton)
     
    CStayPressedButton::CStayPressedButton()
    {
    	m_pThreadUserData = NULL;
    	m_pUserActionThreadProc = NULL;
    	m_hFinished = ::CreateEvent(NULL, FALSE, FALSE, NULL);
    }
     
    CStayPressedButton::~CStayPressedButton()
    {
    	if(m_hFinished)
    		::CloseHandle(m_hFinished);
    }
    14. Tables des messages
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    BEGIN_MESSAGE_MAP(CStayPressedButton, CButton)
    	ON_WM_LBUTTONDOWN()
    	ON_WM_LBUTTONUP()
    	ON_WM_MOUSEMOVE()
    END_MESSAGE_MAP()
    15. Quand le bouton gauche de la souris est enfoncé l'action est lancé :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    void CStayPressedButton::OnLButtonDown(UINT nFlags, CPoint point)
    { 
    	if(m_pUserActionThreadProc == NULL)
    	{
    		CButton::OnLButtonDown(nFlags, point);
    		return;
    	}
     
    	SetCapture();
     
    	AfxBeginThread((AFX_THREADPROC)ThreadProc, (LPVOID) this);
     
    	CButton::OnLButtonDown(nFlags, point);
    }
    16. Quand le bouton gauche de la souris est relaché l'action est stoppé:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    void CStayPressedButton::OnLButtonUp(UINT nFlags, CPoint point)
    {
    	if(GetCapture())
    	{
    		::SetEvent(m_hFinished);
    		ReleaseCapture();
    	}
     
    	CButton::OnLButtonUp(nFlags, point);
    }
    17. Si la souris sort du rectangle du bouton l'action est aussi stoppé:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    void CStayPressedButton::OnMouseMove(UINT nFlags, CPoint point)
    {
    	if(GetCapture())
    	{
    		CRect rect;
    		GetClientRect(&rect);
     
    		if( !rect.PtInRect(point) )
    		{
    			::SetEvent(m_hFinished);
    			ReleaseCapture();
    		}
    	}
    	CButton::OnMouseMove(nFlags, point);
    }
    18. Le thread du bouton
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    UINT CALLBACK CStayPressedButton::ThreadProc(LPVOID pParam)
    {
    	CStayPressedButton* pBtn = (CStayPressedButton*)pParam;
    	ASSERT_KINDOF(CStayPressedButton, pBtn);
     
    	do
    	{
    		pBtn->m_pUserActionThreadProc(pBtn->m_pThreadUserData);
    	}
    	while(::WaitForSingleObject(pBtn->m_hFinished, 0) != WAIT_OBJECT_0);
     
    	return 0;
    }
    Et Voilà

  2. #2
    Membre confirmé Avatar de stephdim
    Profil pro
    Inscrit en
    Août 2007
    Messages
    462
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 462
    Points : 521
    Points
    521
    Par défaut
    salut,

    quelques remarques concernant ton code : (voir ici : http://www.developpez.net/forums/d72...intenu-action/ )

    il manque quelque chose dans le code à gabrielly.
    c'est de s'assurer que le thread c'est bien arrêter.
    si l'action est trop longue entre chaque itération, on risque de déclencher plusieurs thread en même temps.

    d'autre part, le code pour arrêter le thread (SetEvent) aurait dû être placé dans le Handler "OnCaptureChanged", de façon que si un autre contrôle vole la capture de la souris, on arrete le thread. dans le code à Gabrielly, le thread restera actif indéfiniment.

    d'autre part, l'usage d'un "Event" n'etait pas nécessaire. une simple variable aurait fait l'affaire. c'est le thread IHM qui accède en écriture à la variable. Le thread de travail n'a un accès qu'en lecture. Il n'y a pas de notion d'accès concurrent.
    le code modifié suivant les remarques ci-dessus:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
     
    class CStayPressedButton : public CButton
    {
    	DECLARE_DYNAMIC(CStayPressedButton)
     
    public:
    	CStayPressedButton();
    	virtual ~CStayPressedButton();
     
    	LPVOID m_pThreadUserData;
    	AFX_THREADPROC m_pUserActionThreadProc;
     
    protected:
    	static UINT CALLBACK ThreadProc(LPVOID lParam);
     
    	CWinThread *m_pThread;
    	bool m_Finished;
     
    	DECLARE_MESSAGE_MAP()
     
    	afx_msg void OnCaptureChanged(CWnd *pWnd);
    	afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
    	afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
    	afx_msg void OnMouseMove(UINT nFlags, CPoint point);
    };
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
     
    IMPLEMENT_DYNAMIC(CStayPressedButton, CButton)
     
    CStayPressedButton::CStayPressedButton()
    {
    	m_pThreadUserData = NULL;
    	m_pUserActionThreadProc = NULL;
    	m_Finished = false;
    	m_pThread = NULL;
    }
     
    CStayPressedButton::~CStayPressedButton()
    {
    	ASSERT(m_pThread==NULL);
    }
     
    BEGIN_MESSAGE_MAP(CStayPressedButton, CButton)
    	ON_WM_CAPTURECHANGED()
    	ON_WM_LBUTTONDOWN()
    	ON_WM_LBUTTONUP()
    	ON_WM_MOUSEMOVE()
    END_MESSAGE_MAP()
     
    void CStayPressedButton::OnCaptureChanged(CWnd *pWnd)
    {
    	if (m_pThread!=NULL)
    	{
    		m_Finished=true;
    		WaitForSingleObject(m_pThread->m_hThread,INFINITE);
    		m_Finished=false;
    		delete m_pThread;
    		m_pThread=NULL;
    	}
     
    	CButton::OnCaptureChanged(pWnd);
    }
     
    void CStayPressedButton::OnLButtonDown(UINT nFlags, CPoint point)
    { 
    	CButton::OnLButtonDown(nFlags, point);
     
    	if(m_pUserActionThreadProc != NULL)
    	{
    		ASSERT(m_pThread==NULL);
     
    		SetCapture();
    		if (GetCapture()==this)
    		{
    			m_pThread=AfxBeginThread((AFX_THREADPROC)ThreadProc, (LPVOID) this, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
    			m_pThread->m_bAutoDelete=FALSE;
    			m_pThread->ResumeThread();
    		}
    	}
     }
     
    void CStayPressedButton::OnLButtonUp(UINT nFlags, CPoint point)
    {
    	CButton::OnLButtonUp(nFlags, point);
     
    	if(m_pThread!=NULL)
    		ReleaseCapture();
    }
     
    void CStayPressedButton::OnMouseMove(UINT nFlags, CPoint point)
    {
    	CButton::OnMouseMove(nFlags, point);
     
    	if(m_pThread!=NULL)
    	{
    		CRect rect;
    		GetClientRect(&rect);
     
    		if( !rect.PtInRect(point) )
    			ReleaseCapture();
    	}
    }
     
    UINT CALLBACK CStayPressedButton::ThreadProc(LPVOID pParam)
    {
    	CStayPressedButton* pBtn = (CStayPressedButton*)pParam;
    	ASSERT_KINDOF(CStayPressedButton, pBtn);
     
    	do
    	{
    		pBtn->m_pUserActionThreadProc(pBtn->m_pThreadUserData);
     
    	} while (!pBtn->m_Finished);
     
    	return 0;
    }
    le code sera plus robuste.

    @+

  3. #3
    Membre éprouvé
    Avatar de Gabrielly
    Inscrit en
    Juin 2004
    Messages
    722
    Détails du profil
    Informations forums :
    Inscription : Juin 2004
    Messages : 722
    Points : 1 128
    Points
    1 128
    Par défaut
    Citation Envoyé par 3DArchi
    ...As-tu fait des tests ? ...
    Oui bien sûr.

    Citation Envoyé par stephdim
    il manque quelque chose dans le code à gabrielly.
    c'est de s'assurer que le thread c'est bien arrêter.
    Le thread s'arrête dès que tu relaches le bouton gauche de la souris ou si cette dernière sort du rectangle du bouton. L'arrêt du thread est certain.

    Citation Envoyé par stephdim
    si l'action est trop longue entre chaque itération, on risque de déclencher plusieurs thread en même temps
    Impossible, il faut que tu relaches d'abord la souris pour lancer un nouveau thread. Mais l'itération peut être longue tout dépend de ce qui est mis dans UINT CMyDlg::UserActionThreadProc(LPVOID pThreadUserData)

    Citation Envoyé par stephdim
    ...de façon que si un autre contrôle vole la capture de la souris...
    comment ça peut se faire quand ton doigt est collé à la souris
    mais je crois que la capture peut être volé d'une façon ou d'une autre et donc
    le OnCaptureChanged peut être ajouté. Thanks

    Citation Envoyé par stephdim
    c'est sûr que si le bouton est relaché il ne sera vu qu'a la prochaine itération, donc avec un léger retard.
    En effet si les itérations sont longues la dernière itération peut co-exister avec la nouvelle d'un autre thread.

  4. #4
    Membre confirmé Avatar de stephdim
    Profil pro
    Inscrit en
    Août 2007
    Messages
    462
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 462
    Points : 521
    Points
    521
    Par défaut
    Le thread s'arrête dès que tu relaches le bouton gauche de la souris ou si cette dernière sort du rectangle du bouton. L'arrêt de thread est certain.
    et si je relâche le bouton et le réappuie dans la foulée, alors que le Thread n'est pas encore passé dans l'itération de test de fin de thread ? en supposant que j'ai le temps, parce qu'une itération dure 1-2 secondes par exemple.

    je démarre alors un deuxieme thread, qui s'arrêtera immédiatement (à cause de l'Event signalé dû au relachement du bouton). Du coup, le premier restera actif (Event en Auto-Reset)

    risque d'avoir à un moment donné, deux threads en parrallèle.

    mais bon, on devrait pouvoir retombé sur ses pattes avec ta solution. un peu scabreux tout de meme

    comment ça peut se faire quand ton doigt est collé à la souris
    il y a d'autres boutons et d'autres doigts

    supposons un drag and drop d'une CListCtrl qui démarre avec le bouton droit par exemple ... ou alors un menu contextuel ... j'ai plein d'exemple

    @+

  5. #5
    Membre éprouvé
    Avatar de Gabrielly
    Inscrit en
    Juin 2004
    Messages
    722
    Détails du profil
    Informations forums :
    Inscription : Juin 2004
    Messages : 722
    Points : 1 128
    Points
    1 128
    Par défaut
    Bon je met fin à l'exécution simultané de deux threads ou plus.
    Voici le correctif :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void CStayPressedButton::OnLButtonUp(UINT nFlags, CPoint point)
    {
    	if(GetCapture())
    	{
    		::SetEvent(m_hFinished);
    		ReleaseCapture();
    		EnableWindow(FALSE);
    	}
    
    	CButton::OnLButtonUp(nFlags, point);
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    void CStayPressedButton::OnMouseMove(UINT nFlags, CPoint point)
    {
    	if(GetCapture())
    	{
    		CRect rect;
    		GetClientRect(&rect);
    
    		if( !rect.PtInRect(point) )
    		{
    			::SetEvent(m_hFinished);
    			ReleaseCapture();
    			EnableWindow(FALSE);
    		}
    	}
    	CButton::OnMouseMove(nFlags, point);
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    UINT CALLBACK CStayPressedButton::ThreadProc(LPVOID pParam)
    {
    	CStayPressedButton* pBtn = (CStayPressedButton*)pParam;
    	ASSERT_KINDOF(CStayPressedButton, pBtn);
    	
    	do
    	{
    		pBtn->m_pUserActionThreadProc(pBtn->m_pThreadUserData);
    	}
    	while(::WaitForSingleObject(pBtn->m_hFinished, 0) != WAIT_OBJECT_0);
    
    	pBtn->EnableWindow(TRUE);
    	return 0;
    }

  6. #6
    Membre confirmé Avatar de stephdim
    Profil pro
    Inscrit en
    Août 2007
    Messages
    462
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 462
    Points : 521
    Points
    521
    Par défaut
    pas bête l'astuce

    @+

Discussions similaires

  1. [FAQ][VC++]Comment créer un auto repeat bouton.
    Par Gabrielly dans le forum Contribuez
    Réponses: 0
    Dernier message: 15/04/2009, 18h57
  2. Réponses: 0
    Dernier message: 15/04/2009, 17h52
  3. Réponses: 0
    Dernier message: 31/10/2008, 10h53
  4. comment créer des boutons sur une feuille en utilisant eclipse
    Par ratamahatta dans le forum Eclipse Java
    Réponses: 1
    Dernier message: 09/06/2006, 19h17
  5. [VBA-E] Comment créer un bouton ds une wksheet par un code vba
    Par moicwill dans le forum Macros et VBA Excel
    Réponses: 3
    Dernier message: 29/05/2006, 17h12

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo