Catch mouse event on tree widget item in QTreeWidget - mouseevent

In tree widget I have following signal connected:
connect(mTreeWidget, SIGNAL(itemClicked(QTreeWidgetItem*, int)),
SLOT(onItemClicked(QTreeWidgetItem*, int)));
where onItemClicked() slot is following:
void WidgetBox::onItemClicked(QTreeWidgetItem *item, int )
{
int index = getPageIndex(item);
setCurrentIndex(index);
}
int WidgetBox::getPageIndex(QTreeWidgetItem *item)
{
if (!item) return -1;
QTreeWidgetItem *parent = item->parent();
if(parent) // Parent is top level item
{
return mTreeWidget->indexOfTopLevelItem(parent);
}
else // Current item is top level
{
return item->treeWidget()->indexOfTopLevelItem(item);
}
}
void WidgetBox::setCurrentIndex(int index)
{
if (index != currentIndex() && checkIndex(index))
{
mTreeWidget->setCurrentItem(mTreeWidget->topLevelItem(index));
emit currentIndexChanged(index);
}
}
However I can't catch itemClicked() signal and onItemClicked() never executed because top level items has push button widget (set with setItemWidget() method) which intercepts mouse event and child items contain container widgets which may have any widget combinations in them.
Is there a good method here to invoke this itemClicked() signal for both top level and child items of tree widget?
installEventFilter() for all widgets found in an item by something like following:QList<QWidget *> widgets = parentWidget.findChildren<QWidget *>();?
Or establish mouse event propagation somehow?
QCoreApplication::postEvent()?
How to organize such process better so all widgets process mouse event as they need to and TreeWidget issue SIGNAL(itemClicked()) as well?
Full sources to reproduce: https://github.com/akontsevich/WidgetBox

So solution is simple and following - just re-send void itemClicked(QTreeWidgetItem *item, int column); signal in PageEventFilter:
PageEventFilter::PageEventFilter(QObject *parent, QTreeWidgetItem *item)
: QObject(parent)
, mItem(item)
{
connect(this, SIGNAL(itemClicked(QTreeWidgetItem*,int)),
mItem->treeWidget(), SIGNAL(itemClicked(QTreeWidgetItem*,int)));
}
bool PageEventFilter::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::MouseButtonPress)
{
// Resend signal to QTreeWidget
emit itemClicked(mItem, 0);
return false; // Send event to the object (do not filter it)
}
else
{
// standard event processing
return QObject::eventFilter(obj, event);
}
}
P.S. Will leave previous answer as well if somebody needs code or idea.

Created event filter like this:
class PageEventFilter : public QObject
{
Q_OBJECT
public:
PageEventFilter(QObject *parent, QTreeWidgetItem *item);
protected:
bool eventFilter(QObject *obj, QEvent *event);
private:
QTreeWidgetItem *mItem;
};
bool PageEventFilter::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::MouseButtonPress)
{
// Emitate mouse event for parent QTreeWidget
QMouseEvent *oldEvent = (QMouseEvent *)event;
QRect itemRect = mItem->treeWidget()->visualItemRect(mItem);
QPointF mousePos(itemRect.x() + 1, itemRect.y() + 1);
QMouseEvent *newEvent = new QMouseEvent(oldEvent->type(),
mousePos,
oldEvent->button(),
oldEvent->buttons(),
oldEvent->modifiers());
QCoreApplication::postEvent(mItem->treeWidget(), newEvent);
return false; // Sent event to the object (do not filter it)
}
else
{
// standard event processing
return QObject::eventFilter(obj, event);
}
}
Install it to a button in tree widget item. It creates and sends (imitates) mouse event to tree widget, however tree widget still does not send itemClicked(QTreeWidgetItem*, int) signal. What is the problem could be? Wrong mouse pos? Tried to send event to viewport() - no luck as well. Any way to solve this?

Related

Click a QPushbutton in a QWidget will cause focusOutEvent, how to ignore this?

In my code when focusOutEvent slot calls, this widget will be closed. While, I came cross a problem that click a QPushbutton in this current widget will also cause focusOutEvent and button clicked() signal can never be triggered any more.
In construct function:
connect(btn, SIGNAL(clicked()), this, SLOT(btnClickFucntion()));
then, I have function like following:
void AWidget::mouseReleaseEvent(QMouseEvent *event)
{
Q_UNUSED(event);
this->close();
}
void AWidget::focusOutEvent(QFocusEvent *event)
{
if (!this->isHidden())
{
event->accept();
this->close();
}
else
event->ignore();
}
void AWidget::btnClickFucntion()
{
//Do something
}
However, btnClickFucntion{} never called.
bool AWidget::isFocusChange2BtnInside()
{
QPushButton *btn = dynamic_cast<QPushButton *>(this->focusWidget());
if (btn == this->btn)
return true;
else
return false;
}
I add this function as a condition(if true, return; else go next step) before calling close() function, and fix it.
Sorry to bother.

Changed event for JavaScript Binding in Qml?

I have a property which is defined by a JavaScript expression (currentContainer):
Item {
id: theContainer
property alias currentIndex: theListView.currentIndex
signal onCurrentIndexChanged()
property MyCustomCppContainer currentContainer: {
if(theListView.currentIndex >= 0)
theModel.getCustomContainer(theListView.currentIndex)
else
null
}
signal onCurrentContainerChanged() // nobody calls this signal (yet)
MyCustomCppModel {
id: theModel
}
ListView {
id: theListView
anchors.fill: parent
model: theModel
currentIndex: -1
onCurrentIndexChanged: theContainer.onCurrentIndexChanged()
/* Other properties stripped for example */
}
}
Sadly I always get the last selected container, not the currently selected one:
ContainerItem {
onCurrentIndexChanged: {
//On first change, currentContainer is null
//though the first one was selected
//After selecting the second entry
//I get the result I expected last time
console.log(currentContainer.name);
}
}
I think a solution would be to have another signal for currentContainer: onCurrentContainerChanged().
But who calls this special signal?
I could solve this using a C++ helper class:
class PropertyChangedHelper : public QObject
{
Q_OBJECT
Q_PROPERTY(QVariant theProperty WRITE setTheProperty NOTIFY thePropertyChanged)
public:
PropertyChangedHelper(QObject* parent = nullptr) : QObject(parent) {}
virtual ~PropertyChangedHelper() {}
void setTheProperty(QVariant) {
Q_EMIT thePropertyChanged();
}
Q_SIGNALS:
void thePropertyChanged();
private:
Q_DISABLE_COPY(PropertyChangedHelper)
};
Usage is quite simple:
PropertyChangedHelper {
theProperty: containerItem.currentContainer
onThePropertyChanged: {
console.log(containerItem.currentContainer.name);
}
}
I am not sure if this violates any Qml/QtQuick philosophies, but it works.

XAML: Tap a textbox to enable?

In XAML and WinRT, Is there a way to set up a textbox so that it is disabled for text input until it is tapped.
I tried setting up the Tapped event and then setting the IsEnabled=true, but that only seems to work if the IsEnabled=true in the first place.
I found this on MSDN:
http://social.msdn.microsoft.com/Forums/en-US/winappswithcsharp/thread/708c0949-8b06-40ec-85fd-201139ca8b2d
Talks about adding the TappedEvent manually to the event handled for each TextBox, which is cumbersome, but also doesn't seem to work unless IsEnabled was already set to true.
Basically, I want a form where all textboxes display data but are disabled unless the user taps to enable the box and then type.
You can use IsReadOnly instead of IsEnabled to achieve what you are looking for. In addition, you can set up the tapped event handlers in code easily. I'm not sure if setting up handlers in code is a requirement for this to work, as you noted above; however, it does simplify things.
Here are the details.
In the constructor of your page class (here it is MainPage), call the setup function:
public MainPage()
{
this.InitializeComponent();
// call the setup for the textboxes
SetupTextBoxes();
}
Here is where we do the magic - make all textboxes on this page readonly and set up tap handler:
private void SetupTextBoxes()
{
var tbs = GetVisualChildren<TextBox>(this, true);
foreach (var tb in tbs)
{
tb.IsReadOnly = true;
tb.AddHandler(TappedEvent, new TappedEventHandler(tb_Tapped), true);
}
}
Utility function to get a list of all children of the given type (T) of the passed in parent.
private List<T> GetVisualChildren<T>(DependencyObject parent, bool recurse = true)
where T : DependencyObject
{
var children = new List<T>();
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
DependencyObject v = (DependencyObject)VisualTreeHelper.GetChild(parent, i);
var child = v as T;
if (child == null && recurse)
{
var myChildren = GetVisualChildren<T>(v, recurse);
children.AddRange(myChildren);
}
if (child != null)
children.Add(child);
}
return children;
}
Finally, the event handler. This enables each textbox when tapped.
private void tb_Tapped(object sender, TappedRoutedEventArgs e)
{
((TextBox)(sender)).IsReadOnly = false;
}

Pause application in QML when app is in background Symbian

I want to know of any pure QML way to find out whether the application is in the background or not and then accordingly stop or play music. In meego the alternate way to do is through the PlatformWindow Element but it does not exist in Symbian QML. Help needed please
Finally I got it working :) and i did it though Qt way... here are the steps
1) Create a class MyEventFilter
class myEventFilter : public QObject
{
bool eventFilter(QObject *obj, QEvent *event) {
switch(event->type()) {
case QEvent::WindowActivate:
emit qmlvisiblechange(true);
qDebug() << "Window activated";
bis_foreground=true;
return true;
case QEvent::WindowDeactivate:
emit qmlvisiblechange(false);
qDebug() << "Window deactivated";
bis_foreground=false;
return true;
default:
return false;
}
}
void dosomething();
private:
int something;
public:
bool bis_foreground;
Q_OBJECT
public slots:
Q_INVOKABLE QString checkvisibility() {
if (bis_foreground==true) return "true";
else return "false";
}
signals:
void qmlvisiblechange(bool is_foreground);
};
2) Then in main.cpp include this file include the class and add setContext propery like this
context->setContextProperty("myqmlobject", &ef);
3) in qml file call it like this:
Item {
id: name
Connections
{
target:myqmlobject
onQmlvisiblechange:
{
if(is_foreground)
{
//dont do anything...
}
else
{
playSound.stop()
}
}
}
}
Enjoy :)
Why do you need a pure QML way?
You can detect if an application has been sent to the background by installing an event filter.
Check: http://www.developer.nokia.com/Community/Wiki/Detecting_when_a_Qt_application_has_been_switched_to_the_background_and_when_resumed
For a "pure" QML way, there is the Symbian QML element:
http://doc.qt.nokia.com/qt-components-symbian/qml-symbian.html
It has a foreground property that indicates whether the app is in the foreground or in the background. You can try connecting to onForegroundChanged.
From the documentation, the Symbian element is not "creatable". It exists as a context property named symbian. So a sample usage would be:
import QtQuick 1.1
import com.nokia.symbian 1.1
PageStackWindow {
id: window
initialPage: MainPage {tools: toolBarLayout}
showStatusBar: true
showToolBar: true
function appForegroundChanged() {
console.log("Foreground: " + symbian.foreground)
}
function appCurrentTimeChanged() {
console.log("Current time: " + symbian.currentTime)
}
Component.onCompleted: {
symbian.currentTimeChanged.connect(appCurrentTimeChanged)
symbian.foregroundChanged.connect(appForegroundChanged)
}
ToolBarLayout {
id: toolBarLayout
ToolButton {
flat: true
iconSource: "toolbar-back"
onClicked: window.pageStack.depth <= 1 ? Qt.quit() : window.pageStack.pop()
}
}
}

Events when dock is showing or hiding

How can I get events when the Dock is showing or hiding?
You can get a notification if the dock is visible or not using Carbon. I do not know of any way to do it in Cocoa.
(I haven't tested this; it's from the code here)
Create your callback method:
#import <Carbon/Carbon.h>
static const EventTypeSpec appEvents[] = {
{ kEventClassApplication, kEventAppSystemUIModeChanged }
};
OSStatus DockChangedHandler(EventHandlerCallRef inCallRef, EventRef event, void *userData) {
OSStatus status = eventNotHandledErr;
switch(GetEventClass(event)) {
case kEventClassApplication: {
SystemUIMode *outMode;
SystemUIOptions *outOptions;
GetSystemUIMode(outMode, outOptions);
status = noErr;
}
break;
default:
return;
}
/*Insert whatever you want to do when you're notified of a dock change*/
return status;
}
And then put this wherever you want to start listening for the notification:
InstallApplicationEventHandler(NewEventHandlerUPP(DockChangedHandler), GetEventTypeCount(appEvents), appEvents, 0, NULL);
Further information: http://developer.apple.com/library/mac/#technotes/tn2002/tn2062.html