Customising location-sensitive context menu in QTextEdit - python

I am trying to adjust the context menu in a QTextEdit. I have succeeded in getting access to and then displaying the default menu with the following code:
class LinkTextBrowser(QTextBrowser):
def contextMenuEvent(self, event):
menu = self.createStandardContextMenu(event.pos())
# do stuff to menu here
menu.popup(event.globalPos())
However, this does not work for location-sensitive clicks. The case in question is the "Copy Link Location" item in a QTextBrowser's right click menu, which is only enabled if you right click on a link, for obvious reasons. I can't get it to ever be enabled. I suspect I am passing the wrong position to createStandardContextMenu, but I can't figure out the correct position to feed it.
I have tried both event.globalPos() and event.pos(), neither of which work. I also looked at the source code for QTextEdit, but didn't get anywhere. What position is it expecting?
Edit: Update: It appears the problem is the scrolling in the TextBrowser; if I scroll to the top of the window and use event.pos() it behaves. I don't have working code yet, but correcting for the scroll is the solution.
(Specifically, I want to disconnect the signal emitted by the Copy Link Location action and connect it to my own function so I can adjust the URL before copying it to the clipboard, allowing me to make links absolute and so forth before copying, and I have no particular desire to re-write the working bits.)

Here is the working transform of the coordinates:
class LinkTextBrowser(QTextBrowser):
def contextMenuEvent(self, event):
self.link_pos = event.pos()
# correct for scrolling
self.link_pos.setX(self.link_pos.x() + self.horizontalScrollBar().value())
self.link_pos.setY(self.link_pos.y() + self.verticalScrollBar().value())
menu = self.createStandardContextMenu(self.link_pos)
# do stuff to menu
menu.popup(event.globalPos())

Try self.mapToGlobal(event.pos()), it should take into account scroll position.

Maybe you can try something like:
QMenu *menu = new QMenu();
menu->addAction(...);
menu->exec(textEdit->mapToGlobal(pos));
It's C++ but I'm sure that you can easy convert it to python.

Related

PyQt: syncing scroll in 2 different qlistwidgets?

I have 2 different QListwidgets which scrolls are synced properly with the code below. The problem is the secondary Qlistwidget (the one that is following the other´s scroll) doesn´t update its items unless i hover over it with the mouse.
I have very little experience with pyqt, so the only thing i tried is using the repaint and update methods (i added both in the code, at the end of the function). Unfortunately nothing really happens.
def move_scrollbar(vs, value):
vs.blockSignals(True)
vs.setValue(value)
vs.blockSignals(False)
self.list1.repaint()
self.list2.repaint()
self.list1.update()
self.list2.update()
vs1 = self.list1.verticalScrollBar()
vs2 = self.list2.verticalScrollBar()
vs1.valueChanged.connect(partial(move_scrollbar, vs2))
vs2.valueChanged.connect(partial(move_scrollbar, vs1))
I expect the view of the secondary or linked QListwidget update the same way to the primary (the one which i actually scroll) does.
This works for me:
self.vs1 = self.list1.verticalScrollBar()
self.vs2 = self.list2.verticalScrollBar()
self.vs1.valueChanged.connect(self.move_scrollbar)
self.vs2.valueChanged.connect(self.move_scrollbar)
def move_scrollbar(self, value):
self.vs1.setValue(value)
self.vs2.setValue(value)

Changing icon in nested menus

I don't think this may be posssible but I had still want to try asking.
In the attached screenshot, I have nested menus.
Is it possible to change the arrow keys icon as 'highlighted' by the red box?
I am trying to change the arrow key to a plus icon if there are no sub menu items found.
The default arrow can be in use if there are sub menu items found.
Yes, you can change the color of right-arrow.
But there is a trick to change it.
The truth of indicator is "branch-closed png file"
You can see the png file at the almost bottom on the page in the link.
So, it can not be solved by the pure-programmic way.
You prepare the picture in advance by yourself.
and please following code in QMenu constructor.
self.setStyleSheet("QMenu::right-arrow{image:url(stylesheet-branch-closed-red.png);}")
Attention:
stylesheet-branch-closed-red.png is my renamed picture.
You can download the original picture from the above link page.
you right-click the png picture and save as name.
This code comes from your past question.
class QCustomMenu(QtGui.QMenu):
"""Customized QMenu."""
def __init__(self, title, parent=None):
super(QCustomMenu, self).__init__(title=str(title), parent=parent)
self.setup_menu()
self.setStyleSheet("QMenu::right-arrow{image:url(stylesheet-branch-closed-red.png);}")
def setup_menu(self):
self.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu)
def contextMenuEvent(self, event):
no_right_click = [QAddAction]
if any([isinstance(self.actionAt(event.pos()), instance) for instance in no_right_click]):
return
pos = event.pos()
def addAction(self, action):
super(QCustomMenu, self).addAction(action)
As the result, it will become like this.
You will dislike the white part of the arrow.
No problem, you can delete them clearly with a free-paint soft, but I didn't do it because it was needless.

Identifying Left and Right Mouse Button Clicks in PyQgis application

Hi guys I am relatively new to PyQt. I am trying create a custom plugin for Qgis which enables the user to select some features by drawing polygon on the canvas using mouse clicks and then performs intersection of the selected features with another layer. What I want to do is that when user right clicks on the canvas the polygon selection should stop. For this I have to identify between the right and left mouse signals. I have made a dummy function just to test this functionality:
def mousePressEvent(self):
print "code enters mousePressEvent function"
if event.buttons() == "Qt::LeftButton"
print"Left button pressed"
I am calling this function as follows:
QObject.connect(self.clickTool,SIGNAL("canvasClicked(QMouseEvent,Qt::MouseButton)"),self.mousePressEvent)
But I am unable to call the function. I guess I am doing something wrong in canvasClicked section. Any help in this matter would be much appreciated. Thanks in advance :)
The best way to achieve this is to use the QgsMapToolEmitPoint object. An example would be:
In your code, create a variable called emitPoint and in the run() function set it:
self.emitPoint = QgsMapToolEmitPoint(self.mapCanvas)
QObject.connect(
self.emitPoint,
SIGNAL("canvasClicked(const QgsPoint &, Qt::MouseButton)"),
self.clickedOnMap)
and create a function:
def clickedOnMap(self, pointOnMap, buttonUsed):
if (button==Qt.LeftButton):
....
the buttonUsed parameter has one of the values in the enum Qt::MenuButtons (as you can see in the link: http://doc.qt.io/qt-4.8/qt.html#MouseButton-enum).

Tooltips or status bar not working in a PyQt app from Maya

I've got a PyQt4 QDialog that I'm launching from python in Autodesk Maya. I want to have a status bar in the window or, if need be, tooltips. Maya doesn't seem to approve of either. I've implemented it using the method described here:
http://www.qtcentre.org/threads/10593-QDialog-StatusBar
If I launch my app standalone, both work correctly. Running from Maya, though, the status updates get sent to the general Maya status bar (which is not very obvious if you're in a different window), and Maya seems to steal the events completely from me: if I monitor the events that my event() method is getting, it never gets a QEvent.StatusTip event. I've tried swapping my QDialog for a QMainWindow, but it doesn't seem to change anything.
Any suggestions for avenues to look down to get this working?
At the moment, I'm working around this in a horrible way: subclassing each of widgets I want to use, and adding a signal to send to the parent, self.setMouseTracking(True), and a mouseMoveEvent(self, e) that sends the signal to the parent. Then at the top of the tree I set the status bar. It's the sort of nasty code that makes me feel dirty, subclassing all the widget types, but it does seem to be working.
Any better suggestions VERY gratefully received!
I need to tackle this as well, so your post was quite helpful.
When I have encountered event issues like this before, I solved it by using installEventFilter on all widgets (the same filter), rather than subclassing. Then you can receive and accept the events to block them from Maya (or let them through, e.g. space bar for marking menus over your gui, etc)
Here is what I use to let Maya have the spacebar (marking menus), ctrl+A (attribute editor toggle) and ctrl+Z (undo). This would be added to your event filter:
if event.type() == QEvent.KeyPress:
key = event.key()
mod = event.modifiers()
if ((ctrla and key == Qt.Key_A and mod == Qt.ControlModifier) or # CTRL+A
(ctrlz and key == Qt.Key_Z and mod == Qt.ControlModifier) or # CTRL+Z
(space and key == Qt.Key_Space)): # Space Bar
event.ignore()
return True
return False
You would just need to do the opposite and use event.accept() and return False
For QWidgets, colts answer here is pretty good.
This is how I got it working for QActions:
class ActionFn(object):
def __init__(self, action):
self.action = action
def __call__(self):
self.action.parent()._displayStatusTip(self.action.statusTip())
Then after the action is created:
newAction._statusFn = _StatusTipActionFn(newAction)
newAction.hovered.connect(newAction._statusFn)
I hope this helps.

Scrolling QGraphicsView programmatically

I've want to implement a scroll/pan-feature on a QGraphicsView in my (Py)Qt application. It's supposed to work like this: The user presses the middle mouse button, and the view scrolls as the user moves the mouse (this is quite a common feature).
I tried using the scroll() method inherited from QWidget. However, this somehow moves the view instead - scrollbars and all. See picture.
So, given that this is not the way I'm supposed to do this, how should I? Or is it the correct way, but I do something else wrong? The code I use:
def __init__(self):
...
self.ui.imageArea.mousePressEvent=self.evImagePress
self.ui.imageArea.mouseMoveEvent=self.evMouseMove
self.scrollOnMove=False
self.scrollOrigin=[]
...
def evImagePress(self, event):
if event.button() == Qt.LeftButton:
self.evImageLeftClick(event)
if event.button() == Qt.MidButton:
self.scrollOnMove=not self.scrollOnMove
if self.scrollOnMove:
self.scrollOrigin=[event.x(), event.y()]
...
def evMouseMove(self, event):
if self.scrollOnMove:
self.ui.imageArea.scroll(event.x()-self.scrollOrigin[0],
event.y()-self.scrollOrigin[1])
It works as I expect, except for the whole move-the-widget business.
Fails to scroll http://img55.imageshack.us/img55/3222/scrollfail.jpg
My addition to translate() method.
It works great unless you scale the scene. If you do this, you'll notice, that the image is not in sync with your mouse movements. That's when mapToScene() comes to help. You should map your points from mouse events to scene coordinates. Then the mapped difference goes to translate(), voila viola- your scene follows your mouse with a great precision.
For example:
QPointF tmp2 = mapToScene(event->pos());
QPointF tmp = tmp2.mapToScene(previous_point);
translate(tmp.x(),tmp.y());
I haven't done this myself but this is from the QGraphicsView documentation
... When the scene is larger
than the scroll bars' values, you can
choose to use translate() to navigate
the scene instead.
By using scroll you are moving the widget, translate should achieve what you are looking for, moving the contents of the QGraphicsScene underneath the view
Answer given by denis is correct to get translate to work. The comment by PF4Public is also valid: this can screw up scaling. My workaround is different than P4FPublc's -- instead of mapToScene I preserve the anchor and restore it after a translation:
previousAnchor = view.transformationAnchor()
#have to set this for self.translate() to work.
view.setTransformationAnchor(QGraphicsView.NoAnchor)
view.translate(x_diff,y_diff)
#have to reset the anchor or scaling (zoom) stops working:
view.setTransformationAnchor(previousAnchor)
You can set the QGraphicsScene's area that will be displayed by the QGraphicsView with the method QGraphicsView::setSceneRect(). So when you press the button and move the mouse, you can change the center of the displayed part of the scene and achieve your goal.

Categories

Resources