class MyCompleter(QCompleter):
insertText = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
keywords_list = ['open browser', 'click', 'input text', 'contain', 'log', 'clear',
'clearcookies', 'doubleclick', 'scrol to view', 'should', 'reload',
'and', 'wait', 'children', 'right click', '.rightclick()', 'read file'
, 'equal']
crntDir = "D:\\log"
image_list = []
philes = os.listdir(crntDir)
for phile in philes:
if phile.endswith(".png"):
image_list.append(phile)
print(image_list)
#image name in image_list
QCompleter.__init__(self,keywords_list, parent)
self.setCompletionMode(QCompleter.PopupCompletion)
QCompleter.__init__(self,image_list, parent)
self.highlighted.connect(self.setHighlighted)
def setHighlighted(self, text):
self.lastSelected = text
def getSelected(self):
return self.lastSelected
I want to show two different word list, keyword_list for simple word complete, image_list is get all image names in assigned folder. If I input image name, then complete show image full name *.png.
import sys
import os
import qdarkstyle
from PyQt5 import QtGui, QtCore,QtWidgets
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import *
class MyListModel(QtCore.QAbstractListModel):
def __init__(self, datain, parent=None, *args):
QtCore.QAbstractListModel.__init__(self, parent, *args)
self.listdata = datain
def rowCount(self, parent=QtCore.QModelIndex()):
return len(self.listdata)
def data(self, index, role):
s = QtCore.QSize(250, 200)
if index.isValid() and role == QtCore.Qt.DecorationRole:
return QtGui.QIcon(QtGui.QPixmap(self.listdata[index.row()]))
if index.isValid() and role == QtCore.Qt.DisplayRole:
# print(QtCore.QVariant(self.ListItemData[index.row()]['name']))
return QtCore.QVariant(os.path.splitext(os.path.split(self.listdata[index.row()])[-1])[0])
else:
return QtCore.QVariant()
def getItem(self, index):
if index > -1 and index < len(self.ListItemData):
return self.ListItemData[index]
class MyListView(QtWidgets.QListView):
def __init__(self,parent=None):
super(MyListView, self).__init__(parent)
self.Listview = QListView()
self.setViewMode(QtWidgets.QListView.IconMode)
self.setIconSize(QtCore.QSize(90, 90))
self.setGridSize(QtCore.QSize(110, 110))
crntDir = "D:\\log"
list_data = []
image_list = []
philes = os.listdir(crntDir)
for phile in philes:
if phile.endswith(".png"):
list_data.append(os.path.join(crntDir, phile))
image_list.append(phile)
self.List_data = list_data
lm = MyListModel(list_data)
self.setModel(lm)
self.show()
self.clicked.connect(self.onclicked)
def onclicked(self,item):
image_selected_path = self.List_data[item.row()]
print(image_selected_path)
self.close()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = MyListView()
window.setWindowOpacity(0.9)
window.show()
window.raise_()
sys.exit(app.exec_())
I want image display in this window to show matched image name. How to join this two function
Related
Consider this example, modified from QStyledItemDelegate paint refresh issues :
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class MyElement(object):
def __init__(self, numid):
self.numid = numid
self.strid = "Hello world {}".format(numid)
self.param = 'a' if numid%2==0 else 'b'
def __repr__(self):
return "(numid {}, strid '{}', param '{}')".format(self.numid, self.strid, self.param)
elements = [ MyElement(i) for i in range(20) ]
print(elements)
class ElementListModel(QtCore.QAbstractListModel):
def __init__(self, elements = [], parent = None):
super(ElementListModel, self).__init__()
self.__elements = elements
def rowCount(self, parent):
return len(self.__elements)
def data(self, index, role):
thiselement = self.__elements[index.row()]
if role == QtCore.Qt.DisplayRole:
return str( thiselement.strid )
elif role == QtCore.Qt.DecorationRole:
return QtGui.QColor(thiselement.numid*10,thiselement.numid,0)
class ElementThumbDelegate(QtWidgets.QStyledItemDelegate): #(QtGui.QStyledItemDelegate):
def __init__(self, view, parent=None):
super(ElementThumbDelegate, self).__init__(parent)
def paint(self, painter, options, index):
super(ElementThumbDelegate, self).paint(painter, options, index)
#painter.setRenderHint(QtGui.QPainter.Antialiasing)
#painter.setPen(QtGui.QColor(255, 255, 255))
#painter.setBrush(QtGui.QColor(10, 10, 10))
#painter.drawRect(options.rect)
#painter.drawText(options.rect, QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter, str(index.data()))
#def sizeHint(self, options, index):
# return QtCore.QSize(50, 50)
def main():
app = QtWidgets.QApplication(sys.argv)
viewer = QtWidgets.QListView()
viewModel = ElementListModel(elements)
viewer.setModel(viewModel)
#viewer.setViewMode(QtWidgets.QListView.IconMode)
viewer.setItemDelegate(ElementThumbDelegate(viewer))
viewer.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
It results with this:
Note that there is a box by default to the left of the item, which you can "color" via DecorationRole in the data method of the ListModel (and apparently, you can also store an icon there, if you return a QIcon instead of QColor, but I've never tried it).
My question is:
How can I draw a border around that icon space/box, depending on some property? In the example above, if MyElement.param == 'a' of a given element in the list, then I would want a light blue (RGB: (38, 76, 100), or #62c2ff) border of width 2 pixels drawn around the "icon space/box" - just like I've manually done in the mockup image in the circled area; otherwise I would not want a border
How could I additionally draw a single letter in the center of that space/box, depending on some property? For instance, if a MyElement.param == 'b' of a given element in the list, then I'd like the letter 'b' written in white in the middle of the "icon space/box" - otherwise, I would not want an extra text written in that space.
The paint() method of ElementThumbDelegate should have otherwise been enough of a pointer on how to do this; but if you uncomment it, you'll see the entire item is changed - not just the left icon box/space.
The class that performs the painting is the delegate who takes the information from the DecorationRole role to create the icon, so the solution is to create the custom icon depending on the item information. That creation can be done in the model or in the delegate, in this case I will use the second option but for this the item must be exposed through a custom role like Qt.UserRole:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class MyElement(object):
def __init__(self, numid):
self.numid = numid
self.strid = "Hello world {}".format(numid)
self.param = "a" if numid % 2 == 0 else "b"
def __repr__(self):
return "(numid {}, strid '{}', param '{}')".format(
self.numid, self.strid, self.param
)
elements = [MyElement(i) for i in range(20)]
class ElementListModel(QtCore.QAbstractListModel):
def __init__(self, elements=[], parent=None):
super(ElementListModel, self).__init__()
self.__elements = elements
def rowCount(self, parent=QtCore.QModelIndex()):
return len(self.__elements)
def data(self, index, role):
if not index.isValid() or not (0 <= index.row() < self.rowCount()):
return
thiselement = self.__elements[index.row()]
if role == QtCore.Qt.DisplayRole:
return str(thiselement.strid)
if role == QtCore.Qt.UserRole:
return thiselement
class ElementThumbDelegate(QtWidgets.QStyledItemDelegate):
def initStyleOption(self, option, index):
super().initStyleOption(option, index)
thiselement = index.data(QtCore.Qt.UserRole)
if isinstance(thiselement, MyElement):
if thiselement.param == "a":
option.features |= QtWidgets.QStyleOptionViewItem.HasDecoration
pixmap = QtGui.QPixmap(option.decorationSize)
pixmap.fill(QtGui.QColor("#62c2ff"))
painter = QtGui.QPainter(pixmap)
color = QtGui.QColor(thiselement.numid * 10, thiselement.numid, 0)
painter.fillRect(pixmap.rect().adjusted(2, 2, -2, -2), color)
painter.end()
option.icon = QtGui.QIcon(pixmap)
if thiselement.param == "b":
option.features |= QtWidgets.QStyleOptionViewItem.HasDecoration
pixmap = QtGui.QPixmap(option.decorationSize)
color = QtGui.QColor(thiselement.numid * 10, thiselement.numid, 0)
pixmap.fill(color)
painter = QtGui.QPainter(pixmap)
painter.setPen(QtGui.QColor("white"))
painter.drawText(pixmap.rect(), QtCore.Qt.AlignCenter, "b")
painter.end()
option.icon = QtGui.QIcon(pixmap)
def main():
app = QtWidgets.QApplication(sys.argv)
viewer = QtWidgets.QListView()
viewModel = ElementListModel(elements)
viewer.setModel(viewModel)
viewer.setItemDelegate(ElementThumbDelegate(viewer))
viewer.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Using PySide 1.2
Grabbing the index of a clicked item in a QTableView returns a different result wether there is a horizontal header or not.
If there is a horizontal header, it's seems to be included in the computing of visualRect. So the first line, first column index is no longer 0, 0 but 1, 0.
Is this intended ? Because it is really confusing and inconvenient because the last line index will be invalid.
To reproduce :
import re
import operator
import os
import sys
# from Qt.QtCore import *
# from Qt.QtGui import *
# from Qt.QtWidgets import *
from PySide.QtCore import *
from PySide.QtGui import *
def main():
app = QApplication(sys.argv)
w = MyWindow()
w.show()
sys.exit(app.exec_())
class CustomTableView(QTableView):
"""
"""
def __init__(self, parent=None):
"""
"""
super(CustomTableView, self).__init__(parent=parent)
def mousePressEvent(self, mouse_event):
"""
"""
view_pos = self.mapFromGlobal(mouse_event.globalPos())
index = self.indexAt(view_pos)
print('mouse press index not cutting header : ' + str((index.row(), index.column())))
view_pos.setY(view_pos.y() - self.horizontalHeader().sizeHint().height())
index = self.indexAt(view_pos)
print('mouse press index cutting header : ' + str((index.row(), index.column())))
super(CustomTableView, self).mousePressEvent(mouse_event)
class MyWindow(QWidget):
def __init__(self, *args):
QWidget.__init__(self, *args)
# create table
self.get_table_data()
table = self.createTable()
# layout
layout = QVBoxLayout()
layout.addWidget(table)
self.setLayout(layout)
def get_table_data(self):
stdouterr = os.popen4("dir c:\\")[1].read()
lines = stdouterr.splitlines()
lines = lines[5:]
lines = lines[:-2]
self.tabledata = [re.split(r"\s+", line, 4)
for line in lines]
def createTable(self):
# create the view
tv = CustomTableView()
# set the table model
header = ['date', 'time', '', 'size', 'filename']
tm = MyTableModel(self.tabledata, header, self)
tv.setModel(tm)
return tv
class MyTableModel(QAbstractTableModel):
def __init__(self, datain, headerdata, parent=None, *args):
""" datain: a list of lists
headerdata: a list of strings
"""
QAbstractTableModel.__init__(self, parent, *args)
self.arraydata = datain
self.headerdata = headerdata
def rowCount(self, parent):
return len(self.arraydata)
def columnCount(self, parent):
return len(self.arraydata[0])
def data(self, index, role):
if not index.isValid():
return None
elif role != Qt.DisplayRole:
return None
return self.arraydata[index.row()][index.column()]
def headerData(self, col, orientation, role):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return self.headerdata[col]
return None
if __name__ == "__main__":
main()
You do not have to use the global position and then pass it to the local position since you will get the position with respect to the upper corner of the QTableView, but indexAt requires the position with respect to the content according to the docs:
PySide.QtGui.QAbstractItemView.indexAt(point)
Parameters: point – PySide.QtCore.QPoint
Return type: PySide.QtCore.QModelIndex
Returns the model index of the item at the viewport coordinates point.
You must use the position of the event: mouse_event.pos()
def mousePressEvent(self, mouse_event):
index = self.indexAt(mouse_event.pos())
print('mouse press index: ' + str((index.row(), index.column())))
super(CustomTableView, self).mousePressEvent(mouse_event)
If you want to use the global position then you must convert it to a local position with respect to the viewport():
def mousePressEvent(self, mouse_event):
view_pos = self.viewport().mapFromGlobal(mouse_event.globalPos())
index = self.indexAt(view_pos)
print('mouse press index : ' + str((index.row(), index.column())))
super(CustomTableView, self).mousePressEvent(mouse_event)
I am fairly new to PyQt, I'm working on a project that contains a QTableView, with one of its columns displaying system paths. I would like to add a QTreeView so users can click the + or > buttons to expand what is underneath the paths.
Here is my basic implementation:
from PyQt4 import QtGui
from PyQt4 import QtCore
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.resize(600,400)
self.setWindowTitle("My Basic Treeview")
self.treeview = QtGui.QTreeView(self)
self.treeview.model = QtGui.QFileSystemModel()
self.treeview.model.setRootPath('/opt')
self.treeview.setModel(self.treeview.model)
self.treeview.setColumnWidth(0, 200)
self.setCentralWidget(self.treeview)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
Although, in the above case, I get all folders but I just want the /opt path and its underneath folders.
import operator
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class MyWindow(QWidget):
def __init__(self, data_list, header, *args):
QWidget.__init__(self, *args)
# setGeometry(x_pos, y_pos, width, height)
self.setGeometry(300, 200, 570, 450)
self.setWindowTitle("Click on column title to sort")
table_model = MyTableModel(self, data_list, header)
table_view = QTableView()
table_view.setModel(table_model)
# set font
font = QFont("Courier New", 14)
table_view.setFont(font)
# set column width to fit contents (set font first!)
table_view.resizeColumnsToContents()
# enable sorting
table_view.setSortingEnabled(True)
layout = QVBoxLayout(self)
layout.addWidget(table_view)
self.setLayout(layout)
class MyTableModel(QAbstractTableModel):
def __init__(self, parent, mylist, header, *args):
QAbstractTableModel.__init__(self, parent, *args)
self.mylist = mylist
self.header = header
def rowCount(self, parent):
return len(self.mylist)
def columnCount(self, parent):
return len(self.mylist[0])
def data(self, index, role):
if not index.isValid():
return None
elif role != Qt.DisplayRole:
return None
return self.mylist[index.row()][index.column()]
def headerData(self, col, orientation, role):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return self.header[col]
return None
# the solvent data ...
header = ['Name', ' Email', ' Status', ' Path']
# use numbers for numeric data to sort properly
data_list = [
('option_A', 'zyro#email.com', 'Not Copied', '/Opt'),
('option_B', 'zyro#email.com', 'Not Copied', '/Users'),
]
app = QApplication([])
win = MyWindow(data_list, header)
win.show()
app.exec_()
Visual example :
I think your question can be divided in two parts:
how, in a QTreeView, the /opt path and its children can be shown, but without showing its siblings. In other words, how is it possible to show the root directory in a QTreeView ;
how can a QTreeView be added to a QTableView.
1. How to include the root directory in a QTreeView :
The root of a QTreeView is the directory for which the content is shown in the view. It is set when calling the method setRootIndex. According to a post by wysota on Qt Centre:
You can't display the invisibleRootItem because it is a fake item used only to have an equivalent of empty QModelIndex.
A workaround would be to set the root directory to the parent of /opt and filtering out the siblings of /opt with a subclass of a QSortFilterProxyModel. Note that I've also reimplemented the sizeHint method which will be necessary for the resizing of the rows of the QTableView:
from PyQt4 import QtGui, QtCore
import os
class MyQTreeView(QtGui.QTreeView):
def __init__(self, path, parent=None):
super(MyQTreeView, self).__init__(parent)
ppath = os.path.dirname(path) # parent of path
self.setFrameStyle(0)
#---- File System Model ----
sourceModel = QtGui.QFileSystemModel()
sourceModel.setRootPath(ppath)
#---- Filter Proxy Model ----
proxyModel = MyQSortFilterProxyModel(path)
proxyModel.setSourceModel(sourceModel)
#---- Filter Proxy Model ----
self.setModel(proxyModel)
self.setHeaderHidden(True)
self.setRootIndex(proxyModel.mapFromSource(sourceModel.index(ppath)))
#--- Hide All Header Sections Except First ----
header = self.header()
for sec in range(1, header.count()):
header.setSectionHidden(sec, True)
def sizeHint(self):
baseSize = super(MyQTreeView,self).sizeHint()
#---- get model index of "path" ----
qindx = self.rootIndex().child(0, 0)
if self.isExpanded(qindx): # default baseSize height will be used
pass
else: # shrink baseShize height to the height of the row
baseSize.setHeight(self.rowHeight(qindx))
return baseSize
class MyQSortFilterProxyModel(QtGui.QSortFilterProxyModel):
def __init__(self, path, parent=None):
super(MyQSortFilterProxyModel, self).__init__(parent)
self.path = path
def filterAcceptsRow(self, row, parent):
model = self.sourceModel()
path_dta = model.index(self.path).data()
ppath_dta = model.index(os.path.dirname(self.path)).data()
if parent.data() == ppath_dta:
if parent.child(row, 0).data() == path_dta:
return True
else:
return False
else:
return True
2. How to add a *QTreeView* to a *QTableView* :
It is possible to add a QTreeView to a QTableView by using a QItemDelegate. The post by Pavel Strakhov greatly helped me for this, since I had never used QTableView in combination with delegates before answering to this question. I always used QTableWidget instead with the setCellWidget method.
Note that I've setup a signal in the MyDelegate class which call the method resizeRowsToContents in the MyTableView class. This way, the height of the rows resize according the the reimplementation of the sizeHint method of the MyQTreeView class.
class MyTableModel(QtCore.QAbstractTableModel):
def __init__(self, parent, mylist, header, *args):
super(MyTableModel, self).__init__(parent, *args)
self.mylist = mylist
self.header = header
def rowCount(self, parent=QtCore.QModelIndex()):
return len(self.mylist)
def columnCount(self, parent=QtCore.QModelIndex()):
return len(self.mylist[0])
def data(self, index, role):
if not index.isValid():
return None
elif role != QtCore.Qt.DisplayRole:
return None
return self.mylist[index.row()][index.column()]
def headerData(self, col, orientation, role):
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
return self.header[col]
return None
class MyDelegate(QtGui.QItemDelegate):
treeViewHeightChanged = QtCore.pyqtSignal(QtGui.QWidget)
def createEditor(self, parent, option, index):
editor = MyQTreeView(index.data(), parent)
editor.collapsed.connect(self.sizeChanged)
editor.expanded.connect(self.sizeChanged)
return editor
def sizeChanged(self):
self.treeViewHeightChanged.emit(self.sender())
class MyTableView(QtGui.QTableView):
def __init__(self, data_list, header, *args):
super(MyTableView, self).__init__(*args)
#---- set up model ----
model = MyTableModel(self, data_list, header)
self.setModel(model)
#---- set up delegate in last column ----
delegate = MyDelegate()
self.setItemDelegateForColumn(3, delegate)
for row in range(model.rowCount()):
self.openPersistentEditor(model.index(row, 3))
#---- set up font and resize calls ----
self.setFont(QtGui.QFont("Courier New", 14))
self.resizeColumnsToContents()
delegate.treeViewHeightChanged.connect(self.resizeRowsToContents)
3. Basic application :
Here is a basic application based on the code you provided in your OP:
if __name__ == '__main__':
header = ['Name', ' Email', ' Status', ' Path']
data_list = [('option_A', 'zyro#email.com', 'Not Copied', '/opt'),
('option_B', 'zyro#email.com', 'Not Copied', '/usr')]
app = QtGui.QApplication([])
win = MyTableView(data_list, header)
win.setGeometry(300, 200, 570, 450)
win.show()
app.exec_()
Which results in:
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've implemented a very simple log viewer in Python using PyQt4.
I am interested in using it to follow the execution of a program, so the list view has to be refreshed when a new line is appended to the log file.
Here is my implementation (without the watch):
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class LogEntryModel(QAbstractListModel):
def __init__(self, logfile, parent=None):
super(LogEntryModel, self).__init__(parent)
self.slurp(logfile)
def rowCount(self, parent=QModelIndex()):
return len(self.entries)
def data(self, index, role):
if index.isValid() and role == Qt.DisplayRole:
return QVariant(self.entries[index.row()])
else:
return QVariant()
def slurp(self, logfile):
self.entries = []
with open(logfile, 'rb') as fp:
for line in fp.readlines():
tokens = line.strip().split(' : ')
sender = tokens[2]
message = tokens[4]
entry = "%s %s" % (sender, message)
self.entries.append(entry)
class LogViewerForm(QDialog):
def __init__(self, logfile, parent=None):
super(LogViewerForm, self).__init__(parent)
# build the list widget
list_label = QLabel(QString("<strong>MoMo</strong> Log Viewer"))
list_model = LogEntryModel(logfile)
self.list_view = QListView()
self.list_view.setModel(list_model)
list_label.setBuddy(self.list_view)
# define the layout
layout = QVBoxLayout()
layout.addWidget(list_label)
layout.addWidget(self.list_view)
self.setLayout(layout)
if __name__ == "__main__":
app = QApplication(sys.argv)
form = LogViewerForm(sys.argv[1])
form.show()
app.exec_()
As presented, the application works as expected: open the file, parse the contents (split at ' : ' and create a list), and display the list using a QListView.
There is a QFileSystemWatcher class which emits a fileChanged signal, but I don't know where to connect it and how to trigger an add a row to the data and refresh the view event.
Any help?
Thanks.
I am quite new to python and pyqt, but this "works" here:
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class LogEntryModel(QAbstractListModel):
def __init__(self, logfile, parent=None):
super(LogEntryModel, self).__init__(parent)
self.slurp(logfile)
self.logfile = logfile
def rowCount(self, parent=QModelIndex()):
return len(self.entries)
def data(self, index, role):
if index.isValid() and role == Qt.DisplayRole:
return QVariant(self.entries[index.row()])
else:
return QVariant()
def slurp(self, logfile):
self.entries = []
with open(logfile, 'rb') as fp:
for line in fp.readlines():
tokens = line.strip().split(' : ')
sender = tokens[2]
message = tokens[4]
entry = "%s %s" % (sender, message)
self.entries.append(entry)
class LogViewerForm(QDialog):
def __init__(self, logfile, parent=None):
super(LogViewerForm, self).__init__(parent)
self.watcher = QFileSystemWatcher([logfile], parent=None)
self.connect(self.watcher, SIGNAL('fileChanged(const QString&)'), self.update_log)
# build the list widget
list_label = QLabel(QString("<strong>MoMo</strong> Log Viewer"))
list_model = LogEntryModel(logfile)
self.list_model = list_model
self.list_view = QListView()
self.list_view.setModel(self.list_model)
list_label.setBuddy(self.list_view)
# define the layout
layout = QVBoxLayout()
layout.addWidget(list_label)
layout.addWidget(self.list_view)
self.setLayout(layout)
def update_log(self):
print 'file changed'
self.list_model.slurp(self.list_model.logfile)
self.list_view.updateGeometries()
if __name__ == "__main__":
app = QApplication(sys.argv)
form = LogViewerForm(sys.argv[1])
form.show()
app.exec_()
But be aware that this is probably not a good way to do it.
You might want to stream the logfile...
Maybe somebody more experienced can help out.