Identifying Left and Right Mouse Button Clicks in PyQgis application - python

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).

Related

How to have multiple widget buttons perform different actions in Jupyter Notebook?

I am trying to make a series of buttons that take samples from a data set based on some scenario. I have a 3x2 group of buttons, each describing a different scenario. I can't seem to get them to perform their separate actions.
I think I understand how to connect the action of clicking a button to its response. However, I don't understand how to do the same for multiple buttons.
Here's my code that worked to get a single, standalone button to work:
button = widgets.Button(description='Generate message!')
out = widgets.Output()
def on_button_clicked(_):
samp_text = raw_data.sample(1).column(1)
# "linking function with output"
with out:
# what happens when we press the button
print(samp_text)
# linking button and function together using a button's method
button.on_click(on_button_clicked)
# displaying button and its output together
widgets.VBox([button,out])
Now what I'm trying to do is take different kinds of samples given various situations. So I have functions written for each type of sampling method that returns a table of proportions:
1 47.739362
2 44.680851
3 4.920213
9 2.659574
Name: vote, dtype: float64
However the same method in the first example with just one button doesn't work the same with multiple. How do I use widgets.Output() and how do I connect it so that clicking the button outputs the corresponding sample summary?
I expect for a clicked button to output its sample summary as shown above.
I didn't have any problem extending your example to use
multiple buttons. I don't know where you were confused.
Sometimes exceptions that occur in widget callbacks do not
get printed -- maybe you had a bug in your code that you couldn't
see for that reason. It's best to have everything
wrapped in a "with out:"
Created two buttons using the list. Guess code itself explains better.
from ipywidgets import Button, HBox
thisandthat = ['ON', 'OFF']
switch = [Button(description=name) for name in thisandthat]
combined = HBox([items for items in switch])
def upon_clicked(btn):
print(f'The circuit is {btn.description}.', end='\x1b\r')
for n in range(len(thisandthat)):
switch[n].style.button_color = 'gray'
btn.style.button_color = 'pink'
for n in range(len(thisandthat)):
switch[n].on_click(upon_clicked)
display(combined)

Showing plots if checkbox is checked, on python (with PyQt4)

I'm brand new to Python and I'm trying to make my first program with PyQt4. My problem is basically the following: I have two checkboxes (Plot1 and Plot2), and a "End" push button, inside my class. When I press End, I would like to see only the plots that the user checks, using matplotlib. I'm not being able to do that. My first idea was:
self.endButton.clicked.connect(self.PlotandEnd)
self.plot1Checkbox.clicked.connect(self.Plot1)
self.plot2Checkbox.clicked.conncet(self.Plot2)
def PlotandEnd(self)
plot1=self.Plot1()
pyplot.show(plot1)
plot2=self.Plot2()
pyplot.show(plot2)
def Plot1(self)
plot1=pyplot.pie([1,2,5,3,2])
return plot1
def Plot2(self)
plot2=pyplot.plot([5,3,5,8,2])
return plot2
This doesn't work, of course, because "PlotandEnd" will plot both figures, regardless of the respective checkbox. How can I do what I'm trying to?
Wrap the plot creation in an if statement that looks at the state of the check boxes. For example:
def PlotandEnd(self)
if self.plot1Checkbox.isChecked():
plot1=self.Plot1()
pyplot.show(plot1)
if self.plot2Checkbox.isChecked():
plot2=self.Plot2()
pyplot.show(plot2)
You also don't need the following lines:
self.plot1Checkbox.clicked.connect(self.Plot1)
self.plot2Checkbox.clicked.conncet(self.Plot2)
This does nothing useful at the moment! Qt never uses the return value of your PlotX() methods, and you only want things to happen when you click the End button, not when you click a checkbox. The PlotX() methods are only currently useful for your PlotandEnd() method.

python tkinter: Custom menu while pulling down

I'm learning creating software with Python and Tkinter. Now I need to change menu items for different conditions, but could not find an easy way to do it. Well, let me try to explain my question clearly using an example:
Like shown in the figure, I have a listbox on the left and a listbox on the right. I also have a menu to move the items around, the commands are "move to right", "move to left" and "exchange". The following conditions are considered:
When I only get items selected in left listbox, I want only the command "move to right" enabled, like shown in the figure.
When I only get items selected in right listbox, I want only the command "move to left" enabled.
When I get items selected in both listboxes, I want all commands enabled.
When I get no item selected, I want all commands disabled.
I know I can get the work done by binding events "ListboxSelect" and "Button-1" to some functions, and then use the functions to configure the menu. But it is really a complex work when I have five listboxes in the actual software. So I am wondering whether there is an easy way to do this, like overloading some functions in tkinter.Menu class (I tried overloading post(), grid(), pack() and place(), none of them works).
Any idea is welcomed.
I think what you want to use is the postcommand to modify the menu as appropriate. If you're going to have multiple listboxes, the simplest solution may be to implement your own class. Here's a rough idea:
class EditMenu(Tkinter.Menu):
def __init__(self, parent, listboxes, **kw):
self.commandhook = kw.get('postcommand', None)
kw['postcommand'] = self.postcommand
super(EditMenu, self).__init__(parent, **kw)
self.listboxes = listboxes
self.add_command(label="Move to right", command=self.move_to_right)
self.add_command(label="Move to left", command=self.move_to_left)
self.add_command(label="Exchange", command=self.exchange)
def postcommand(self):
for i in xrange(3):
# do some checks for each entry
# and set state to either Tkinter.DISABLED or Tkinter.NORMAL
self.entryconfig(i, state=state)
if self.commandhook is not None:
self.commandhook()
# Implement your three functions here
If you start to add more items, probably what you'll want to do is create a class for each menu item. In that class you could put in the logic for enable/disable and the callback function implementation. Comment if you'd like to see an example.

Customising location-sensitive context menu in QTextEdit

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.

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