I'm building a tool that lists files in a directory. When you hover over each file it shows a tooltip displaying the date and who made the file. I'd also like an image to accompany this data.
Is it possible to insert an image into a tooltip? For each item in the QTreeView I'd like a specific image and text to popup. If it can't be done with tooltips are there other alternatives?
from PySide import QtCore, QtGui
from shiboken import wrapInstance
import maya.OpenMayaUI as mui
def get_parent():
ptr = mui.MQtUtil.mainWindow()
return wrapInstance( long( ptr ), QtGui.QWidget )
############################################
''' Classes '''
############################################
class Main_Window( QtGui.QDialog ):
def __init__( self, parent=get_parent() ):
super( Main_Window, self ).__init__( parent )
self.create_gui()
self.create_layout()
self.create_connections()
self.get_contents()
#--------------------------------------------------------------------
def create_gui( self ):
self.tv_model=MyModel()
self.tv_file_list = File_List( self )
#--------------------------------------------------------------------
def create_layout( self ):
self.main_layout = QtGui.QVBoxLayout( self )
self.main_layout.addWidget( self.tv_file_list )
self.setLayout( self.main_layout )
#--------------------------------------------------------------------
def get_contents(self):
self.tv_model.clear()
self.tv_model.setHorizontalHeaderLabels(["name","date"])
contents=["path1","path2"]
for path in contents:
date = self.get_date(path)
self.add_file(path,date)
self.tv_file_list.setColumnWidth(0, 150)
#--------------------------------------------------------------------
def add_file(self, name, date):
name = QtGui.QStandardItem(name)
name.setToolTip(name.text())
name.setIcon(self.style().standardIcon(QtGui.QStyle.SP_DirOpenIcon))
date = QtGui.QStandardItem(date)
self.tv_model.appendRow([name, date])
#--------------------------------------------------------------------
def get_date(self, path):
return "a date"
#--------------------------------------------------------------------
def create_connections( self ):
self.tv_file_list.clicked.connect( self.on_click )
# slots --------------------------------------------------------------
def on_click(self, item ):
index = self.tv_file_list.selectedIndexes()[0]
item = self.tv_model.itemFromIndex(index).text()
print item
############################################
class MyModel(QtGui.QStandardItemModel):
def __init__(self, parent=None):
super(MyModel, self).__init__(parent)
#--------------------------------------------------------------------
def flags(self, index):
flag = QtCore.Qt.ItemIsEnabled
if index.isValid():
flag |= QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable
return flag
############################################
class File_List( QtGui.QTreeView ):
''' Create the file filters '''
def __init__( self, mainUIWindow, parent=get_parent() ):
super( File_List, self ).__init__( parent )
self.setModel(mainUIWindow.tv_model)
self.setIndentation(0)
self.setColumnWidth(0,500)
self.setFocusPolicy(QtCore.Qt.NoFocus)
self.setStyleSheet("QToolTip { color: rgb(170,170,170); background-color: rgb(20,20,20); border: 1px rgb(20,20,20); }")
############################################
if __name__ == "__main__":
# workaround for a bug in maya
try:
tree_view_ui.close()
tree_view_ui.deleteLater()
except:
pass
tree_view_ui = Main_Window()
tree_view_ui.show()
try:
tree_view_ui.show()
except:
tree_view_ui.close()
tree_view_ui.deleteLater()
SOLUTION:
Thanks to ekhumoro for the quick and easy solution!
Here's the updated code and a link to some HTML basics that helped me format the tooltip:
from PySide import QtCore, QtGui
from shiboken import wrapInstance
import maya.OpenMayaUI as mui
def get_parent():
ptr = mui.MQtUtil.mainWindow()
return wrapInstance( long( ptr ), QtGui.QWidget )
############################################
''' Classes '''
############################################
class Main_Window( QtGui.QDialog ):
def __init__( self, parent=get_parent() ):
super( Main_Window, self ).__init__( parent )
self.create_gui()
self.create_layout()
self.create_connections()
self.get_contents()
#--------------------------------------------------------------------
def create_gui( self ):
self.tv_model=MyModel()
self.tv_file_list = File_List( self )
#--------------------------------------------------------------------
def create_layout( self ):
self.main_layout = QtGui.QVBoxLayout( self )
self.main_layout.addWidget( self.tv_file_list )
self.setLayout( self.main_layout )
#--------------------------------------------------------------------
def get_contents(self):
self.tv_model.clear()
self.tv_model.setHorizontalHeaderLabels(["name","date"])
contents=["path1","path2"]
for path in contents:
date = self.get_date(path)
self.add_file(path,date)
self.tv_file_list.setColumnWidth(0, 150)
#--------------------------------------------------------------------
def add_file(self, name, date):
name = QtGui.QStandardItem(name)
user_text = "Me"
image_path = "C:/windows/temp/image_01.png"
name.setToolTip('<b>{0}</b><br><img src="{1}">'.format(user_text, image_path))
name.setIcon(self.style().standardIcon(QtGui.QStyle.SP_DirOpenIcon))
date = QtGui.QStandardItem(date)
self.tv_model.appendRow([name, date])
#--------------------------------------------------------------------
def get_date(self, path):
return "a date"
#--------------------------------------------------------------------
def create_connections( self ):
self.tv_file_list.clicked.connect( self.on_click )
# slots --------------------------------------------------------------
def on_click(self, item ):
index = self.tv_file_list.selectedIndexes()[0]
item = self.tv_model.itemFromIndex(index).text()
print item
############################################
class MyModel(QtGui.QStandardItemModel):
def __init__(self, parent=None):
super(MyModel, self).__init__(parent)
#--------------------------------------------------------------------
def flags(self, index):
flag = QtCore.Qt.ItemIsEnabled
if index.isValid():
flag |= QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable
return flag
############################################
class File_List( QtGui.QTreeView ):
''' Create the file filters '''
def __init__( self, mainUIWindow, parent=get_parent() ):
super( File_List, self ).__init__( parent )
self.setModel(mainUIWindow.tv_model)
self.setIndentation(0)
self.setColumnWidth(0,500)
self.setFocusPolicy(QtCore.Qt.NoFocus)
self.setStyleSheet("QToolTip { color: rgb(170,170,170); background-color: rgb(20,20,20); border: 1px rgb(20,20,20); }")
############################################
if __name__ == "__main__":
# workaround for a bug in maya
try:
tree_view_ui.close()
tree_view_ui.deleteLater()
except:
pass
tree_view_ui = Main_Window()
tree_view_ui.show()
try:
tree_view_ui.show()
except:
tree_view_ui.close()
tree_view_ui.deleteLater()
Tooltips accept rich-text, which means you can use any markup that is part of the Supported HTML Subset. This includes an img tag, so all you need is something like:
item.setToolTip('<b>%s</b><br><img src="%s">' % (filename, iconpath))
Related
In my example I want users to have the ability to drag and drop children between families of all kinds. However I want to restrict users from having the ability to drag and drop a person onto another person, or dropping a person as a root object which in return would make it become a family.
I've gotten very close. There is a single bug though. If a user drags a person to a family, while hovering over the cursor over the family, if you carefully move the cursor to the top most edge of the family you'll see the indicator allowing you to drop the item so it's above the Family, not inside of Family. The code tests true and allows the user to successfully drop the person. The result places the person outside of the family. The image below shows what i mean.
The main focus on the code is between lines 75 - 125.
# Imports
# ------------------------------------------------------------------------------
import sys
from PySide import QtCore, QtGui
# Class Object
# ------------------------------------------------------------------------------
class Person():
def __init__(self, name="", age=0):
self.name = name
self.age = age
class Family():
def __init__(self, name=""):
self.name = name
DROP_VALIDATION_DICT = {
Person : ( Family, None ),
Family : ( None, ),
}
# Custom QTreeWidgetItem
# ------------------------------------------------------------------------------
class CustomTreeNode( QtGui.QTreeWidgetItem ):
def __init__( self, parent, data ):
super( CustomTreeNode, self ).__init__( parent )
self.data = data
#property
def data(self):
return self._data
#data.setter
def data(self, value):
self._data = value
self.setText( 0, self.data.name )
def update(self):
if self.data:
self.setText(0, self.data.name)
# Custom QTreeWidgetItem
# ------------------------------------------------------------------------------
class CustomTreeWidget( QtGui.QTreeWidget ):
def __init__(self, parent=None):
QtGui.QTreeWidget.__init__(self, parent)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
self.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.setItemsExpandable(True)
self.setAnimated(True)
self.setDragEnabled(True)
self.setDropIndicatorShown(True)
self.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
self.setAlternatingRowColors(True)
# self._dragroot = self.itemRootIndex() # added
# signals
self.itemExpanded.connect(self.on_item_expanded)
self.itemCollapsed.connect(self.on_item_collapsed)
def set_headers(self, headers=[]):
self.setColumnCount( len(headers) )
self.setHeaderLabels( headers )
def keyPressEvent(self, event):
if (event.key() == QtCore.Qt.Key_Escape and
event.modifiers() == QtCore.Qt.NoModifier):
self.clearSelection()
else:
QtGui.QTreeWidget.keyPressEvent(self, event)
def mousePressEvent(self, event):
item = self.itemAt(event.pos())
if item is None:
self.clearSelection()
QtGui.QTreeWidget.mousePressEvent(self, event)
def on_item_expanded(self):
key_mod = QtGui.QApplication.keyboardModifiers()
if key_mod == QtCore.Qt.ControlModifier:
self.expandAll()
def on_item_collapsed(self):
key_mod = QtGui.QApplication.keyboardModifiers()
if key_mod == QtCore.Qt.ControlModifier:
self.collapseAll()
# drag-n-drop with constraints
def dragEnterEvent(self, event):
item = self.itemAt(event.pos())
if item is not None and ( isinstance(item.data, Family) ):
# if event.mimeData().hasUrls():
event.acceptProposedAction()
else:
super(CustomTreeWidget, self).dragEnterEvent(event)
def dragMoveEvent(self, event):
print "testing"
print "moving..."
item = self.itemAt(event.pos())
rect = self.visualItemRect(item)
# QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.ForbiddenCursor))
print rect, event.pos()
print "Cursor: ", event.pos().y(), event.pos().y()+2
if event.pos().y() < rect.y()+2:
print "not inside"
else:
print "inside"
super(CustomTreeWidget, self).dragMoveEvent(event)
def dropEvent(self, event):
item = self.itemAt(event.pos())
if item is not None and ( isinstance(item.data, Family) ):
super(CustomTreeWidget,self).dropEvent(event)
else:
print "ignored"
event.setDropAction(QtCore.Qt.IgnoreAction)
# UI
# ------------------------------------------------------------------------------
class ExampleWidget(QtGui.QWidget):
def __init__( self, parent=None ):
super(ExampleWidget, self).__init__()
self.initUI()
def initUI(self):
# widgets
self.treewidget = CustomTreeWidget()
self.treewidget.set_headers( ["items"] )
self.btn_add_family_tree_node = QtGui.QPushButton("Add Family")
self.btn_add_person_tree_node = QtGui.QPushButton("Add Person")
# layout
self.mainLayout = QtGui.QGridLayout(self)
self.mainLayout.addWidget(self.btn_add_family_tree_node, 0,0)
self.mainLayout.addWidget(self.btn_add_person_tree_node, 0,1)
self.mainLayout.addWidget(self.treewidget, 1,0,1,3)
# signals
self.btn_add_family_tree_node.clicked.connect(self.add_family_tree_node_clicked)
self.btn_add_person_tree_node.clicked.connect(self.add_person_tree_node_clicked)
# display
self.resize(300, 400)
self.show()
self.center_window(self, True)
# test data
self.add_test_families("Roberstons", ["Kevin", "Mindy", "Riley"])
self.add_test_families("Rodriguez", ["Matt", "Kim", "Stephanie"])
def add_test_families(self, name, children):
family = Family( name )
node = CustomTreeNode( self.treewidget, family )
node.setExpanded(True)
# # disable drag and drop flags
# # QtCore.Qt.ItemIsDragEnabled
# flags = QtCore.Qt.ItemIsSelectable | \
# QtCore.Qt.ItemIsUserCheckable | \
# QtCore.Qt.ItemIsEnabled | \
# QtCore.Qt.ItemIsDropEnabled
# node.setFlags(flags)
for i in xrange(len(children)):
person = Person( children[i] )
CustomTreeNode( node, person )
# Functions
# --------------------------------------------------------------------------
def add_family_tree_node_clicked(self):
print "Created new family!"
roots = [self.treewidget]
text, ok = QtGui.QInputDialog.getText(self, 'Input Dialog', 'Enter family name:')
if ok:
for root in roots:
family = Family( text )
node = CustomTreeNode( root, family )
node.setExpanded(True)
self.treewidget.itemSelectionChanged.emit()
def add_person_tree_node_clicked(self):
print "Created new person!"
roots = self.treewidget.selectedItems()
text, ok = QtGui.QInputDialog.getText(self, 'Input Dialog', 'Enter your name:')
if ok:
for root in roots:
person = Person( text )
node = CustomTreeNode( root, person )
node.setExpanded(True)
self.treewidget.itemSelectionChanged.emit()
def center_window(self, window , cursor=False):
qr = window.frameGeometry()
cp = QtGui.QCursor.pos() if cursor else QtGui.QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
window.move(qr.topLeft())
# __name__
# ------------------------------------------------------------------------------
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
ex = ExampleWidget()
sys.exit(app.exec_())
Is there a way I can get the items being drag/dropped and their destination parent?
In an ideal scenario what I want to happen is once the dropEvent finishes, it prints the qtreewidgetitems which were moved, as well as the new parent which the items were moved to. The parent would either be the qtreewidget itself or another qtreewidgetitem depending on where the drop happened.
Can someone help me out here please?
Below is the code i have so far.
# Imports
# ------------------------------------------------------------------------------
import sys
from PySide import QtGui, QtCore, QtSvg
class TreeNodeItem( QtGui.QTreeWidgetItem ):
def __init__( self, parent, name="" ):
super( TreeNodeItem, self ).__init__( parent )
self.setText( 0, name )
self.stuff = "Custom Names - " + str(name)
class TreeWidget(QtGui.QTreeWidget):
def __init__(self, parent=None):
QtGui.QTreeWidget.__init__(self, parent)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
self.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.setItemsExpandable(True)
self.setAnimated(True)
self.setDragEnabled(True)
self.setDropIndicatorShown(True)
self.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
self.setAlternatingRowColors(True)
# def dropEvent(self, event):
# print "finished"
def dropEvent(self, event):
return_val = super( TreeWidget, self ).dropEvent( event )
print ("Drop finished")
d = event.mimeData()
print d, event.source()
return return_val
# Main
# ------------------------------------------------------------------------------
class ExampleWidget(QtGui.QWidget):
def __init__(self,):
super(ExampleWidget, self).__init__()
self.initUI()
def initUI(self):
# formatting
self.resize(250, 400)
self.setWindowTitle("Example")
# widget - passes treewidget
self.itemList = QtGui.QTreeWidget()
self.itemList = TreeWidget()
headers = [ "Items" ]
self.itemList.setColumnCount( len(headers) )
self.itemList.setHeaderLabels( headers )
# layout Grid - row/column/verticalpan/horizontalspan
self.mainLayout = QtGui.QGridLayout(self)
self.mainLayout.setContentsMargins(5,5,5,5)
self.mainLayout.addWidget(self.itemList, 0,0,1,1)
# display
self.show()
# Functions
# --------------------------------------------------------------------------
def closeEvent(self, event):
print "closed"
def showEvent(self, event):
print "open"
for i in xrange(20):
TreeNodeItem( parent=self.itemList , name=str(i) )
# Main
# ------------------------------------------------------------------------------
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
ex = ExampleWidget()
sys.exit(app.exec_())
I would suggest using a View/Model approach.
Decoding the 'application/x-qabstractitemmodeldatalist' will only return the item dragged, and the dropMimeData isn't used in the QTreeWidget.
Look here for an example.
https://wiki.python.org/moin/PyQt/Handling%20Qt's%20internal%20item%20MIME%20type
The tool I'm building uses tooltips to display extra info about a file before you click on it. It would be great if someone could lend some insight into how to accomplish this. I'm about a month into PySide so I'm having trouble deciphering these advanced examples/answers I've found online, so a simple code example with some comments will help me out a lot.
Here's what I have so far. I have no idea what I'm doing when it comes to events, so this is the best I could do with the code examples I have:
from PySide import QtCore, QtGui
from shiboken import wrapInstance
import maya.OpenMayaUI as mui
def get_parent():
ptr = mui.MQtUtil.mainWindow()
return wrapInstance( long( ptr ), QtGui.QWidget )
############################################
''' Classes '''
############################################
class Main_Window( QtGui.QDialog ):
def __init__( self, parent=get_parent() ):
super( Main_Window, self ).__init__( parent )
self.setMouseTracking(True) # Set tracking
self.create_gui()
self.create_layout()
self.create_connections()
self.get_contents()
self.shapeItems = []
#-------------------------------------------------------------------- # Mouse things
def mouseMoveEvent(self, event):
if (event.buttons() & QtCore.Qt.LeftButton):
self.moveItemTo(event.pos())
#-------------------------------------------------------------------- # Mouse things
def event(self, event):
if event.type() == QtCore.QEvent.ToolTip:
helpEvent = event
index = self.itemAt(helpEvent.pos())
if index != -1:
QtGui.QToolTip.showText(helpEvent.globalPos(), self.shapeItems[index].toolTip())
else:
QtGui.QToolTip.hideText()
event.ignore()
return True
return super(Main_Window, self).event(event)
#--------------------------------------------------------------------
def create_gui( self ):
self.tv_model=MyModel()
self.tv_file_list = File_List( self )
#--------------------------------------------------------------------
def create_layout( self ):
self.main_layout = QtGui.QVBoxLayout( self )
self.main_layout.addWidget( self.tv_file_list )
self.setLayout( self.main_layout )
#--------------------------------------------------------------------
def get_contents(self):
self.tv_model.clear()
self.tv_model.setHorizontalHeaderLabels(["name","date"])
contents=["path1","path2"]
for path in contents:
date = self.get_date(path)
self.add_file(path,date)
self.tv_file_list.setColumnWidth(0, 150)
#--------------------------------------------------------------------
def add_file(self, name, date):
name = QtGui.QStandardItem(name)
name.setToolTip(name.text())
name.setIcon(self.style().standardIcon(QtGui.QStyle.SP_DirOpenIcon))
date = QtGui.QStandardItem(date)
self.tv_model.appendRow([name, date])
#--------------------------------------------------------------------
def get_date(self, path):
return "a date"
#--------------------------------------------------------------------
def create_connections( self ):
self.tv_file_list.clicked.connect( self.on_click )
# slots --------------------------------------------------------------
def on_click(self, item ):
index = self.tv_file_list.selectedIndexes()[0]
item = self.tv_model.itemFromIndex(index).text()
print item
############################################
class MyModel(QtGui.QStandardItemModel):
def __init__(self, parent=None):
super(MyModel, self).__init__(parent)
#--------------------------------------------------------------------
def flags(self, index):
flag = QtCore.Qt.ItemIsEnabled
if index.isValid():
flag |= QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable
return flag
############################################
class File_List( QtGui.QTreeView ):
''' Create the file filters '''
def __init__( self, mainUIWindow, parent=get_parent() ):
super( File_List, self ).__init__( parent )
self.setModel(mainUIWindow.tv_model)
self.setIndentation(0)
self.setColumnWidth(0,500)
self.setFocusPolicy(QtCore.Qt.NoFocus)
self.setStyleSheet("QToolTip { color: rgb(170,170,170); background-color: rgb(20,20,20); border: 1px rgb(20,20,20); }")
############################################
if __name__ == "__main__":
# workaround for a bug in maya
try:
tree_view_ui.close()
tree_view_ui.deleteLater()
except:
pass
tree_view_ui = Main_Window()
tree_view_ui.show()
try:
tree_view_ui.show()
except:
tree_view_ui.close()
tree_view_ui.deleteLater()
HERE is a post describing how to create instant tooltips, but without any code examples I'm at a loss for how to write this. The documentation wasn't really much of a help either (it really should have simple examples for beginners).
HERE is a code that shows how to implement mouse move events, but I haven't been able to get it to work in my own example above. I keep getting errors that say: "TypeError: super(type, obj): obj must be an instance or subtype of type" and "AttributeError: 'Main_Window' object has no attribute 'itemAt'"
Again, any help or thoughts would be great. Thank you
SOLUTION
from PySide import QtCore, QtGui
from shiboken import wrapInstance
import maya.OpenMayaUI as mui
def get_parent():
ptr = mui.MQtUtil.mainWindow()
return wrapInstance( long( ptr ), QtGui.QWidget )
############################################
''' Classes '''
############################################
class Main_Window( QtGui.QDialog ):
def __init__( self, parent=get_parent() ):
super( Main_Window, self ).__init__( parent )
self.create_gui()
self.create_layout()
self.create_connections()
self.get_contents()
#--------------------------------------------------------------------
def create_gui( self ):
self.tv_model=MyModel()
self.tv_file_list = File_List( self )
self.tv_file_list.setMouseTracking(True) # Set mouse tracking
#--------------------------------------------------------------------
def create_layout( self ):
self.main_layout = QtGui.QVBoxLayout( self )
self.main_layout.addWidget( self.tv_file_list )
self.setLayout( self.main_layout )
#--------------------------------------------------------------------
def get_contents(self):
self.tv_model.clear()
self.tv_model.setHorizontalHeaderLabels(["name","date"])
contents=["path1","path2"]
for path in contents:
date = self.get_date(path)
self.add_file(path,date)
self.tv_file_list.setColumnWidth(0, 150)
#--------------------------------------------------------------------
def add_file(self, name, date):
name = QtGui.QStandardItem(name)
user = "me"
name.setToolTip("<b>{0}</b><br><b>{1}</b>".format(name.text(), user) ) # Here's where I set the tooltip
name.setIcon(self.style().standardIcon(QtGui.QStyle.SP_DirOpenIcon))
date = QtGui.QStandardItem(date)
self.tv_model.appendRow([name, date])
#--------------------------------------------------------------------
def get_date(self, path):
return "a date"
#--------------------------------------------------------------------
def create_connections( self ):
self.tv_file_list.clicked.connect( self.on_click )
self.tv_file_list.entered.connect( self.handleItemEntered ) # New connection
# slots --------------------------------------------------------------
def on_click(self, item ):
index = self.tv_file_list.selectedIndexes()[0]
item = self.tv_model.itemFromIndex(index).text()
print item
#--------------------------------------------------------------------
def handleItemEntered(self, index): # New slot
if index.isValid():
QtGui.QToolTip.showText(
QtGui.QCursor.pos(),
index.data(),
self.tv_file_list.viewport(),
self.tv_file_list.visualRect(index)
)
############################################
class MyModel(QtGui.QStandardItemModel):
def __init__(self, parent=None):
super(MyModel, self).__init__(parent)
def flags(self, index):
flag = QtCore.Qt.ItemIsEnabled
if index.isValid():
flag |= QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable
return flag
############################################
class File_List( QtGui.QTreeView ):
''' Create the file filters '''
def __init__( self, mainUIWindow, parent=get_parent() ):
super( File_List, self ).__init__( parent )
self.setModel(mainUIWindow.tv_model)
self.setIndentation(0)
self.setColumnWidth(0,500)
self.setFocusPolicy(QtCore.Qt.NoFocus)
self.setStyleSheet("QToolTip { color: rgb(170,170,170); background-color: rgb(20,20,20); border: 1px rgb(20,20,20); }")
############################################
if __name__ == "__main__":
# workaround for a bug in maya
try:
tree_view_ui.close()
tree_view_ui.deleteLater()
except:
pass
tree_view_ui = Main_Window()
tree_view_ui.show()
try:
tree_view_ui.show()
except:
tree_view_ui.close()
tree_view_ui.deleteLater()
This is much easier to do using the treeview's entered signal. And if you use the overload of showText that takes a rect argument, QToolTip will automatically do the rest.
Here's a simple demo:
from PySide import QtCore, QtGui
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.view = QtGui.QTreeView(self)
self.view.setMouseTracking(True)
self.view.entered.connect(self.handleItemEntered)
model = QtGui.QStandardItemModel(self)
for text in 'One Two Three Four Five'.split():
model.appendRow(QtGui.QStandardItem(text))
self.view.setModel(model)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.view)
def handleItemEntered(self, index):
if index.isValid():
QtGui.QToolTip.showText(
QtGui.QCursor.pos(),
index.data(),
self.view.viewport(),
self.view.visualRect(index)
)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 200, 200)
window.show()
sys.exit(app.exec_())
I'm building a tool in PySide for Maya (3D software) that auto versions and saves files in a directory.
I've spent the past few days trying to convert my initial QTreeWidget code into a model/view pattern with QTreeView and QAbstractItemModel to get tool tips and other functions, but I'm finding it harder than anticipated. When I'm learning a new coding language/technique I like to find two scripts that do the same thing- one with the new technique and one with the old technique. This way I can compare and breakdown what the new code is doing.
My big obstacle is that I'm having trouble finding a QTreeView sample that does what my QTreeWidget sample does. Also, most examples manually populate the QTreeView which doesn't help me much either. It would be great if someone could modify the QTreeView code so it does what my QTreeWidget code does. Comments on best practices for QTreeView would be great as well!
My QTreeWidget code currently...
1) gets a list of strings from a source and lists them in the first column
2) gets the date from each string and places it in the second column
QTreeView code:
from PySide import QtCore, QtGui
from shiboken import wrapInstance
import maya.OpenMayaUI as mui
import sys, os
def get_parent():
ptr = mui.MQtUtil.mainWindow()
return wrapInstance( long( ptr ), QtGui.QWidget )
################################################################################
class MyTree(QtGui.QMainWindow):
def __init__(self, parent=get_parent() ):
super(MyTree, self).__init__(parent)
data = MyData.init()
frame = QtGui.QFrame();
frame.setLayout( QtGui.QHBoxLayout() );
treeViewModel = TreeViewModel(data)
treeView = Widget_TreeView(treeViewModel)
frame.layout().addWidget( treeView );
self.setCentralWidget(frame)
################################################################################
class MyData():
def __init__(self, txt, parent=None):
self.txt = txt
self.tooltip = None
self.parent = parent
self.child = []
self.icon = []
self.index = None
self.widget = None
#---------------------------------------------------------------------------
# test initialization
#staticmethod
def init():
root = MyData("root")
root.tooltip = "root tooltip"
for i in range(0, 2):
child1 = MyData("child %i" % (i), root)
child1.tooltip = "child1 tooltip"
root.child.append(child1)
for x in range(0, 2):
child2 = MyData("child %i %i" % (i, x), child1)
child2.tooltip = "child2 tooltip"
child1.child.append(child2)
return root
# my failed attempt at adding my own data.
'''
path = "C:\Program Files"
contents = os.listdir( path )
data_list = []
for item in contents:
_data = MyData(item)
_data.tooltip = "_data tooltip"
data_list.append(_data)
return data_list # [0] adding this adds the first item to the UI,
# but i need every item from the directory
'''
################################################################################
class TreeViewModel(QtCore.QAbstractItemModel):
#---------------------------------------------------------------------------
def __init__(self, tree):
super(TreeViewModel, self).__init__()
self.__tree = tree
self.__view = None
#---------------------------------------------------------------------------
def flags(self, index):
flag = QtCore.Qt.ItemIsEnabled
if index.isValid():
flag |= QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable
return flag
#---------------------------------------------------------------------------
def index(self, row, column, parent=QtCore.QModelIndex()):
node = QtCore.QModelIndex()
if parent.isValid():
nodeS = parent.internalPointer()
nodeX = nodeS.child[row]
node = self.__createIndex(row, column, nodeX)
else:
node = self.__createIndex(row, column, self.__tree)
return node
#---------------------------------------------------------------------------
def parent(self, index):
return QtCore.QModelIndex()
#---------------------------------------------------------------------------
def rowCount(self, index=QtCore.QModelIndex()):
count = 1
node = index.internalPointer()
if node is not None:
count = len(node.child)
return count
#---------------------------------------------------------------------------
def columnCount(self, index=QtCore.QModelIndex()):
return 2
#---------------------------------------------------------------------------
def data(self, index, role=QtCore.Qt.DisplayRole):
data = None
return data
#---------------------------------------------------------------------------
def setView(self, view):
self.__view = view
#---------------------------------------------------------------------------
def __createIndex(self, row, column, node):
if node.index == None:
index = self.createIndex(row, column, node)
node.index = index
if node.widget is None:
node.widget = Widget_Tooltip(node)
self.__view.setIndexWidget(index, node.widget)
return node.index
################################################################################
class Widget_TreeView(QtGui.QTreeView):
#---------------------------------------------------------------------------
def __init__(self, model, parent=None):
super(Widget_TreeView, self).__init__(parent)
self.setModel(model)
#self.setIndentation(0)
model.setView(self)
root = model.index(0,0)
################################################################################
class Widget_Tooltip(QtGui.QWidget):
#---------------------------------------------------------------------------
def __init__(self, node):
super(Widget_Tooltip, self).__init__()
# Vars
self.node = node
self.txt = None
# Commands
self.create_tooltip(self.node)
############################################
def create_tooltip(self, node):
layout = QtGui.QHBoxLayout()
self.txt = QtGui.QLabel( node.txt)
self.txt.setToolTip("Text tooltip %s %s" % (node.txt, node.tooltip))
layout.addWidget(self.txt, 1)
self.setLayout(layout)
################################################################################
if __name__ == '__main__':
try:
form_ui.close()
form_ui.deleteLater()
except:
pass
form_ui = MyTree()
form_ui.show()
try:
form_ui.show()
except:
form_ui.close()
form_ui.deleteLater()
QTreeWidget code:
import sys, os, time
from PySide import QtCore, QtGui
from shiboken import wrapInstance
import maya.OpenMayaUI as mui
def get_parent():
ptr = mui.MQtUtil.mainWindow()
return wrapInstance( long( ptr ), QtGui.QWidget )
class Main_Window(QtGui.QDialog):
def __init__(self, parent = get_parent()):
super(Main_Window, self).__init__(parent)
# Commands
self.create_gui()
self.create_layout()
self.get_contents( None )
def create_gui( self ):
self.tw_file_list = File_List( self )
self.parent = self.tw_file_list.invisibleRootItem()
def create_layout( self ):
self.layout = QtGui.QHBoxLayout( self )
self.layout.addWidget(self.tw_file_list)
self.setLayout( self.layout )
def get_contents( self, path ):
self.tw_file_list.clear()
path = "C:\Program Files"
contents = os.listdir( path )
for item in contents:
print item
parent = self.tw_file_list.invisibleRootItem()
date = self.get_date( item, path)
self.add_item(item, date, parent)
def add_item(self, name, date, parent):
item = QtGui.QTreeWidgetItem(parent)
item.setText(0, name)
item.setText(1, date)
item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled )
return item
def get_date( self, item, path):
path = "C:\Program Files"
file = str(path + "/" + item)
date = time.localtime(os.path.getmtime(file))
clean_date = "{0}_{1}_{2} {3}:{4}".format( date[0], date[1], date[2], date[3], str(date[4]).zfill(2) )
return clean_date
############################################
class File_List( QtGui.QTreeWidget ):
''' Create the file filters '''
def __init__( self, parent=get_parent() ):
super( File_List, self ).__init__( parent )
# Setup UI
self.setColumnCount(2)
self.setHeaderLabels(["name","date"])
self.parent = self.invisibleRootItem()
############################################
if __name__ == "__main__":
# Workaround hack for a PySide bug within maya
try:
main_ui.close()
main_ui.deleteLater()
except:
pass
# Show stuff
main_ui = Main_Window()
main_ui.show()
try:
main_ui.show()
except:
main_ui.close()
main_ui.deleteLater()
Here's your QTreeWidget example, simplified:
import sys
from PySide import QtCore, QtGui
class File_List( QtGui.QTreeWidget ):
def __init__( self, parent=None):
super( File_List, self ).__init__( parent )
self.setColumnCount(2)
self.setHeaderLabels(["name","date"])
self.get_contents()
def get_contents( self):
self.clear()
contents = ["path1","path2"]
for path in contents:
date = self.get_date(path)
self.add_item(path,date)
def add_item(self, name, date):
item = QtGui.QTreeWidgetItem(self)
item.setText(0, name)
item.setText(1, date)
item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled )
return item
def get_date(self, path):
return "a date"
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
win= File_List()
win.show()
sys.exit(app.exec_())
And here's the same thing with a QTreeView and a QStandardItemModel (+ how to add children):
import sys
from PySide import QtCore, QtGui
class MyModel(QtGui.QStandardItemModel):
def __init__(self, parent=None):
super(MyModel, self).__init__(parent)
self.get_contents()
def get_contents(self):
self.clear()
contents=["path1","path2"]
for path in contents:
date = self.get_date(path)
self.add_item(path,date)
def add_item(self,name,date):
item1 = QtGui.QStandardItem(name)
item2 = QtGui.QStandardItem(date)
self.appendRow([item1, item2])
#to append child items
childItem=QtGui.QStandardItem("child")
item1.appendRow(childItem)
def get_date(self, path):
return "a date"
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
model=MyModel()
treeView=QtGui.QTreeView()
treeView.setModel(model)
model.setHorizontalHeaderLabels(["name","date"])
treeView.show()
sys.exit(app.exec_())
I need to be able to send text from class "node" in node.py:
import datetime
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class node( QGraphicsItem ):
def __init__( self, position, scene ):
super( node, self ).__init__( None, scene )
self.setFlags( QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemIsMovable )
self.rect = QRectF( -30, -30, 120, 60 )
self.setPos( position )
scene.clearSelection()
def sendFromNodeToBox( self, text ):
# how do i send text from here to textBox?
pass
def boundingRect( self ):
return self.rect
def paint( self, painter, option, widget ):
painter.setRenderHint( QPainter.Antialiasing )
pen = QPen( Qt.SolidLine )
pen.setColor( Qt.black )
pen.setWidth( 3 )
if option.state & QStyle.State_Selected:
#####################
self.sendFromNodeToBox( 'node selected' )
#####################
self.setZValue( 1 )
pen.setWidth( 4 )
pen.setColor( Qt.green )
else:
pen.setWidth( 3 )
self.setZValue( 0 )
painter.setPen( pen )
painter.setBrush( QColor( 200, 0, 0 ) )
painter.drawRoundedRect( self.rect, 10.0, 10.0 )
to statusBox in mainWindow.ui, which is being loaded by mainWindow.py
import os, sip, sys, subprocess, platform
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from PyQt4.uic import *
from PyQt4.QtOpenGL import *
from src.node import *
app = None
class mainWindow( QMainWindow ):
def __init__( self, parent = None ):
super( mainWindow, self ).__init__( parent )
self.currentPlatform = platform.system()
if self.currentPlatform == "Windows":
self.ui = loadUi( r'ui\mainWindow.ui', self )
elif self.currentPlatform == "Darwin":
self.ui = loadUi( r'ui/mainWindow.ui', self )
else:
print 'platform not supported'
quit()
# Scene view
self.scene = SceneView()
self.nodeDropGraphicsView.setViewport( QGLWidget( QGLFormat( QGL.SampleBuffers ) ) )
self.nodeDropGraphicsView.setScene( self.scene )
self.sendTextToBox( 'this text comes from mainWindow class, line 37 and 38.\n' )
self.sendTextToBox( 'press right mouse button.\n' )
def sendTextToBox( self, text ):
cursorBox = self.statusBox.textCursor()
cursorBox.movePosition(cursorBox.End)
cursorBox.insertText( str( text ) )
self.statusBox.ensureCursorVisible()
class SceneView( QGraphicsScene ):
def __init__( self, parent=None ):
super( SceneView, self ).__init__( parent )
text = self.addText( 'title' )
def mousePressEvent( self, event ):
pos = event.scenePos()
if event.button() == Qt.MidButton:
pass
elif event.button() == Qt.RightButton:
newNode = node( pos, self )
super( SceneView, self ).mousePressEvent( event )
def mouseReleaseEvent( self, event ):
print 'mouseReleaseEvent'
self.line = None
super( SceneView, self ).mouseReleaseEvent( event )
if __name__ == "__main__":
app = QApplication( sys.argv )
screenSize = QApplication.desktop().availableGeometry()
window = mainWindow()
window.resize( int( screenSize.width() ), int( screenSize.height() ) )
window.show()
app.exec_()
App runs on win and osx. Linux not tested yet.
Python 2.7 and Qt 4.8 required.
Any suggestions?
The full source is here:
https://www.dropbox.com/sh/lcetrurnemr2cla/AAD-Z6ijgTrG0qVU_cum5viua?dl=0
Help is being much appreciated.
One way to do this would be to define a custom signal on the SceneView class, and then the graphics item can emit the text via its scene:
class node( QGraphicsItem ):
...
def sendFromNodeToBox( self, text ):
self.scene().textMessage.emit(text)
class SceneView( QGraphicsScene ):
textMessage = pyqtSignal(str)
class mainWindow( QMainWindow ):
def __init__( self, parent = None ):
...
self.scene = SceneView()
self.scene.textMessage.connect(self.sendTextToBox)