150

J'ai déjà mis le nez plusieurs fois dans les sources de Qt, mais ne connaissant pas du totu Windows, j'ai même pas essayé. Merci beaucoup pour tout ce que t'as fait. Maintenant, la solution pour moi c'est où un tour de magie, ou l'apprentissage de Windows pour faire ça en natif.
Pour apprendre Qt, c'est facile parce que tu parts de l'interface, et tu codes un clic, un survol, un ajustement etc... tu descends dans la complexité, tu découvres les classes une à une.
Pour Windows, ya pas de concepteur d'interface de ce genre, et j'ai pas envie de lire la doc de 150 classes avant d'écrire ma première ligne, pas le courage :/

En tout cas, un grand merci. Je sais pas pourquoi, mais j'ai l'impression que je suis le seul à me mettre dans ce genre de galère, ou totu au moins le seul à ne pas savoir en sortir...

151

Folco (./150) :
Je sais pas pourquoi, mais j'ai l'impression que je suis le seul à me mettre dans ce genre de galère, ou totu au moins le seul à ne pas savoir en sortir...
C'est l'histoire de ta vie, non ? cheeky

GC > tu penses que le subclassing "à la barbare" dont je parlais avant pourrait marcher ? C'est moins propre, mais si la méthode propre ne marche pas...
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo

152

L'histoire de ma vie ? Tu veux dire que je suis constamment en situation d'échec, ou que je vise toujours des trucs pas possibles, trop tordus ou juste trop haut pour moi ? C'est bien probable. Par exemple, j'ai rien compris à ton histoire de subclassing...

153

Ah, et
Call : Kevin Kofler appelé(e) sur ce topic...

C'est toi ZE spécialiste de Qt, non ? cheeky

154

Mais non ! Je disais juste que tu aimes te lancer des défis pas triviaux, c'est tout cheeky

(et puis c'est très bien de se lancer dans des trucs qui ont l'air de te dépasser à priori. C'est comme ça qu'on progresse !)

Le subclassing, c'est grosso-modo comme détourner une interruption. Tu as une fonction, qui s'appelle la "window procedure", qui est appelée lorsqu'un objet reçoit un message à traiter (note : dans ce contexte, "window" est un terme générique pour un objet GUI, ça peut être une fenêtre mais aussi un bouton, une zone de texte, etc.). L'adresse de cette fonction est stockée dans un champ de la structure associée à l'objet. Subclasser, c'est simplement changer ce champ pour mettre à la place l'adresse d'une autre fonction, qui sera du coup appelée à chaque message. Libre à elle ensuite de traiter le message elle-même, d'appeler la window procedure d'origine, de le modifier ou de l'ignorer complètement. L'avantage, c'est que c'est normalement transparent pour le code existant, donc c'est censé marcher sans avoir besoin de décortiquer ce que fait ce code.

Là où c'est différent d'une interruption, et un peu plus subtil, c'est que l'appel à la window procedure ne se fait pas automatiquement en interrompant le thread en cours (tout ce fonctionnement date de l'époque où Windows ne faisait que du multitâche coopératif, donc c'était techniquement impossible). C'est le thread principal qui doit le faire, en exécutant une boucle qui ne s'arrête que quand tu quittes l'appli :
- attente et récupération des messages avec GetMessage
- exécution de la window procedure kivabien avec DispatchMessage

Ça, c'est le fonctionnement "normal" prévu par le système : la boucle de messages ne gère rien elle-même, elle se contente de déléguer. Mais en pratique, un programme peut très bien gérer certains messages directement dans la boucle d'événements elle-même, sans jamais appeler DispatchMessage (donc la window procedure, en fin de compte). Si c'est le cas de Qt, le subclassing ne marchera pas, parce que les messages n'atteindront jamais ta fonction.

Sinon, je vois une autre solution : créer un thread avec sa propre boucle de messages, qui ne fait que gérer la hotkey (en envoyant un signal au thread principal quand il reçoit le message correspondant). À moins que Qt pousse le vice jusqu'à intercepter tous les messages de tous les threads de l'appli, ça devrait fonctionner.

Points à prendre en compte :

- est-ce que ça ne pose pas de problème d'envoyer un signal depuis un autre thread que celui de la GUI ? (à voir dans la doc de Qt)

- il faut un moyen de dire au thread de se terminer (quand on quitte l'appli). Le plus simple est probablement de gérer, en plus de WM_HOTKEY, un autre message (par exemple WM_QUIT) dont la réception va déclencher l'arrêt du thread. Quand on quitte l'appli, le thread principal envoie le message au thread de traitement de la hotkey avec PostThreadMessage, et attend que le thread soit effectivement arrêté avec WaitForSingleObject.

- si tu utilises les fonctions de threading de Qt au lieu des fonctions natives, il faudra récupérer le handle et l'ID natifs du thread, parce que tu en auras besoin pour appeler les fonctions Windows (là aussi, voir dans la doc de Qt).
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo

155

Ah oui génial, pourquoi ne pas mettre ce code dans un thread à part ?
int main(int argc, char *argv[])
{
    if (RegisterHotKey(NULL, 1, MOD_ALT | 0x4000, 0x42))  //0x42 is 'b'
        qDebug() << "Hotkey 'ALT+b' registered, using MOD_NOREPEAT flag";

    MSG msg = {0};
    while (GetMessage(&msg, NULL, 0, 0) != 0)
    {
        if (msg.message == WM_HOTKEY)
            qDebug() << "WM_HOTKEY received";
    }

    return 0;
}
Evidemment ça s'appellerait pas main, mais c'est l'idée quoi. Il me semble que j'ai besoin d'aucune référence du thread principal, ou de la fenêtre principale, non ?
Après, faut que je regarde comment dialoguent les threads, si c'est aussi simple que les signaux entre objets, ça sera fingueurs in ze noze.

Mais je me fais sûrement des idées pour que ça paraisse si simple, non ?

156

Non, c'est l'idée. Le principe est simple, après c'est éventuellement les détails qui peuvent casser les pieds smile

À la réflexion, tu peux te passer de WaitForSingleObject, car Qt a probablement déjà une fonction qui permet d'attendre qu'un thread se soit terminé. Il y a juste PostThreadMessage qui a besoin de l'ID du thread, donc faut trouver comment récupérer ça pour un thread Qt (ou utiliser un thread natif à la place, mais il faudrait vérifier que ça ne pose pas de problème d'appeler des fonctions Qt depuis un thread non-Qt).
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo

157

Si je reste dans les QThreads, il n'y aura que RegisterHotkey qui sera du monde Windows, tout le reste sera Qt : communication, destruction de thread, j'ai pas regardé la doc de QThread, mais il me parait évident qu'il y a tout ça.

Zerosquare, si ça marche, je t'offre une distribution Linux japonaise !

Ah, et un grand merci pour l'explication sur le subclassing. Il s'agit de rediriger un appel de fonction en changeant un pointeur. C'est ce qu'on apprend à faire en asm quand on commence à s'amuser et à réussir à écrire une dizaine de lignes sans bug grin

158

Il te faut quand même un moyen d'indiquer au thread que l'appli va être fermée, et donc qu'il faut qu'il s'arrête. Or vu qu'il ne traitera que des événements Windows, ça veut dire utiliser une fonction Windows pour lui envoyer un message, donc connaître son identifiant Windows.

Tu pourrais tricher en utilisant la méthode Terminate de QThread, mais c'est très crade, et indigne d'un codeur de ton niveau embarrassed

À première vue, pour récupérer l'ID Windows d'un thread Qt, il faut utiliser QThread::currentThreadId(). Malheureusement c'est une méthode statique qui donne seulement l'ID du thread en cours d'exécution, donc tu ne pourras pas récupérer l'ID du thread "hotkey" depuis le thread principal directement. Du coup, il faut que ce soit le thread "hotkey" lui-même qui lise cette info et la renvoie au thread principal d'une manière ou d'une autre.

Attention, si tu envisages une variable globale pour ça, elle doit être déclarée volatile, et il faut aussi un mécanisme de synchronisation pour vérifier que le thread principal n'essaie pas de lire avant que le thread "hotkey" n'ait écrit l'info. Mais de toute façon, si tu regardes la doc de PostThreadMessage, tu verras que les messages ne seront pas traités tant que le thread n'aura pas créé sa boucle de messages. Donc de toute façon, si tu veux faire les choses proprement, il faut que le thread principal vérifie que le thread "hotkey" soit prêt (qu'il ait récupéré son ID et créé sa boucle de message) avant d'essayer de communiquer avec lui ; regarde du côté des objets de synchronisation de Qt.

(Et je veux bien la récompense si tu rayes "distribution Linux" de ta phrase tongue)
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo

159

ok ça marche grin

160

Bon, la suite. Je lis la doc de QThread.
Je tombe donc sur cette méthode : http://doc.qt.io/qt-5/qthread.html#setEventDispatcher
Et là je lis :
This is only possible as long as there is no event dispatcher installed for the thread yet. That is, before the thread has been started with start() or, in case of the main thread, before QCoreApplication has been instantiated
.
Révélation ! J'ai toujours instancié ma QApplication avant d'appeler setNativeEventFilter !
Je modifie donc mon code :
    native_event_filter = new NativeEventFilter;
    QAbstractEventDispatcher* aed = new QAbstractEventDispatcher;
    aed->installNativeEventFilter(native_event_filter);
    QApplication::setEventDispatcher(aed);

    QApplication app(argc, argv);
Seul souci, j'avais pas fait gaffe, mais abstract, ça veut dier abstrait en fait. Cette saloperie d'interface a 20 méthodes virtuelles pures :/

161

./154 > De ce que j'ai lu, QT n'intercepte que les messages sur le "thread en cours" quand le QEventDispatcherWin32 est créé… Maintenant, on peut juste espérer qu'il n'es pas appelé dans un autre thread smile

./160 > Ben oui, c'est une classe abstraite, c'est pour ça que je n'espérais pas que tu te fasses chier à l'implémenter.
(Comme j'ai lu le code de QT, j'ai fortement tendance à penser qu'ils ont fait un truc beaucoup trop compliqué, mais en même temps, j'ai peur qu'il y ait des dépendances aux effets de bord de la méthode qu'ils ont choisi d'utiliser, ça et là dans leur code… Je pense que le jeu n'en vaut pas la chandelle.)

(Maintenant, je suis fortement révolté par l'idée d'un thread dédié… Mais c'est certain que ça fonctionnera.)
avatar
Le scénario de notre univers a été rédigée par un bataillon de singes savants. Tout s'explique enfin.
T'as un problème ? Tu veux un bonbon ?
[CrystalMPQ] C# MPQ Library/Tools - [CrystalBoy] C# GB Emulator - [Monoxide] C# OSX library - M68k Opcodes

162

(je suis d'accord que c'est un peu utiliser un rouleau compresseur pour écraser une mouche, mais vu que la méthode "douce" ne semble pas marcher... et puis Folco m'a promis une récompense, alors zut tongue)
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo

163

Folco (./153) :
Ah, et
Call : Kevin Kofler appelé(e) sur ce topic...
C'est toi ZE spécialiste de Qt, non ? cheeky
Mouais, mais la boucle d'évènements et son intégration avec la boucle d'évènements native, c'est un peu de la magie noire pour moi, surtout sous Windows. sick
Zerosquare (./154) :
- est-ce que ça ne pose pas de problème d'envoyer un signal depuis un autre thread que celui de la GUI ? (à voir dans la doc de Qt)
Les signaux entre threads sont gérés depuis Qt 4.
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

164

Au fait, pourquoi n'utilises-tu pas tout simplement la classe QxtGlobalShortcut? Même si Qxt n'est plus maintenu, la classe ne dépend pas du reste de la bibliothèque. Et sinon, au moins le code donne une idée comment faire.

En regardant le code:
https://bitbucket.org/libqxt/libqxt/src/696423b68972fc9edae318558b8ce26dc187cc40/src/widgets/qxtglobalshortcut.h?at=master
https://bitbucket.org/libqxt/libqxt/src/696423b68972fc9edae318558b8ce26dc187cc40/src/widgets/qxtglobalshortcut_p.h?at=master
https://bitbucket.org/libqxt/libqxt/src/696423b68972fc9edae318558b8ce26dc187cc40/src/widgets/qxtglobalshortcut.cpp?at=master
https://bitbucket.org/libqxt/libqxt/src/696423b68972fc9edae318558b8ce26dc187cc40/src/widgets/win/qxtglobalshortcut_win.cpp?at=master
ils font à peu près comme ta première tentative, mais ils utilisent QAbstractEventDispatcher::instance()->installNativeEventFilter(this); pour installer le filtre. (C'est la solution à ton problème du ./160.)
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

165

Un grand merci. Je pars en vacances pour une semaine, on verra quand on continuera à plancher sur la question happy

166

Bonnes vacances !

(tu vas nous manquer, du coup qui va poster dans les topics de bon matin ? embarrassed)
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo

167

Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
Excellent, ce genre de macro qui sert à avoir un code bien propre. smile

explicit QxtGlobalShortcut(const QKeySequence& shortcut, QObject* parent = 0);
J'ai encore raté un truc, ou l'explicit est inutile ? Ou c'est à cause de l'argument par défaut ?

168

(tu es revenu de vacances ?)
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo

169

(Non, mais tout le monde dort encore et ça me démange grin)

170

Folco (./167) :
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
Excellent, ce genre de macro qui sert à avoir un code bien propre. smile
Mais cette macro ne permet pas de simuler une propriété (obj->enabled) au niveau C++, tu as seulement obj->property("enabled") (qui sera aussi nettement plus lent que obj->isEnabled()). Le système de propriétés sert, d'un pour des interfaces comme Qt Designer, et de deux pour le QML.
explicit QxtGlobalShortcut(const QKeySequence& shortcut, QObject* parent = 0);J'ai encore raté un truc, ou l'explicit est inutile ? Ou c'est à cause de l'argument par défaut ?
Deuxième solution. C'est utile à cause de l'argument par défaut.
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

171

Merci. smile

172

Bon et bien merci à tous, Kevin c'est toi qui avait raison, les sources de Qxt m'ont fourni la solution, il faut bien faire le setNativeEventFilter dans le constructeur de NativeEventFilter.

Immense merci à tous pour votre temps passé à l'aider. top

De manière très étonnante, j'ai aussi viré le if (msg == "windows_machin"), sans quoi ça ne marche pas.
Etonamment aussi, l'auteur de Qxt retourne toujours false, ce qui semble en contradiction avec la doc. Mais ça marche, à creuser.

173

(et ma récompense alors ? cry)
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo

174

tiens : #japonaise# smile

Est-ce qu'on peut faire une connection de ce genre :
MainWindow::MainWindow()
{
    ActionNewGame = new QAction(this);
    connect(ActionNewGame, &QAction::triggered, Controller::get(), &Controller::newGame);
get() est une méthode statique qui renvoie l'instance de Controller.
newGame() est une méthode publique non-statique.

Je me prends un segfault au connect(), alors que c'est le tout début de mon programme. J'ai essayé de sortir l'appel à la méthode static de la connection, avec Controller* c = Controller::get();, puis connexion avec c; Même résultat.
L'instance du contrôlleur est bien créée, je l'ai vérifié.

Quand j'essaye l'ancienne syntaxe : connect(ActionNewGame, SIGNAL("triggered"), Controller::get(), SLOT("newGame"));, j'ai cette erreur au runtime : QObject::connect: Cannot connect QAction::"triggered" to (null)::"newGame"

Qu'est-ce que je rate encore ? Je croyais en avoir déjà fait, des connexions comme ça...

175

C'est bizarre, ça m'a l'air correct a priori.
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

176

Je t'aurais bien donné la réponse, mais j'attendais autre chose qu'un simple smiley, qui en plus ne marche même pas embarrassed

non, en fait j'en sais rien du tout, désolé !
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo

177

(sans blague grin)

Kevin -> la source est là, si jamais quelque chose te sautait aux yeux : https://github.com/Folcogh/FMetro
Je cherche de mon côté. Merci d'avoir répondu. Ce que je trouve sur google à propos des méthodes statiques ne m'aide pas...

178

Controller n'est pas encore créé à cet endroit (cf. https://github.com/Folcogh/FMetro/blob/master/main.cpp#L9), donc Controller::get() retourne nullptr.
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

179

Mais quel foutu abruti... J'avais un vrai pattern de singleton au début, je l'ai viré sûrement pour une raison à la con sick
Merci beaucoup. hehe

180

Tiens, nouveau problème lors de l'établissement d'une connexion entre un QPushButton et une méthode non-statique de ma MainWindow :StartPage::StartPage(QWidget *parent) : QWidget(parent), ui(new Ui::StartPage) { ui->setupUi(this); connect(ui->buttonNewGame, &QPushButton::clicked, mainwindow, &MainWindow::newGame); }mainwindow est une variable globale, qui pointe sur ma MainWindow classique. Elle est initialisée dans main().
Et ça crashe dans qobject.h :
        return connectImpl(sender, reinterpret_cast<void **>(&signal),
                           receiver, reinterpret_cast<void **>(&slot),
                           new QtPrivate::QSlotObject<Func2, typename QtPrivate::List_Left<typename SignalType::Arguments, SlotType::ArgumentCount>::Value,
                                           typename SignalType::ReturnType>(slot),
                            type, types, &SignalType::Object::staticMetaObject);
Ca compile sans problème, mais ça segfault à la connexion. Comme le code me parait tout à fait lambda, je sais pas vers où chercher sad

edit -> Bon, j'ai résoudru le problème avec un singleton pour MainWindow, mais sans savoir pourquoi ça déconnait avant sad