jak przekazywać zdarzenia QEvent z obiektu podrzędnego do nadrzędnego?

0

Zrobiłem sobie przykładowy kod przekazywania zdarzeń z obiektów podrzędnych do obiektów nadrzędnych.

Co chcę uzyskać?
najbardziej interesuje mnie poprawna obsługa zdarzenia QEvent::MouseButtonDblClick i odbieranie pozostałych zdarzeń bez modyfikacji, gdyż chcę zrobić kalendarz gdzie jak kliknę dwa razy myszką w dowolną datę to odpali mi się jakieś okno z tabelą - w sumie wywoła mi się na przykład slot z oknem.

Co mam? (ale to testy, bo chcę zrozumieć jak to działa)
Wychodzi na to, że jak dodam jakiś dowolny obiekt nadrzędny QWidget to jakby on przysłaniał zdarzenia z obiektu podrzędnego i te zdarzenia z obiektu podrzędnego dalej nie są odbierane - DLACZEGO?

Tutaj wyjaśnienie co mam w kodzie

mam klasę ChildWidget

class ChildWidget : public QWidget

ciało metody tej klasy jest następująca - tutaj chcę przekazać double Click do obiektów wyżej

void ChildWidget::mouseDoubleClickEvent(QMouseEvent *event)
{
    emit mouseDoublePressed(event);
    QWidget::mouseDoubleClickEvent(event);
}

już na tym etapie napiszę, że to działa do momentu gdy nie stworzę nowej klasy z drugim obiektemQWidget jakby to był "rodzic" (w sumie wszystkie obiekty w Qt są QWidgetami

class ParentWidget : public QWidget

a tak odbieram QEvent w klasie ParentWidget

ParentWidget::ParentWidget(QWidget *parent) : QWidget(parent)
{
    child = new ChildWidget(this);
    QObject::connect(child, &ChildWidget::mouseDoublePressed, this, &ParentWidget::handleMouseDoublePress);
}

void ParentWidget::handleMouseDoublePress(QMouseEvent *event)
{
    if(event->type()==QEvent::MouseButtonDblClick){
        qInfo()<< "double click";
    }
}

i całość działa. Pojawia się malutkie okienko i jak kliknie się w to okno dwa razy myszką, to przekazywanie zdarzenia działa... DO MOMENTU jak nie zmieni się rozmiaru okna i wtedy nie działa. Czyli gdzieś na jakimś etapie zdarzenie nie jest przekazywane.

Czy ktoś z was wie jak rozwiązać ten problem? Chcę zrozumieć jak poprawnie przekazywać mouseDoubleClickEvent(QMouseEvent *event) i inne zdarzenia znajdujące się w klasie QWidget - na moją logikę te zdarzenia powinny działać, bo w końcu QCalendarWidget dziedziczy po QWidget, a ten zawiera metodę mouseDoubleClickEvent(QMouseEvent *event), a mimo to nie działa to jak trzeba.

kod znajduje się na GitLabie

0

dobra, doszedłem co było nie tak. Kurde ale te Qt jest dziwne...

spójrzcie na screen i czerwony prostokąt - tak, to kurde Widget podrzędny. Przez przypadek odkryłem, że skubaniec nie zmieniał rozmiaru i podczas działania programu on reaguje na podwójne kliknięcia ale w obszarze czerwonego prostokąta, poza tym obszarem podwójny click nie istnieje...

screenshot-20231230230746.png

jak rozwiązać problem? W obiekcie nadrzędnym tj rodzic należy nadpisać metodę

virtual void resizeEvent(QResizeEvent *event) override;

i jak to zrobiłem?

void ParentWidget::resizeEvent(QResizeEvent *event)
{
    QWidget::resizeEvent(event);

    int oldWidth = event->oldSize().width();
    int oldHeight = event->oldSize().height();

    if(oldWidth >= 0 && oldHeight >= 0){
        child->resize(child->width() + event->size().width() - oldWidth,
                      child->height() + event->size().height() - oldHeight);
    }
}

po odpaleniu całość wygląda tak

screenshot-20231230231119.png

i teraz "dwuklik" myszą działa. Ważna uwaga, w obiekcie rodzic nie stosować funkcji

void resize(int w, int h);

gdyż to spowoduje, że obiekt podrzędny będzie miał "swój" rozmiar i jak okno powiększycie, to ten prostokąt owszem będzie się powiększał ale będzie mniejszy. Póki co, nie wiem jak rozwiązać ten problem.

Chyba problem "solved". Teraz pozostaje ogarnąć ten kalendarz.

1

To nie Qt jest dziwne, to raczej przenoszenie rozwiązań z innego systemu do Qt bez zrozumienia Qt,
ja bym obejrzał przykłady Qt , bo użycie w Twoim przypadku resizeEvent to trochę armata na muchę jeżeli możesz użyć layoutu (jak sugeruje @kq)

0
Marius.Maximus napisał(a):

To nie Qt jest dziwne, to raczej przenoszenie rozwiązań z innego systemu do Qt bez zrozumienia Qt,

nie rozumiem co masz na myśli, że "jest to przenoszenie rozwiązań z innego systemu do Qt"? Czytając dokumentację, oni wprost nawet stwierdzają, że lepiej używać QEvent niż systemu sygnał-slot

ja bym obejrzał przykłady Qt , bo użycie w Twoim przypadku resizeEvent to trochę armata na muchę jeżeli możesz użyć layoutu (jak sugeruje @kq)

wiem ale jakoś musiałem zrozumieć czym są QEvent - póki co, to umiem go użyć w niektórych przypadkach - ale najbardziej chciałbym poznać śmieszka, który zaimplementował mouseDoubleClickEvent który owszem działa ale na belce jak zaznaczono na screenie

screenshot-20240102093633.png

a każdy myśli, że to nie działa, przekopałem tyle neta, że szkoda gadać, bo każdy kto szuka tej informacji, co ja szukałem, to klika w datę... ten śmieszek, który w ten sposób to zrobił powinien mieć specjalne miejsce, gdzie każdy mógłby dać mu plaskacza za taki "pomysł". Owszem, da się to obejść, ale trzeba nadpisać WSZYSTKIE events za pomocą funkcji event(QEvent *event)

Nadal uważasz, że pomysł nie jest "dziwny"?

Bo co mi daje "dwuklik" w belce kalendarza? Na co mi taki "niedopracowany" dwuklik?

0

Ja bym zachęcił do obejrzenia źródeł komponentu który chcesz używać w tym przypadku: qcalendarwidget.cpp
tam są wszystkie odpowiedzi np. ta związana z działaniem mouseDoubleClickEvent

1
zkubinski napisał(a):

dobra, doszedłem co było nie tak. Kurde ale te Qt jest dziwne...

spójrzcie na screen i czerwony prostokąt - tak, to kurde Widget podrzędny. Przez przypadek odkryłem, że skubaniec nie zmieniał rozmiaru i podczas działania programu on reaguje na podwójne kliknięcia ale w obszarze czerwonego prostokąta, poza tym obszarem podwójny click nie istnieje...

A niby czemu miałby zmieniać rozmiar? Jak ma zmieniać rozmiar to trzeba wyjaśnić bibliotece w jaki sposób ma się to odbywać (bo logicznych sposobów jest bez liku).
Qt Widgets najlepiej stosować QLayout (czyli coś co bierze odpowiedzialność za rozmieszczenie widgetów w innym widget).


ParentWidget::ParentWidget(QWidget *parent) : QWidget(parent)
{
    auto layout = new QVBoxLayout(this);
    child = new ChildWidget(this);
    layout->addWidget(child);
    QObject::connect(child, &ChildWidget::mouseDoublePressed, this, &ParentWidget::handleMouseDoublePress);
}

To powinno wypełnić całego rodzica.

Co ty właściwie chcesz zrobić? Czytam i widzę dużo narzekania, że nie działa jak chcesz, ale nie jestem w stanie zrozumieć co właściwie chcesz osiągnąć?
Na dodatek nagle wątek przeskoczył na temat kalendarza, gdzie wcześniej były dwa proste widgety.

0
MarekR22 napisał(a):

Co ty właściwie chcesz zrobić? Czytam i widzę dużo narzekania, że nie działa jak chcesz, ale nie jestem w stanie zrozumieć co właściwie chcesz osiągnąć?
Na dodatek nagle wątek przeskoczył na temat kalendarza, gdzie wcześniej były dwa proste widgety.

wiem, że może to być dziwne i że wątek skoczył na temat kalendarza (ale w sumie pisałem o tym podczas pierwszego posta).

żebyś zrozumiał dlaczego ten wątek zszedł na temat kalendarza to wyjaśnię, że od początku na kalendarzu chciałem nadpisać funkcję mouseDoubleClickEvent(QMouseEvent *event)

więc czytając dokumentację, ta funkcja jest podstawową składową QWidget <- QCalendarWidget - ta klasa dziedziczy tą funkcję. a skoro dziedziczy, to musi działać.... ale o tym dalej...

i w mojej klasie dziedziczącej po QCalendarWidget chciałem tą funkcję nadpisać, bo moim zamiarem było uzyskanie takiego efektu, że jak kliknę w dowolną datę 2x lewym buttonem myszy, to uruchomi mi się wyskakujące okno z tabelą aby w tej tabeli wpisać np "datę umówionego spotkania" i inne informacje

gdy podklasowałem sobie klasę QCalendarWidget czyli zrobiłem coś takiego

class MyCalendar : public QCalendarWidget
{
  public:
  //some class members 
  virtual void 	mouseDoubleClickEvent(QMouseEvent *event) override;
};

to nagle się okazało, że mouseDoubleClickEvent nie działa gdy klikniesz "2x prawym myszy" w dowolną datę kalendarza - więc jak to do jasnej ciasnej z tym jest? Albo działa albo nie?
I tak po paru dniach szukania założyłem ten wątek w którym postanowiłem sprawdzić czy na innych widgetach które dziedziczą po QWidget sytuacja jest taka sama. Myślałem, że jest taka sama, bo przez przypadek odkryłem, że mouseDoubleClickEvent działa, a moim problemem było to, że widget potomny nie zmienił rozmiaru do rozmiaru rodzica.

Efekt? Dzięki temu doświadczeniu zacząłem klikać na belce kalendarza i odkryłem, że mouseDoubleClickEvent działa ale tylko na górnej belce kalendarza jak zaznaczyłem kilka postów wyżej - a śmieszek, który tak to zaimplementował powinien otrzymać "nagrodę"

I taki był cel tego posta - wiem, że ten post wygląda dziwnie ale kurde w jakiś sposób trzeba dojść do rozwiązania w szczególności, że google, SO na ten temat mówią, że "to nie działa"... - działa, tyko nie tak, jak wszyscy domyślnie tego oczekują... i wśród tych ludzi byłem ja. Ale doszedłem co jest nie tak dzięki temu, że zrobiłem przykład podany w tym wątku.

0

@zkubinski: QCalendarWidget uzywa w prywatnych elementach mouseDoubleClickEvent wiec tak jak to robisz nie zadziała (dlatego sugerowałem zajrzenie do źródeł)

opcje to skopiować i przerobić
albo dodać qApp->installEventFilter i tam poszukać

1

Też mam trudność aby zrozumieć cóż masz Bracie @zkubinski na myśli; sugerując się jednak tytułem wątku mniemam, iż chodzi o sytuację, kiedy jakiś event, np. QMouseDoubleClick event, po obsłużeniu w widgecie powinien być przesłany do jego rodzica. Temat to zgłębienia w dokumentacji "Qt events propagation", dodatkowo może z filtrami eventów, o czym Brat @Marius.Maximus napomyka.

W skrócie mówiąc, jeśli chcesz powiadomić Qt iż dany event został obsłużony w twoim widgecie, i nie ma potrzeby słać go do rodziców to robisz coś w deseń:

void MyWidget::mousePressEvent(QMouseEvent *event)
{
  if (chcesz aby event był wysłany dalej, do rodziców widgeta)
  {
    //robisz, to co potrzebujesz przed wysłaniem eventu dalej
    event->setAccepted(false);//a tak oznaczasz event dla Qt że "nie został" obsłużony, i niech spróbuje go posłać do widgetów będących bliżej korzenia drzewa widgetów
  }
  else
  {
    //robisz coś
    event->setAccepted(true);//taki event nie będzie propagowany do parentów
  }
}
0
MasterBLB napisał(a):

Też mam trudność aby zrozumieć cóż masz Bracie @zkubinski na myśli; sugerując się jednak tytułem wątku mniemam, iż chodzi o sytuację, kiedy jakiś event, np. QMouseDoubleClick event, po obsłużeniu w widgecie powinien być przesłany do jego rodzica. Temat to zgłębienia w dokumentacji "Qt events propagation", dodatkowo może z filtrami eventów, o czym Brat @Marius.Maximus napomyka.

W skrócie mówiąc, jeśli chcesz powiadomić Qt iż dany event został obsłużony w twoim widgecie, i nie ma potrzeby słać go do rodziców to robisz coś w deseń:

void MyWidget::mousePressEvent(QMouseEvent *event)
{
  if (chcesz aby event był wysłany dalej, do rodziców widgeta)
  {
    //robisz, to co potrzebujesz przed wysłaniem eventu dalej
    event->setAccepted(false);//a tak oznaczasz event dla Qt że "nie został" obsłużony, i niech spróbuje go posłać do widgetów będących bliżej korzenia drzewa widgetów
  }
  else
  {
    //robisz coś
    event->setAccepted(true);//taki event nie będzie propagowany do parentów
  }
}

ideę rozumiem ale jak go odebrać "dalej"? W sensie w innym widgecie? dasz jakiś przykład jak widget przesyła event do następnego widgeta?

1

Tak samo — void WidżetWyżej::mousePressEvent(QMouse Event *event) i działasz. Przykład przesyłania eventa do nadrzędnego widgeta zacytowałeś sam. Ja bym użył ignore(), celem poprawy czytelności, ale i setAccepted(false) działa.

W normalnych warunkach¹ Qt propaguje eventy w górę (od dzieci do rodziców), aż nie napotka czegoś, co zaakceptuje event. Jak nic nie znajdzie, to event przepada. Dzieje się to automatycznie, nie musisz nic więcej robić.


¹ Są wyjątki, np. closeEvent po ignore() przepada.

0

@zkubinski: tak z ciekawości , temat ogarnięty ? Zrobiłeś własny Widżet do daty, czy zrezygnowałeś z DbClick ?

1 użytkowników online, w tym zalogowanych: 0, gości: 1