How can i set the data for the second column of a QStandardItem which i then add to a QTreeview?
In my case I just want to place a checkbox or text for the sake of example.
import sys
from PySide import QtGui, QtCore
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.items_model = QtGui.QStandardItemModel()
self.ui_items = QtGui.QTreeView()
self.ui_items.setAlternatingRowColors(True)
self.ui_items.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
self.ui_items.header().setResizeMode(QtGui.QHeaderView.ResizeToContents)
self.ui_items.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
self.ui_items.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.ui_items.setModel(self.items_model)
grid = QtGui.QGridLayout()
grid.setContentsMargins(0, 0, 0, 0)
grid.addWidget(self.ui_items, 0, 0)
self.setLayout(grid)
self.update_model()
def update_model(self):
model = self.ui_items.model()
model.clear()
model.setHorizontalHeaderLabels(['Assets'])
# Create Data
for i in range(1,5):
root = QtGui.QStandardItem()
root.setData('Apple', role=QtCore.Qt.DisplayRole)
root.setColumnCount(2)
model.appendRow(root)
self.ui_items.expandAll()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
ex = Browser()
ex.show()
sys.exit(app.exec_())
You can pass a list of items to appendRow, with each item representing a column:
# Create Data
for i in range(1,5):
column1 = QtGui.QStandardItem('Apple')
column2 = QtGui.QStandardItem('Orange')
column2.setCheckable(True)
model.appendRow([column1, column2])
Related
Let me explain my problem in detail. I have a QGraphicsRectItem as a parent in QGraphicsScene, and I have QGraphicsPolygonItems as its children. When The children are not selected and I move the parent everything's fine - they keep their positions relative to the parent and move with it. But when children are selected and I move the parent children move weirdly (I would like them to behave the same way as if they were not selected - move relative to their parent). Here's the code and a gif displaying both situations.
class Test(QWidget):
def __init__(self, parent=None):
super(Test, self).__init__(parent)
self.resize(1000, 800)
self.generalLayout = QVBoxLayout()
self.view_ = GraphicsView()
self.size_grip = QSizeGrip(self)
self.generalLayout.addWidget(self.size_grip, 0, Qt.AlignBottom | Qt.AlignRight)
self.generalLayout.addWidget(self.view_)
self.setLayout(self.generalLayout)
def contextMenuEvent(self, event):
self.menu = QMenu(self)
self.new_children_menu = QMenu("New child", self.menu)
parentAction = QAction("New Parent", self)
childAction = QAction('Child', self)
click_pos = self.view_.mapToScene(event.pos())
parent_item = self.itemUnderMouse()
mouse_pos = self.mousePosition(click_pos, parent_item)
parentAction.triggered.connect(lambda: self.view_.addParent(mouse_pos, parent_item))
childAction.triggered.connect(lambda: self.view_.addButton(mouse_pos, parent_item))
self.new_children_menu.addAction(childAction)
self.menu.addAction(parentAction)
self.menu.addMenu(self.new_children_menu)
self.menu.popup(QCursor.pos())
def mousePosition(self, click, prnt_item):
if prnt_item is None:
return click
else:
return prnt_item.mapFromScene(click)
def itemUnderMouse(self):
for item in self.view_.scene().items():
if item.isUnderMouse():
return item
else:
continue
class GraphicsView(QGraphicsView):
def __init__(self):
super(GraphicsView, self).__init__()
self.setAttribute(Qt.WA_StyledBackground, True)
self.setStyleSheet('background-color: white;')
self.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
self.setMouseTracking(True)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.setFrameShape(QFrame.NoFrame)
self.setCursor(QCursor(Qt.PointingHandCursor))
self.size_grip = QSizeGrip(self)
self._scene = QGraphicsScene()
self._scene.setSceneRect(0, 0, 400, 400)
self.setScene(self._scene)
self.fitInView(self.scene().sceneRect(), Qt.KeepAspectRatio)
self.setDragMode(QGraphicsView.ScrollHandDrag)
self.parent = GraphicsRectItem
self.child = newButton
def addParent(self, pos, prnt):
new_parent = self.parent(0, 0, 100, 150)
new_parent.setPos(pos)
if prnt is None:
self.scene().addItem(new_parent)
else:
new_parent.setParentItem(prnt)
def addButton(self, pos, parent_item):
new_button = self.child(pos)
if parent_item is None:
self.scene().addItem(new_button)
else:
new_button.setParentItem(parent_item)
class GraphicsRectItem(QGraphicsRectItem):
def __init__(self, *args):
super().__init__(*args)
self.setAcceptHoverEvents(True)
self.setFlag(QGraphicsItem.ItemIsMovable, True)
self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
self.setFlag(QGraphicsItem.ItemIsFocusable, True)
class newButton(QGraphicsPolygonItem):
def __init__(self, pos):
super(newButton, self).__init__()
self.newPoly = QPolygonF()
self.newPolyPoints = (QPointF(0, 0),
QPointF(0, 50),
QPointF(50, 50),
QPointF(50, 0))
for point in self.newPolyPoints:
self.newPoly.append(point)
self.setPolygon(self.newPoly)
self.setBrush(QBrush(QColor("violet")))
self.setPen(QPen(QColor("gray")))
self.setFlags(
self.ItemIsSelectable
| self.ItemIsMovable
| self.ItemIsFocusable
| self.ItemSendsGeometryChanges
)
self.setAcceptHoverEvents(True)
poly_center = self.boundingRect().center()
self.setTransformOriginPoint(poly_center)
self.setPos(pos)
if __name__ == '__main__':
app = QApplication([])
win = Test()
win.show()
app.exec_()
I also want the children to not go out of the parent's bounding rectangle area, but if I set the "ItemClipsChildrenToShape" flag the children disappear inside the parent. This gif illustrates that situation.
I want to implement a blender-like property adjustment box with pyside2 in Autodesk Maya, as shown below.
I have found some similar links, but without success specifically how to do it.
How to create graphic slider in Python that can be modified with mouse?
import sys
from PySide2.QtCore import Qt, QEvent
from PySide2.QtGui import QStandardItemModel, QStandardItem
from PySide2.QtWidgets import QApplication, QWidget, QTableView, QHBoxLayout, QStyledItemDelegate, QStyle, QStyleOptionProgressBar, QStyleOptionButton
class MyTableView(QTableView):
def __init__(self):
super(MyTableView, self).__init__()
class MyDelgate(QStyledItemDelegate):
def __init__(self):
super(MyDelgate, self).__init__()
def paint(self, painter, option, index):
if index.column() == 1:
style = QStyleOptionProgressBar()
style.minimum = 0
style.maximum = 10
style.progress= index.data(Qt.DisplayRole)
style.rect = option.rect
QApplication.style().drawControl(QStyle.CE_ProgressBar, style, painter)
elif index.column() == 2:
style = QStyleOptionButton()
if index.data(Qt.DisplayRole) == True:
style.state = QStyle.State_On
else:
style.state = QStyle.State_Off
style.state |= QStyle.State_Enabled
style.rect = option.rect
style.rect.setX(option.rect.x() + option.rect.width() / 2 - 7)
QApplication.style().drawControl(QStyle.CE_CheckBox, style, painter)
else:
return QStyledItemDelegate.paint(self, painter, option, index)
def editorEvent(self, event, model, option, index):
if index.column() == 2:
if event.type() == QEvent.MouseButtonPress and option.rect.contains(event.pos()):
data = not index.data(Qt.DisplayRole)
model.setData(index, data, Qt.DisplayRole)
return True
else:
return QStyledItemDelegate.editorEvent(self, event, model, option, index)
class MyWidget(QWidget):
def __init__(self):
super(MyWidget, self).__init__()
self.mode = QStandardItemModel()
root = self.mode.invisibleRootItem()
item1 = QStandardItem()
item1.setData('a', Qt.DisplayRole)
item2 = QStandardItem()
item2.setData(1, Qt.DisplayRole)
item3 = QStandardItem()
item3.setData(False, Qt.DisplayRole)
item4 = QStandardItem()
item4.setData('b', Qt.DisplayRole)
item5 = QStandardItem()
item5.setData(2, Qt.DisplayRole)
item6 = QStandardItem()
item6.setData(True, Qt.DisplayRole)
root.setChild(0, 0, item1)
root.setChild(0, 1, item2)
root.setChild(0, 2, item3)
root.setChild(1, 0, item4)
root.setChild(1, 1, item5)
root.setChild(1, 2, item6)
tableView = MyTableView()
tableView.setModel(self.mode)
tableView.setItemDelegate(MyDelgate())
layout = QHBoxLayout()
layout.addWidget(tableView)
self.setLayout(layout)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyWidget()
w.resize(500, 300)
w.move(300, 300)
w.setWindowTitle('Simple')
w.show()
sys.exit(app.exec_())
Thanks for any advice.
You need to track the mouse position in the editorEvent and compute the ratio based on the sub element rectangle of the style option.
def editorEvent(self, event, model, option, index):
if index.column() == 1:
if event.type() in (event.MouseMove, event.MouseButtonPress):
progressOpt = QStyleOptionProgressBar()
progressOpt.rect = option.rect
style = option.widget.style()
rect = style.subElementRect(style.SE_ProgressBarGroove,
progressOpt, option.widget)
pos = max(0, min(rect.width(), event.x() - rect.x()))
model.setData(index, round(pos / rect.width() * 10), Qt.DisplayRole)
return True
elif index.column() == 2:
if event.type() == QEvent.MouseButtonPress and option.rect.contains(event.pos()):
data = not index.data(Qt.DisplayRole)
model.setData(index, data, Qt.DisplayRole)
return True
return super().editorEvent(event, model, option, index)
import sys, os
from PySide6.QtCore import *
from PySide6.QtGui import *
from PySide6.QtWidgets import *
from ui_listitem import *
class myItem(QWidget):
def __init__(self, parent = None) -> None:
super().__init__(parent=parent)
self.ui = Ui_rootWidget() # this ui just have 2 text labels horizontal aligned
self.ui.setupUi(self)
class myList(QListWidget):
def __init__(self, parent = None) -> None:
super().__init__(parent=parent)
self.resize(400, 400)
self.setDragDropMode(QAbstractItemView.DragDropMode.InternalMove)
for i in range(10):
wi = QListWidgetItem(self)
wi.widget = myItem(self)
wi.widget.ui.label1.setText(f'text{i}')
wi.widget.ui.label2.setText(f'text{i}')
wi.setSizeHint(wi.widget.sizeHint())
self.addItem(wi)
self.setItemWidget(wi, wi.widget)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = myList()
w.show()
sys.exit(app.exec())
result:
uh..I don't know what it happened. What should I do to get it to work?
I read a post(InternalMove in QListWidget makes item disappear). I tried setDefaultDropAction(Qt.TargetMoveAction) or setMovement(QListView.Free) or both but not worked.
I am using python 3.9.7, pyside 6.2.1, on Windows 10 Pro 20H2 19042.1348 build.
Addition.
A similar but not identical disappearance occurs in a QListView without a custom widget.
import sys, os
from PySide6.QtCore import *
from PySide6.QtGui import *
from PySide6.QtWidgets import *
class myDelegate(QStyledItemDelegate):
def sizeHint(self, option, index):
return QSize(350, 35)
class myList(QListView):
def __init__(self, parent = None) -> None:
super().__init__(parent=parent)
self.resize(400, 400)
self.setDragDropMode(QAbstractItemView.DragDropMode.InternalMove)
self.model = QStandardItemModel(self)
self.setModel(self.model)
self.delegate = myDelegate(self)
self.setItemDelegate(self.delegate)
for i in range(10):
item = QStandardItem(f'text{i}')
self.model.appendRow(item)
self.setDragDropOverwriteMode(False)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = myList()
w.show()
sys.exit(app.exec())
result:
I tried setDefaultDropAction with Qt.MoveAction, Qt.CopyAction or Qt.TargetMoveAction but all not worked.
Are these all originally intended? Please let me know how to move items in listview or listwidget without disappearing. Or maybe it's impossible?
I am trying to use the figure that is being created inside the class "SubplotAnimation" and place it to my graph page but it doesn't work.. Please help me... Here is my code (Tried to take all the unnecessary stuff, but some are left...):
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
#import tkinter as tk
import Tkinter as tk
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
import matplotlib.animation as animation
class SubplotAnimation(animation.TimedAnimation):
def __init__(self):
fig = plt.figure()
fig.set_size_inches(10, 7)
ax2 = plt.subplot2grid((2, 2), (0, 0), colspan=2)
ax3 = plt.subplot2grid((2, 2), (1, 0), colspan=2)
self.t = np.linspace(0, 80, 400)
self.x = np.cos(2 * np.pi * self.t / 10.)
self.y = np.sin(2 * np.pi * self.t / 10.)
self.z = 10 * self.t
ax2.set_xlabel('y')
ax2.set_ylabel('z')
self.line2 = Line2D([], [], color='black')
ax2.add_line(self.line2)
ax2.set_xlim(0, 800)
ax2.set_ylim(-1, 1)
ax3.set_xlabel('x')
ax3.set_ylabel('z')
self.line3 = Line2D([], [], color='black')
ax3.add_line(self.line3)
ax3.set_xlim(0, 800)
ax3.set_ylim(-1, 1)
animation.TimedAnimation.__init__(self, fig, interval=50, blit=True)
def _draw_frame(self, framedata):
i = framedata
self.line2.set_data(self.z[:i], self.y[:i])
self.line3.set_data(self.z[:i], self.x[:i])
self._drawn_artists = [self.line2, self.line3]
def new_frame_seq(self):
return iter(range(self.t.size))
def _init_draw(self):
lines = [self.line2, self.line3]
for l in lines:
l.set_data([], [])
#ani.save('test_sub.mp4')
#property
def fig(self):
return self._fig
plt.show()
class MainPage(tk.Tk):
def __init__(self, *args, **kwargs):
root = tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, "Heeeeelp")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
#********** FRAMES*******#
self.frames = {} #empty..
frame = GraphPage(container, self)
self.frames[GraphPage] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(GraphPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class GraphPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
label = tk.Label(self, text="Help")
label.grid(row=0, column=0, sticky='NW')
ani = SubplotAnimation()
canvas = FigureCanvasTkAgg(ani.fig, self)
canvas.show()
canvas.get_tk_widget().grid(row=1, column=0, rowspan=6, columnspan=3, sticky='NSEW')
app = MainPage()
app.geometry("980x640")
app.mainloop()
Okay, some things are needed to make this work.
First of all, I don't really understand why you have the code
#property
def fig(self):
return self._fig
but I think you need to just delete this. Also remove the plt.show() beneath it since that does nothing.
Then you need to rename fig in SubplotAnimation to self.fig:
self.fig = plt.figure()
self.fig.set_size_inches(10, 7)
Lastly, you should take the call to animation.TimedAnimation.__init__, put it right behind canvas = FigureCanvasTkAgg(ani.fig, self) and change it to
animation.TimedAnimation.__init__(ani, ani.fig, interval=50, blit=True)
I believe that are all the steps needed to make it work.
If you want to keep the initialization of the animation in your SubplotAnimation class, you can also make a new function to initialize it like
def _init_animation(self):
animation.TimedAnimation.__init__(self, self.fig, interval=50, blit=True)
And call it after canvas = FigureCanvasTkAgg(...) using
ani._init_animation()
I want to run my app in different tabs. Rightnow, it is running in main window. I have done some search work on how to create tabs. I found this to be useful, but not sufficient to meet my requirements
Create TAB and create textboxes that take data in the TAB-page
I want to have a feature of adding a new tab (like new tab in chrome)
Below is my code sample. I described what i require in the comments.
from PyQt4 import Qt, QtCore, QtGui
import sys
class Map(QtGui.QMainWindow):
def __init__(self,parentQExampleScrollArea=None,parentQWidget = None):
super(Map,self).__init__()
self.initUI()
#Initialize the UI
def initUI(self):
#Initilizing Menus toolbars
#This must be maintained in all tabbed panes
filename = ""
#Filename is obtained through open file button in file menu
self.filename = filename
def paintEvent(self, e):
qp = QtGui.QPainter()
qp.begin(self)
self.drawPoints(qp,self.filename)
qp.end()
def drawPoints(self, qp,FILENAME=""):
#Read contents in file
#Get the necessary coordinates
#A separate class for storing the info of all the coordinates
#Run a for loop for all the coordinates in the list
#Actually, object is created here and image of that object is set
# as a square using the coordinates
qp.setBrush(QtGui.QColor(255, 0, 20, 200))
qp.drawRect(20,20,75,75)
qp.drawRect(100,20,75,75)
self.update()
#There are many functions to handle keyboard and mouse events
def main():
#How do I modify so that I can have multiple tabs
#And show images of the coordinates in files
#Basically I want to have the feature of opening many files
# and displaying them in UI
#Basically a feature to add a new tab
#like that of in eclipse netbeans sublime etc
app = QtGui.QApplication(sys.argv)
myQExampleScrollArea = Map()
myQExampleScrollArea.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Thanks in advance.. :)
It's simply to use method int QTabWidget.addTab (self, QWidget widget, QString) to create widget in tab. In each tab, I suggest use QtGui.QWidget more than QtGui.QMainWindow;
Example;
import sys
from PyQt4 import QtGui
class QCustomWidget (QtGui.QWidget):
# Your widget to implement
# Put your override method here
def paintEvent (self, eventQPaintEvent):
currentQPainter = QtGui.QPainter()
currentQPainter.begin(self)
currentQPainter.setBrush(QtGui.QColor(255, 0, 20, 200))
currentQPainter.drawRect(20, 20, 75, 75)
currentQPainter.drawRect(100, 20, 75, 75)
self.update()
currentQPainter.end()
class QCustomTabWidget (QtGui.QTabWidget):
def __init__ (self, parent = None):
super(QCustomTabWidget, self).__init__(parent)
self.addTab(QtGui.QPushButton('Test'), 'Tab 1')
self.addTab(QCustomWidget(), 'Tab 2')
myQApplication = QtGui.QApplication([])
myQCustomTabWidget = QCustomTabWidget()
myQCustomTabWidget.show()
sys.exit(myQApplication.exec_())
So handle with more tab, It's bad to create many line call int QTabWidget.addTab (self, QWidget widget, QString). Anyway, All widget has add in QTabWidget is can reference in ifself. So, your can control element in it by call QWidget QTabWidget.widget (self, int index).
Example to call widget in tab widget;
import os
import sys
from PyQt4 import QtCore, QtGui
class QCustomLabel (QtGui.QLabel):
def __init__(self, imagePath, parentQWidget = None):
super(QCustomLabel, self).__init__(parentQWidget)
self.setPixmap(QtGui.QPixmap(imagePath))
class QCustomWidget (QtGui.QWidget):
def __init__ (self, parentQWidget = None):
super(QCustomWidget, self).__init__(parentQWidget)
self.addQPustButton = QtGui.QPushButton('Open image')
self.addQPustButton.setMaximumWidth(120)
self.addQPustButton.released.connect(self.openImage)
self.workSpaceQTabWidget = QtGui.QTabWidget()
self.workSpaceQTabWidget.setTabsClosable(True)
self.workSpaceQTabWidget.tabCloseRequested.connect(self.closeImage)
allQVBoxLayout = QtGui.QVBoxLayout()
allQVBoxLayout.addWidget(self.addQPustButton)
allQVBoxLayout.addWidget(self.workSpaceQTabWidget)
self.setLayout(allQVBoxLayout)
def openImage (self):
path = QtGui.QFileDialog.getOpenFileName(self, 'Open image')
if not path.isEmpty():
self.workSpaceQTabWidget.addTab(QCustomLabel(path), QtCore.QString(os.path.basename(str(path))))
def closeImage (self, currentIndex):
currentQWidget = self.workSpaceQTabWidget.widget(currentIndex)
currentQWidget.deleteLater()
self.workSpaceQTabWidget.removeTab(currentIndex)
myQApplication = QtGui.QApplication([])
myQCustomWidget = QCustomWidget()
myQCustomWidget.show()
sys.exit(myQApplication.exec_())