I am trying to implement my own version of the QDoubleSpinBox to have it act like in Maya with int/FloatFields so you can Ctrl+click+drag to change the value.
I have this system working on a QLabel by setting the text from my value but then when I try to do it on a QDoubleSpinBox I have a problem with the mousePressEvent. It only works on the arrowButtons, not it the field itself...
here is my initial code :
class MayaLikeDoubleField(QtGui.QDoubleSpinBox):
def __init__(self, parent):
super(MayaLikeDoubleField, self).__init__()
self.offset = 0
def mousePressEvent(self, event):
self.offset = event.x()
def mouseMoveEvent(self, event):
modifiers = QtGui.QApplication.keyboardModifiers()
if modifiers == QtCore.Qt.ControlModifier:
QtGui.QWidget.mouseMoveEvent(self, event)
relPos = event.x()-self.offset
print relPos*0.01
# instead of printing I would set the value.
In this case, it doesn' work. So I have tried this :
class Widget(QtGui.QWidget):
def __init__(self):
super(Widget, self).__init__()
self.layout = QtGui.QHBoxLayout(self)
self.sbox = QtGui.QDoubleSpinBox()
self.layout.addWidget(self.line)
self.sbox.installEventFilter(self)
def mousePressEvent(self, event):
print "Main Widget Mouse Press"
super(Widget, self).mousePressEvent(event)
def eventFilter(self, obj, event):
modifiers = QtGui.QApplication.keyboardModifiers()
if modifiers == QtCore.Qt.ControlModifier:
if self.layout.indexOf(obj) != -1:
if event.type() == event.MouseButtonPress:
print "Widget click"
if event.type() == event.MouseMove:
print "Widget Move"
return super(Widget, self).eventFilter(obj, event)
And surprise ! it doesn't work ... but if i replace the QDoubleSpinBox by a QLineEdit it does work ... why ? for me it works the same way as by default the mousePressButton doesn't work on it...
Is there anything special that I am missing ?
Thanks !
Related
How can I add children to my current qtreeview items in the code i provided below? I want it to look something like this:
The code below creates a treeview that is sorted but im not entirely clear on how to add children items. Hope you can help, thanks.
import sys
from PySide import QtGui, QtCore
class SortModel(QtGui.QSortFilterProxyModel):
def __init__(self, *args, **kwargs):
super(SortModel, self).__init__(*args, **kwargs)
def lessThan(self, left, right):
leftData = self.sourceModel().data(left)
rightData = self.sourceModel().data(right)
if leftData:
leftData = leftData.lower()
if rightData:
rightData = rightData.lower()
print('L:', leftData, 'R:', rightData)
return leftData < rightData
class Browser(QtGui.QDialog):
def __init__(self, parent=None):
super(Browser, self).__init__(parent)
self.initUI()
def initUI(self):
self.resize(200, 300)
self.setWindowTitle('Assets')
self.setModal(True)
self.results = ""
self.uiItems = QtGui.QTreeView()
self.uiItems.setAlternatingRowColors(True)
self.uiItems.setSortingEnabled(True)
self.uiItems.sortByColumn(0, QtCore.Qt.AscendingOrder)
self.uiItems.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
self.uiItems.header().setResizeMode(QtGui.QHeaderView.ResizeToContents)
self._model = self.create_model(self)
self._spmodel = SortModel(self)
self._spmodel.setSourceModel(self._model)
self._spmodel.setDynamicSortFilter(True)
self.uiItems.setModel(self._spmodel)
grid = QtGui.QGridLayout()
grid.setContentsMargins(0, 0, 0, 0)
grid.addWidget(self.uiItems, 0, 0)
self.setLayout(grid)
self.setLayout(grid)
self.uiItems.doubleClicked.connect(self.doubleClickedItem)
self.show()
def doubleClickedItem(self, item):
name = item.data(role=QtCore.Qt.DisplayRole)
print name
def create_model(self, parent):
items = [
'Cookie dough',
'Hummus',
'Spaghetti',
'Dal makhani',
'Chocolate whipped cream'
]
model = QtGui.QStandardItemModel()
model.setHorizontalHeaderLabels(['Name'])
for item in items:
model.appendRow(QtGui.QStandardItem(item))
return model
def showEvent(self, event):
geom = self.frameGeometry()
geom.moveCenter(QtGui.QCursor.pos())
self.setGeometry(geom)
super(Browser, self).showEvent(event)
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_Escape:
# self.hide()
self.close()
event.accept()
else:
super(Browser, self).keyPressEvent(event)
def main():
app = QtGui.QApplication(sys.argv)
ex = Browser()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
You would do this in your create_model method. There are several different ways of appending child rows and columns. Instead of passing a QStandardItem directly to model.appendRow, create it and save it in a variable. Then you can use QStandardItem.appendRow to add children to your top level rows.
I don't know Python and may get the syntax wrong, but the basic pattern is something like this:
std_item = QtGui.QStandardItem ("Dinner")
child_std_item = QtGui.QStandardItem ("Drinks")
std_item.appendRow (child_std_item)
Alternately, you can do this at the model level using model.insertRow and specify the QModelIndex of the parent item. You can get the QModelIndex of an item using model.indexFromItem. Hopefully that's enough to get you going. Each QStandardItem knows its parent, if any, and its children, so it's usually a matter of having the parent available to add/change/remove children.
python3.4+pyqt5
I'm trying to write a program in which buttons shall create other buttons by dragging and press_releasing to form a representation of a rolling mill street.
I created a class Button which does the moving and handles several clicking events for the buttons in creation. This part is working fine.
Where I'm stuck is, I want to read the mouse position where these button should be created.
Here's the code:
def createConnects(self):
self.id000001.released.connect(self.do_something)
self.id000002.released.connect(self.do_something)
#QtCore.pyqtSlot()
def do_something(self):
self.bname = 'Button'
self.button = Button(self.bname, self)
posx = QtGui.QMouseEvent.x()
print(posx)
posy = QtGui.QMouseEvent.y
print(posy)
sender = self.sender()
print(str(sender.objectName()))
#self.button.move(posx, posy)
qmouseevent provides several functions, but the posx line gives me this error:
TypeError: QMouseEvent.x(): first argument of unbound method must have type 'QMouseEvent'
posy-line gives < built-in function y > which isn't what I want, too, but clear.
MouseTracking is switched on in MainWindow class.
Maybe, normaly one would do that by using event in the def line but since it's a slot, that would lead to other problems.
Any hints to get along?
Update:
As recommended here's the full code of the prototype:
#!/usr/bin/env python3.4
import sys, os, math, shutil, re
from subprocess import call, Popen
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.uic import loadUiType
Ui_MainWindow, QMainWindow = loadUiType('mainwindow.ui')
def main(argv):
app = QtWidgets.QApplication(argv)
mainwindow = MyMainWindow()
mainwindow.show()
sys.exit(app.exec_())
class Button(QtWidgets.QPushButton):
def __init__(self, title, parent):
super().__init__(title, parent)
self.button = QtWidgets.QPushButton()
def mouseMoveEvent(self, event):
if event.buttons() != QtCore.Qt.RightButton:
return
mimeData = QtCore.QMimeData()
drag = QtGui.QDrag(self)
drag.setMimeData(mimeData)
drag.setHotSpot(event.pos() - self.rect().topLeft())
dropAction = drag.exec_(QtCore.Qt.MoveAction)
def mousePressEvent(self, event):
QtWidgets.QPushButton.mousePressEvent(self, event)
if event.button() == QtCore.Qt.LeftButton:
print(event.button(),' pressed')
class MyMainWindow(QtWidgets.QMainWindow,Ui_MainWindow):
def __init__(self, *args):
QtWidgets.QMainWindow.__init__(self, *args)
self.setupUi(self)
self.setMouseTracking(True)
self.createConnects()
def dragEnterEvent(self, event):
event.accept()
def dropEvent(self, event):
position = event.pos()
self.button.move(position)
event.setDropAction(QtCore.Qt.MoveAction)
event.accept()
def createConnects(self):
self.id000001.released.connect(self.do_something)
self.id000002.released.connect(self.do_something)
#QtCore.pyqtSlot()
def do_something(self):
print('do_something')
self.bname = 'Button'
self.button = QtWidgets.QPushButton()
#self.button = Button(self.bname, self)
self.button.move(100, 65)
#posx = QtGui.QMouseEvent.x()
#print(posx)
#posy = QtGui.QMouseEvent.y
#print(posy)
sender = self.sender()
print(str(sender.objectName()))
#self.button.move(posx, posy)
if __name__ == '__main__':
main(sys.argv)
cheers,
Christian
I'm attempting to create a QGraphicsView with the following behaviour:
When the control key is held and the left mouse is down, the view should be set to ScrollHandDrag mode to allow the user to pan around.
When in ScrollHandDrag mode, items should not be selectable/movable, as in the question here: In ScrollHandDrag mode of QGraphicsView, How to stop movement of QGraphicsItems on scene?
If the control key was held, the left mouse was clicked, and then the control key was released, then the view should stay in ScrollHandDrag mode until the mouse is released, or will stay in this mode should the control key be down at the time the mouse is released.
To me this sounds like it should be fairly straightforward. I've implemented the logic from the linked question, and some additional logic for my extra requirements. However this seems to cause the following two showstoppers:
In the mousePressEvent, setting the item under the mouse to not have the movable and selectable flags, calling the base class, and then re-applying the flags causes the item to become "frozen". The only way to solve this seems to be a control + click, release control + release click a few times outside of the item. Also when it gets into this state no items can be moved (although they can still be selected).
Double clicking the view causes a mousePressEvent, which is then followed by two mouseReleaseEvents! This breaks my logic.
So I would like to know how I can solve the issue of the items becoming frozen when the logic from In ScrollHandDrag mode of QGraphicsView, How to stop movement of QGraphicsItems on scene? is used, and how to deal with the strange double click mouse events - is there a way to turn them off?
Here is my code (this is also pretty much my hello world Python, so if I've made some horrible Python mistake please let me know):
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class MyMainWindow(QMainWindow):
def __init__(self):
super(MyMainWindow, self).__init__()
self.initUI()
def initUI(self):
self.setWindowTitle("Test")
self.gv = MyGraphicsView()
self.setCentralWidget(self.gv)
self.setGeometry(170, 130, 450, 250)
class MyGraphicsView(QGraphicsView):
def __init__(self):
super(MyGraphicsView, self).__init__()
self.setup()
def setup(self):
self.m_MouseIsDown = False
self.m_ControlKeyDown = False
self.setDragMode(QGraphicsView.RubberBandDrag)
def mouseMoveEvent(self, event):
# print "mouseMoveEvent: " + str(event.pos().x()) + "," + str(event.pos().y())
super(MyGraphicsView, self).mouseMoveEvent(event);
def mousePressEvent(self, event):
print "mousePressEvent"
itemUnderMouse = self.itemAt(event.pos())
if itemUnderMouse is not None:
bHadMovableFlagSet = itemUnderMouse.flags() & QGraphicsItem.ItemIsMovable
bWasSelected = itemUnderMouse.isSelected()
bHadSelectableFlagSet = itemUnderMouse.flags() & QGraphicsItem.ItemIsSelectable
if bHadMovableFlagSet:
print "has ItemIsMovable"
else:
print "hasn't ItemIsMovable"
if bHadSelectableFlagSet:
print "has ItemIsSelectable"
else:
print "hasn't ItemIsSelectable"
if bWasSelected:
print "isSelected true"
else:
print "isSelected false"
itemUnderMouse.setSelected(False)
if event.button() == Qt.LeftButton:
print "mousePressEvent: left button is now down"
self.m_MouseIsDown = True
if self.dragMode() == QGraphicsView.ScrollHandDrag and event.button() == Qt.LeftButton:
print "mousePressEvent: left button down and ScrollHandDrag set"
self.PreventItemsFromMovingOrBeingSelectedWhenPannning(event)
return
print "mousePressEvent: pass through"
super(MyGraphicsView, self).mousePressEvent(event)
def mouseReleaseEvent(self, event):
print "mouseReleaseEvent"
if event.button() == Qt.LeftButton:
print "mouseReleaseEvent - left button is now up"
self.m_MouseIsDown = False
if self.dragMode() == QGraphicsView.ScrollHandDrag and self.m_ControlKeyDown == False:
print "mouseReleaseEvent - left button up, in ScrollHandDrag mode and control key is not pressed, change to RubberBandDrag"
self.setDragMode(QGraphicsView.RubberBandDrag)
super(MyGraphicsView, self).mouseReleaseEvent(event)
def keyPressEvent(self, event):
if event.key() == Qt.Key_Control:
print "control key down"
self.m_ControlKeyDown = True
# ignore if mouse already down since we don't want to suddenly change to pan mode if an item is being moved
if event.key() == Qt.Key_Control and self.dragMode() != QGraphicsView.ScrollHandDrag and self.m_MouseIsDown == False:
print "keyPressEvent - control key down, mouse isn't down and drag mode is not ScrollHandDrag, change to ScrollHandDrag"
self.setDragMode(QGraphicsView.ScrollHandDrag)
super(MyGraphicsView, self).keyPressEvent(event)
def keyReleaseEvent(self, event):
if event.key() == Qt.Key_Control:
print "control key up"
self.m_ControlKeyDown = False
if event.key() == Qt.Key_Control and self.dragMode() == QGraphicsView.ScrollHandDrag and self.m_MouseIsDown == False:
print "keyReleaseEvent - control key up and drag mode is ScrollHandDrag, mouse is not pressed, change to RubberBandDrag"
self.setDragMode(QGraphicsView.RubberBandDrag)
super(MyGraphicsView, self).keyReleaseEvent(event)
def wheelEvent(self, event):
factor = 1.2;
if event.delta() < 0:
factor = 1.0 / factor
self.scale(factor, factor)
def PreventItemsFromMovingOrBeingSelectedWhenPannning(self, mouseEvent):
itemUnderMouse = self.itemAt(mouseEvent.pos())
if itemUnderMouse is not None:
print "preventing item from moving"
bHadMovableFlagSet = itemUnderMouse.flags() & QGraphicsItem.ItemIsMovable
itemUnderMouse.setFlag(QGraphicsItem.ItemIsMovable, False)
bWasSelected = itemUnderMouse.isSelected()
bHadSelectableFlagSet = itemUnderMouse.flags() & QGraphicsItem.ItemIsSelectable
itemUnderMouse.setFlag(QGraphicsItem.ItemIsSelectable, False)
super(MyGraphicsView, self).mousePressEvent(mouseEvent)
if bHadMovableFlagSet:
print "set ItemIsMovable"
itemUnderMouse.setFlag(QGraphicsItem.ItemIsMovable, True)
if bHadSelectableFlagSet:
print "set ItemIsSelectable"
itemUnderMouse.setFlag(QGraphicsItem.ItemIsSelectable, True)
if bWasSelected:
print "setSelected True"
itemUnderMouse.setSelected(True)
else:
print "no item under mouse - pass through"
super(MyGraphicsView, self).mousePressEvent(mouseEvent)
class MyGraphicsScene(QGraphicsScene):
def __init__(self, parent):
super(MyGraphicsScene, self).__init__()
def main():
a = QApplication(sys.argv)
w = MyMainWindow()
w.show()
scene = MyGraphicsScene(w)
w.gv.setScene(scene)
rect = scene.addRect( 10, 10, 40, 40)
rect.setFlag( QGraphicsItem.ItemIsSelectable )
rect.setFlag( QGraphicsItem.ItemIsMovable )
rect = scene.addRect( 40, 40, 40, 40)
rect.setFlag( QGraphicsItem.ItemIsSelectable )
rect.setFlag( QGraphicsItem.ItemIsMovable )
sys.exit(a.exec_())
if __name__ == '__main__':
main()
If you don't call the base implementation in mouse*Events while panning, item selection won't be an issue. However, this now requires re-implementing the built-in panning function. Fortunately, it's not hard to implement it.
After some iteration at IRC (#pyqt # freenode), this is the final implementation:
Pressing and holding CTRL key enables panning. While mouse is pressed, releasing CTRL key keeps panning mode.
Just mouse press activates Rubber selection
All actions are controlled with Left-Button
class MyGraphicsView(QGraphicsView):
def __init__(self):
super(MyGraphicsView, self).__init__()
self.setDragMode(QGraphicsView.RubberBandDrag)
self._isPanning = False
self._mousePressed = False
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self._mousePressed = True
if self._isPanning:
self.setCursor(Qt.ClosedHandCursor)
self._dragPos = event.pos()
event.accept()
else:
super(MyGraphicsView, self).mousePressEvent(event)
def mouseMoveEvent(self, event):
if self._mousePressed and self._isPanning:
newPos = event.pos()
diff = newPos - self._dragPos
self._dragPos = newPos
self.horizontalScrollBar().setValue(self.horizontalScrollBar().value() - diff.x())
self.verticalScrollBar().setValue(self.verticalScrollBar().value() - diff.y())
event.accept()
else:
super(MyGraphicsView, self).mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
if event.modifiers() & Qt.ControlModifier:
self.setCursor(Qt.OpenHandCursor)
else:
self._isPanning = False
self.setCursor(Qt.ArrowCursor)
self._mousePressed = False
super(MyGraphicsView, self).mouseReleaseEvent(event)
def mouseDoubleClickEvent(self, event): pass
def keyPressEvent(self, event):
if event.key() == Qt.Key_Control and not self._mousePressed:
self._isPanning = True
self.setCursor(Qt.OpenHandCursor)
else:
super(MyGraphicsView, self).keyPressEvent(event)
def keyReleaseEvent(self, event):
if event.key() == Qt.Key_Control:
if not self._mousePressed:
self._isPanning = False
self.setCursor(Qt.ArrowCursor)
else:
super(MyGraphicsView, self).keyPressEvent(event)
def wheelEvent(self, event):
factor = 1.2;
if event.delta() < 0:
factor = 1.0 / factor
self.scale(factor, factor)
I'm trying to write a drawing program but I'm having problems with drawing lines. When I draw a line on the bottom portion of the QGraphicsView the line is drawn to the center of the widget. Why? I don't think I understand the mapTo functions well enough, but the more I read the Qt docs the more confused I get. Hope someone can help.
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import sys
class Main(QWidget):
def __init__(self, parent):
super(Main, self).__init__(parent)
self.resize(300, 300)
vBox = QVBoxLayout(self)
view = View(self)
vBox.addWidget(view)
def keyPressEvent(self, event):
if event.key() == Qt.Key_Escape:
sys.exit()
class View(QGraphicsView):
def __init__(self, parent):
super(View, self).__init__(parent)
self.scene = QGraphicsScene(self)
self.setScene(self.scene)
def mousePressEvent(self, event):
self.start = event.pos()
def mouseReleaseEvent(self, event):
self.stop = event.pos()
self.line = Line(self, self.start, self.stop)
self.scene.addItem(self.line)
class Line(QGraphicsLineItem):
def __init__(self, parent, *args):
# args = start, stop
points = map(parent.mapToScene, args)
(start, stop) = map(QPointF, points)
self.line = QLineF(start, stop)
super(Line, self).__init__(self.line)
def run():
app = QApplication(sys.argv)
a = Main(None)
a.show()
sys.exit(app.exec_())
run()
If you don't set a rect for your scene, one will be calculated automatically and the view will centre itself on the objects within it.
To fix this, add the following to the end of View.__init__():
self.setSceneRect(QRectF(self.viewport().rect()))
I tried sub-classing the QTableWidgetItem and then set my horizontalHeaderItem with that new sub-classed class. for instance:
class ImageWidget(QtGui.QTableWidgetItem):
def __init__(self, imagePath, parent):
super(ImageWidget, self).__init__(parent)
self.picture = QtGui.QPixmap(imagePath)
def paintEvent(self, event):
painter = QtGui.QPainter(self)
painter.drawPixmap(0, 0, self.picture)
class showTable(QtGui.QDialog):
def __init__(self, parent=None):
tableWidget = QtGui.QTableWidget(10, 2)
imagePath = "C:/Documents and Settings/pwr37669/workspace/Pro_GUI_Py/images/led_green.gif"
item = ImageWidget(imagePath, QtGui.QTableWidgetItem())
tableWidget.setHorizontalHeaderItem(0, item)
tableWidget.show()
I know that code won't work, but, I'm trying to get something like that to work.
I also need to change the results of clicking on a cell or the header.
Any help would be greatly appreciated.
Thanks,
Stephen
The following seems to do the trick:
class ImgWidget1(QtGui.QLabel):
def __init__(self,imagePath, parent=None):
super(ImgWidget1, self).__init__(parent)
pic = QtGui.QPixmap(imagePath)
self.setAlignment(QtCore.Qt.AlignCenter)
self.setPixmap(pic)
And then when I wanted to add an image I did this:
self.tableWidget.setCellWidget(rows, cells, ui_polling.ImgWidget1(imagePath))
Hope this helps someone out there.
Pls, check if an example below would work for you:
import sys
from PyQt4 import QtGui, QtCore
class MainForm(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainForm, self).__init__(parent)
tableWidget = QtGui.QTableWidget()
tableWidget.setRowCount(3)
tableWidget.setColumnCount(2)
for column in range(0, 2):
for row in range(0, 3):
print row, column
item = QtGui.QTableWidgetItem("new item")
tableWidget.setItem(row, column, item)
headerItem = QtGui.QTableWidgetItem("Header Test")
headerItem.setIcon(QtGui.QIcon(QtGui.QPixmap("your_image.png")))
headerItem.setTextAlignment(QtCore.Qt.AlignVCenter);
tableWidget.setHorizontalHeaderItem(0, headerItem)
tableWidget.itemClicked.connect(self.on_tableWidget_itemClicked)
tableWidget.connect(tableWidget.horizontalHeader(), QtCore.SIGNAL('sectionClicked(int)'), self.on_headersection_clicked)
self.setCentralWidget(tableWidget)
def on_tableWidget_itemClicked(self, item):
print "item clicked " + item.text()
def on_headersection_clicked(self, item):
print "header section clicked " + str(item)
def main():
app = QtGui.QApplication(sys.argv)
form = MainForm()
form.show()
app.exec_()
if __name__ == '__main__':
main()
hope this helps, regards