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.
Related
I want to find the exact coordinates of the following parts of widgets as shown in the image in blue circles. I cannot figure out how to do this I have tried the following:
self.winfo_x()
self.winfo_y()
self.winfo_xy()
All of these will not give the answer and I do not know any other ways after any of my research. I am making a custom way to drag widgets and I am creating a containment system and need the beginning and end points.
Image(I could not show, I don't have the reputation needed):https://i.stack.imgur.com/ID3kX.png
EDIT:
Here is the picture of my code, I implemented the answer below by using hypothetical button called s:
https://i.stack.imgur.com/mnIaf.png
You can use the method winfo_x to get the x coordinate of the window relative to its parent. You can use winfo.parent to get the name of the parent window, and the method nametowidget to convert that to a widget. The solution is to combine those to recursively get the x or y coordinate of the widget and every parent.
It might look something like this:
def absolute_x(widget):
if widget == widget.winfo_toplevel():
# top of the widget hierarchy for this window
return 0
return widget.winfo_x() + absolute_x(widget.nametowidget(widget.winfo_parent()))
I have a function call on button click which is used to display a number of cuboids. However i want the ability to rotate my frame on user mouse drag so as to get the desired viewing angle (as preferred by user)
However, i cant seem to rotate as well as zoom on my display window.
Edit: What i found was upon a right click drag it changes the viewing angle. However it does not get reflected. Weirdly enoughn it is reflected only after i maximize and then restore the screen. Is there some setting i can do to make it work seamlessly.
Also, the first display happens after i move the window from its initial position. Else its just blank upon launch!! Please advise
def testDraw():
global containers
myscene = display(title='Diagram')
#myscene.material = materials.wood
myscene.select()
myscene.exit=False
#myscene.userspin = True
myscene.userspin = 1
myscene.forward = (.1, -.3, -1)
mybox = [['','','','','','','',''] for x in range(len(containers))]
for x in range(len(containers)):
for y in range(len(containers[x])):
mybox[x]=box(pos=(float(containers[x][1])+float(containers[x][2])/2,float(containers[x][3])+float(containers[x][4])/2,float(containers[x][5])+float(containers[x][6])/2),width=float(containers[x][6]),height=float(containers[x][4]),length=float(containers[x][2]))
#,color='color.'+containers[x][7]
#mybox = box(pos=(2.5,1,5), length=10, height=2, width=5,color=color.blue)
#mybox2 = box(pos=(12.5,1,5), length=10, height=2, width=5,color=color.green)
#Name,length0,length1,height0,height1,width0,width1,color
containers=[['Container_1','`enter code here`0','2','0','7','0','2','blue'],
['Container_2','2','5','0','10','0','2','green'],
['Container_3','7','10','0','5','0','2','red']]
I don't understand what is meant by "it does not get reflected". What is "it"? Also, you haven't provided a runnable program, so it' difficult to know what the context is.
I'll advertise that a better place to pose VPython questions is in the VPython forum at
https://groups.google.com/forum/?fromgroups&hl=en#!forum/vpython-users
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).
I would like to make a custom cell for PyQt.QtGui.QCalendarWidget. I need to add a short small text to the bottom of some calendar cells, is it possible? If so, could anyone point me in the right direction?
So far the idea is to subclass the QCalendarWidget and override its paintCell method. However I have no idea what to do there. I've tried to look at the Qt and the code of the widget, but it's pretty complex and I didn't find the place where they actualy paint the cell. So I would appreciate any advice.
You don't need to reimplement painting. Just call the default implementation and add text drawing:
class MyCalendar(QtGui.QCalendarWidget):
def __init__(self,parent=None):
QtGui.QCalendarWidget.__init__(self,parent)
def paintCell(self, painter, rect, date):
QtGui.QCalendarWidget.paintCell(self, painter, rect, date)
if date.day() % 5 == 0: # example condition based on date
painter.drawText(rect.bottomLeft(), "test")
You need to set proper height for the widget to ensure that there is enough space in cells to display the text.
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.