我正在尝试标记数据跟踪的 x 跨度,并使用 tagNames、起始 x 值和结束 x 值填充表。我正在使用“突出显示”对象的字典来跟踪 x 跨度,以防以后需要对其进行编辑(增加或减少)。字典将 x 起始值映射到突出显示对象,因为 x 起始值预计是唯一的(标记的 x 跨度没有重叠)。
为了做到这一点,当用户编辑表格上的单元格时,我会发出一个信号。第一个信号连接的函数发出另一个信号(理想情况下是 xStart 与 xEnd 是否发生更改,但到目前为止我只实现了 xStart),这实际上改变了跨度的外观以匹配编辑。
几周前我问过类似的问题,但未能得到答案。老问题在这里:PyQT5 插槽参数在第一次调用后未更新。为了响应那里给出的提示,我写了以下示例:
import sys
from PyQt5.QtWidgets import *
from PyQt5 import QtCore
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import matplotlib.widgets as mwidgets
from functools import partial
class Window(QMainWindow):
def __init__(self, parent = None):
super(Window, self).__init__(parent)
self.resize(1600, 800)
self.MyUI()
def MyUI(self):
canvas = Canvas(self, width=14, height=12, dpi=100)
canvas.move(0,0)
#use this object in the dictionary to hold onto all the spans.
class highlight:
def __init__(self, tag, xStart, xEnd, highlightObj):
self.tag = tag
self.xStart = xStart
self.xEnd = xEnd
self.highlightObj = highlightObj
class Canvas(FigureCanvas):
def __init__(self, parent, width = 14, height = 12, dpi = 100):
Plot = Figure(figsize=(width, height), dpi=dpi)
self.Axes = Plot.add_subplot(111)
self.Axes.set_position([0.05, 0.58, 0.66, 0.55])
self.rowCount = 0
super().__init__(Plot)
self.setParent(parent)
##add all relevant lines to plot
self.Axes.plot([0,1,2,3,4], [3, 4, 5, 6, 7])
self.Axes.set_xlabel('Frame', fontsize = 10)
self.Axes.grid()
self.Axes.set_aspect(1)
Plot.canvas.draw()
self.highlights = {} #empty dictionary to store all the tags.
##define a table to hold the values postselection
self.taggingTable = QTableWidget(self)
self.taggingTable.setColumnCount(3)
self.taggingTable.setRowCount(100)
self.taggingTable.setGeometry(QRect(1005,85, 330, 310))
self.taggingTable.setHorizontalHeaderLabels(['Behavior','Start Frame', 'End Frame'])
Canvas.span = mwidgets.SpanSelector(self.Axes, self.onHighlight, "horizontal",
interactive = True, useblit=True, props=dict(alpha=0.5, facecolor="blue"),)
self.draw_idle()
self.taggingTable.selectionModel().selectionChanged.connect(self.onCellSelect)
self.draw_idle()
##highlighting adds a highlight item to the directory.
def onHighlight(self, xStart, xEnd):
tagName = "No Tag"
self.taggingTable.setItem(self.rowCount, 0, QTableWidgetItem(tagName))
self.taggingTable.setItem(self.rowCount, 1, QTableWidgetItem(str(int(xStart))))
self.taggingTable.setItem(self.rowCount, 2, QTableWidgetItem(str(int(xEnd))))
self.rowCount = self.rowCount + 1
highlightObj = self.Axes.axvspan(xStart, xEnd, color = 'blue', alpha = 0.5)
self.highlights[int(xStart)] = highlight(tagName, xStart, xEnd, highlightObj)
self.draw_idle()
def xStartChanged(self, xStart, rowVal):
if self.inCounter == 0:
print("xStart in slot: ", xStart)
xEnd = self.highlights[xStart].xEnd
xStartNew = int(self.taggingTable.item(rowVal, 1).text())
self.highlights[xStart].highlightObj.remove() #remove old from the plot
del self.highlights[xStart] #remove old from directory
highlightObj = self.Axes.axvspan(xStartNew, xEnd, color = 'blue', alpha = 0.5) #add new to plot
self.highlights[xStartNew] = highlight("No tagName", xStartNew, xEnd, highlightObj) #add new to directory
self.taggingTable.clearSelection() #deselect value from table
self.draw_idle()
self.inCounter = self.inCounter + 1
def onCellSelect(self):
index = self.taggingTable.selectedIndexes()
if len(index) != 0:
rowVal = index[0].row()
if not (self.taggingTable.item(rowVal, 1) is None):
xStart = int(self.taggingTable.item(rowVal, 1).text())
print("--------------")
print("xStart in signal: ", xStart)
self.inCounter = 0
self.taggingTable.itemChanged.connect(lambda: self.xStartChanged(xStart, rowVal))
app = QApplication(sys.argv)
window = Window()
window.show()
app.exec()
我运行的测试是当我突出显示两条痕迹时:
然后我成功更改了第一条轨迹:
但是,当我尝试编辑第二条跟踪时,程序崩溃了:
为了调试,我尝试检查发送和接收的信号。它产生以下输出:
--------------
xStart in signal: 0
xStart in slot: 0 ##First slot call gets correct signal
--------------
xStart in signal: 3
xStart in slot: 0 ## Second slot gets the first signal instead of the second
Traceback (most recent call last):
File "//Volumes/path/file.py", line 105, in <lambda>
self.taggingTable.itemChanged.connect(lambda: self.xStartChanged(xStart, rowVal))
File "//Volumes/path/file.py", line 86, in xStartChanged
xEnd = self.highlights[xStart].xEnd
KeyError: 0
zsh: abort python Volumes/path file.py
我尝试使用有关独特连接的在线信息,但我不确定如何实施它们。预先感谢您的帮助。
您似乎需要的是表格小部件信号,只要对特定列进行更改,该信号就会发出一个项目和其旧文本值。不幸的是, itemChanged 信号并不真正合适,因为它不指示 what 发生了变化,并且不提供以前的值。因此,要解决此限制,一种解决方案是子类化 QTableWidget / QTableWidgetItem 并发出具有所需参数的自定义信号。这将完全回避多个信号槽连接的问题。
子类的实现非常简单:
class TableWidgetItem(QTableWidgetItem):
def setData(self, role, value):
oldval = self.text()
super().setData(role, value)
if role == Qt.EditRole and self.text() != oldval:
table = self.tableWidget()
if table is not None:
table.itemTextChanged.emit(self, oldval)
class TableWidget(QTableWidget):
itemTextChanged = pyqtSignal(TableWidgetItem, str)
下面是基于您的示例的基本演示,展示了如何使用它们。 (请注意,我也没有尝试处理 xEnd,因为这超出了当前问题的范围)。
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import matplotlib.widgets as mwidgets
class Window(QMainWindow):
def __init__(self, parent = None):
super(Window, self).__init__(parent)
self.resize(1600, 800)
self.MyUI()
def MyUI(self):
canvas = Canvas(self, width=14, height=12, dpi=100)
canvas.move(0,0)
# CUSTOM SUBCLASSES
class TableWidgetItem(QTableWidgetItem):
def setData(self, role, value):
oldval = self.text()
super().setData(role, value)
if role == Qt.EditRole and self.text() != oldval:
table = self.tableWidget()
if table is not None:
table.itemTextChanged.emit(self, oldval)
class TableWidget(QTableWidget):
itemTextChanged = pyqtSignal(TableWidgetItem, str)
#use this object in the dictionary to hold onto all the spans.
class highlight:
def __init__(self, tag, xStart, xEnd, highlightObj):
self.tag = tag
self.xStart = xStart
self.xEnd = xEnd
self.highlightObj = highlightObj
class Canvas(FigureCanvas):
def __init__(self, parent, width = 14, height = 12, dpi = 100):
Plot = Figure(figsize=(width, height), dpi=dpi)
self.Axes = Plot.add_subplot(111)
self.Axes.set_position([0.05, 0.58, 0.66, 0.55])
self.rowCount = 0
super().__init__(Plot)
self.setParent(parent)
##add all relevant lines to plot
self.Axes.plot([0,1,2,3,4], [3, 4, 5, 6, 7])
self.Axes.set_xlabel('Frame', fontsize = 10)
self.Axes.grid()
self.Axes.set_aspect(1)
Plot.canvas.draw()
self.highlights = {} #empty dictionary to store all the tags.
##define a table to hold the values postselection
# USE CUSTOM TABLE SUBCLASS
self.taggingTable = TableWidget(self)
self.taggingTable.setColumnCount(3)
self.taggingTable.setRowCount(100)
self.taggingTable.setGeometry(QRect(1005,85, 330, 310))
self.taggingTable.setHorizontalHeaderLabels(['Behavior','Start Frame', 'End Frame'])
# CONNECT TO CUSTOM SIGNAL
self.taggingTable.itemTextChanged.connect(self.xStartChanged)
Canvas.span = mwidgets.SpanSelector(self.Axes, self.onHighlight, "horizontal",
interactive = True, useblit=True, props=dict(alpha=0.5, facecolor="blue"),)
self.draw_idle()
##highlighting adds a highlight item to the directory.
def onHighlight(self, xStart, xEnd):
tagName = "No Tag"
self.taggingTable.setItem(self.rowCount, 0, QTableWidgetItem(tagName))
# USE CUSTOM ITEM SUBCLASS
self.taggingTable.setItem(self.rowCount, 1, TableWidgetItem(str(int(xStart))))
self.taggingTable.setItem(self.rowCount, 2, QTableWidgetItem(str(int(xEnd))))
self.rowCount = self.rowCount + 1
highlightObj = self.Axes.axvspan(xStart, xEnd, color = 'blue', alpha = 0.5)
self.highlights[int(xStart)] = highlight(tagName, xStart, xEnd, highlightObj)
self.draw_idle()
def xStartChanged(self, item, oldVal):
try:
# VALIDATE NEW VALUES
xStart = int(oldVal)
xStartNew = int(item.text())
except ValueError:
pass
else:
print("xStart in slot: ", xStart)
xEnd = self.highlights[xStart].xEnd
self.highlights[xStart].highlightObj.remove() #remove old from the plot
del self.highlights[xStart] #remove old from directory
highlightObj = self.Axes.axvspan(xStartNew, xEnd, color = 'blue', alpha = 0.5) #add new to plot
self.highlights[xStartNew] = highlight("No tagName", xStartNew, xEnd, highlightObj) #add new to directory
self.taggingTable.clearSelection() #deselect value from table
self.draw_idle()
app = QApplication(sys.argv)
window = Window()
window.show()
app.exec()