I'm trying to update the value of a Traitsui variable to match a user-defined variable given via PyQt interface.
This seems like something so simple, but has taken me numerous hours now. The print functions I've used here show that the traits variable is updating, but the actual traits window is not refreshing to match.
import sys, os
from pyface.qt import QtGui, QtCore
os.environ['ETS_TOOLKIT'] = 'qt4'
from traits.api import HasTraits,Instance,on_trait_change,Int
from traitsui.api import View,Item,VGroup
class Traits_Scene(HasTraits):
traitsui_value = 0
def update_traitsui_value(self):
Traits_Scene.traitsui_value = P1.pyqt_value
print 'Traitsui value =',self.traitsui_value
view = View(
VGroup(
Item('traitsui_value',springy=False,show_label=True,style='readonly'),
),
resizable=True,
)
class P1(QtGui.QWidget):
pyqt_value = 0
print 'PyQt value =',pyqt_value
def __init__(self, parent=None):
super(P1, self).__init__(parent)
layout = QtGui.QGridLayout(self)
def set_value():
P1.pyqt_value = self.entry.value()
print 'PyQt value =',P1.pyqt_value
Traits_Scene().update_traitsui_value()
self.entry = QtGui.QSpinBox(self)
self.entry.setMinimum(0)
self.entry.setMaximum(10)
self.entry.setValue(0)
self.entry.setPrefix('PyQt value: ')
self.entry.valueChanged.connect(set_value)
layout.addWidget(self.entry, 0, 0, 1, 1)
self.entry.show()
self.viz1 = Traits_Scene()
self.ui1 = self.viz1.edit_traits(parent=self, kind='subpanel').control
layout.addWidget(self.ui1, 1, 0, 1, 1)
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.window = P1(self)
self.setCentralWidget(self.window)
self.show()
if __name__ == '__main__':
app = QtGui.QApplication.instance()
w = MainWindow()
sys.exit(app.exec_())
Related
I am writing a small program and ideally I want to have a fixed size of groups which can unfold in which I have further spoilers which represent items which i can open and close in order to add some entities to my system.
I have been looking for similar questions here and got to the following to work semiproperly:
I have added the -Buttons to remove those childs from the groups and a + to add childs to a group.
This seems to work fine as long as I am not removing or adding widgets.
My code looks like this:
spoilers.py
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtGui import *
from PyQt5.QtCore import *
class Spoiler(QWidget):
def __init__(self, parent=None, title='', animationDuration=300, addRemoveOption='None'):
super(Spoiler, self).__init__(parent=parent)
self.animationDuration = animationDuration
self.toggleAnimation = QParallelAnimationGroup()
self.contentArea = QScrollArea()
self.headerLine = QFrame()
self.toggleButton = QToolButton()
self.mainLayout = QGridLayout()
self.childWidgets = []
self.toggleButton.setStyleSheet("QToolButton { border: none; }")
self.toggleButton.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
self.toggleButton.setArrowType(Qt.RightArrow)
self.toggleButton.setText(str(title))
self.toggleButton.setCheckable(True)
self.toggleButton.setChecked(False)
self.addRemoveOperation = addRemoveOption
if addRemoveOption is not 'None':
if addRemoveOption is 'Add':
self.addRemoveButton = QPushButton('+')
else:
self.addRemoveButton = QPushButton('-')
self.addRemoveButton.clicked.connect(self.onAddRemoveButton)
self.contentArea.setStyleSheet("QScrollArea { background-color: white; border: none; }")
self.contentArea.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
# start out collapsed
self.contentArea.setMaximumHeight(0)
self.contentArea.setMinimumHeight(0)
# let the entire widget grow and shrink with its content
self.toggleAnimation.addAnimation(QPropertyAnimation(self, b"minimumHeight"))
self.toggleAnimation.addAnimation(QPropertyAnimation(self, b"maximumHeight"))
self.toggleAnimation.addAnimation(QPropertyAnimation(self.contentArea, b"maximumHeight"))
# don't waste space
self.mainLayout.setVerticalSpacing(0)
self.mainLayout.setContentsMargins(0, 0, 0, 0)
self.mainLayout.addWidget(self.toggleButton, 0, 0, 1, 1, Qt.AlignLeft)
if addRemoveOption is not 'None':
self.mainLayout.addWidget(self.addRemoveButton, 0, 2, 1, 1, Qt.AlignRight)
self.mainLayout.addWidget(self.contentArea, 1, 0, 1, 3)
self.setLayout(self.mainLayout)
def start_animation(checked):
arrow_type = Qt.DownArrow if checked else Qt.RightArrow
direction = QAbstractAnimation.Forward if checked else QAbstractAnimation.Backward
self.toggleButton.setArrowType(arrow_type)
self.toggleAnimation.setDirection(direction)
self.toggleAnimation.start()
self.toggleButton.clicked.connect(start_animation)
self.contentLayout = QVBoxLayout()
self.setContentLayout(self.contentLayout)
def setContentLayout(self, contentLayout):
self.contentArea.destroy()
self.contentArea.setLayout(contentLayout)
collapsedHeight = self.sizeHint().height() - self.contentArea.maximumHeight()
contentHeight = contentLayout.sizeHint().height()
for i in range(self.toggleAnimation.animationCount()-1):
spoilerAnimation = self.toggleAnimation.animationAt(i)
spoilerAnimation.setDuration(self.animationDuration)
spoilerAnimation.setStartValue(collapsedHeight)
spoilerAnimation.setEndValue(collapsedHeight + contentHeight)
contentAnimation = self.toggleAnimation.animationAt(self.toggleAnimation.animationCount() - 1)
contentAnimation.setDuration(self.animationDuration)
contentAnimation.setStartValue(0)
contentAnimation.setEndValue(contentHeight)
def addChild(self, child):
self.childWidgets += [child]
self.contentLayout.addWidget(child)
self.setContentLayout(self.contentLayout)
def removeChild(self, child):
self.childWidgets.remove(child)
#self.contentLayout.removeWidget(child)
child.destroy()
#self.setContentLayout(self.contentLayout)
def onAddRemoveButton(self):
self.addChild(LeafSpoiler(root=self))
class LeafSpoiler(Spoiler):
def __init__(self, parent=None, root=None, title=''):
if(root == None):
addRemoveOption = 'None'
else:
addRemoveOption = 'Sub'
super(LeafSpoiler, self).__init__(parent=parent, title=title, addRemoveOption=addRemoveOption)
self.root = root
def onAddRemoveButton(self):
self.root.removeChild(self)
gui.py
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from spoilers import *
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
self.setGeometry(300, 300, 300, 220)
# self.setWindowIcon(QIcon('web.png'))
self.centralWidget = QFrame()
self.centralLayout = QVBoxLayout()
self.centralWidget.setLayout(self.centralLayout)
self.spoiler1 = Spoiler(addRemoveOption='Add', title='Group 1')
self.spoiler2 = Spoiler(addRemoveOption='Add', title='Group 2')
for i in range(3):
leaf = LeafSpoiler(root=self.spoiler1)
self.spoiler1.addChild(leaf)
leaf = LeafSpoiler(root=self.spoiler2)
self.spoiler2.addChild(leaf)
self.centralLayout.addWidget(self.spoiler1)
self.centralLayout.addWidget(self.spoiler2)
self.setCentralWidget(self.centralWidget)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MainWindow()
sys.exit(app.exec_())
I am not quiet sure why this doesnt work. I assume that Spoiler.setContentLayout() is not supposed to be called more than once.
I would be very happy if someone could help me out on this one!
Greetings,
Finn
I am not too sure whether I understood your question correctly. I assume you are talking about pyqt crashing when trying to remove a Spoilerleaf? At least this is what's happening on my machine.
Your "removeChild" method seems to be a culprit here. Without knowing too much about the source of the crash, replacing this with a call to deleteLater() enables child deletion on my machine:
class LeafSpoiler(Spoiler):
# [...] same init as your's
def onAddRemoveButton(self):
self.deleteLater()
I have created a widget with QLineEdit and QLabel, I want to get input from QlineEdit and display it with QLabel. I have used Signal and Slot connection, I do not know what I do wrong, but it is not working correctly. I would like to get both values from QLineEdit and later show it.
Current window
what I want?
Code:
import os
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class WinDialog(QtWidgets.QDialog):
currenttextedited = QtCore.pyqtSignal(int)
def __init__(self, parent=None):
super(WinDialog, self).__init__(parent)
self.setGeometry(300,300,350,300)
self.setWindowTitle("Signal & Slot")
self.propertyWidget = PropertyWidget()
section_lay = QtWidgets.QHBoxLayout()
section_label = QtWidgets.QLabel("Name: ")
section_edit = QtWidgets.QLineEdit('')
length_lay = QtWidgets.QHBoxLayout()
length_label = QtWidgets.QLabel("Input a number: L = ")
self.length_edit = QtWidgets.QLineEdit('1000')
self.length_edit.setInputMask("999999")
self.length_edit.setFocus(True)
thick_lay = QtWidgets.QHBoxLayout()
thick_label = QtWidgets.QLabel("Input a text: T = ")
thick_edit = QtWidgets.QLineEdit('')
section_lay.addWidget(section_label)
section_lay.addWidget(section_edit)
length_lay.addWidget(length_label)
length_lay.addWidget(self.length_edit)
length_lay.addStretch()
thick_lay.addWidget(thick_label)
thick_lay.addWidget(thick_edit)
thick_lay.addStretch()
VB_lay = QtWidgets.QVBoxLayout()
VB_lay.addStretch()
VB_lay.addLayout(length_lay)
VB_lay.addLayout(thick_lay)
VB_lay.addStretch()
buttonBox = QtWidgets.QDialogButtonBox()
buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel
|QtWidgets.QDialogButtonBox.Ok)
buttonBox.accepted.connect(self.accept)
buttonBox.rejected.connect(self.reject)
grid = QtWidgets.QGridLayout(self)
grid.addLayout(section_lay, 0, 0, 1, 2)
grid.addLayout(VB_lay, 1, 0)
grid.addWidget(self.propertyWidget, 2, 0)
grid.addWidget(buttonBox, 3, 0, 1, 2)
self.length_edit.textEdited.connect(self.textchanged)
def textchanged(self, text):
print(text)
self.currenttextedited.emit(text)
class PropertyWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(PropertyWidget, self).__init__(parent)
HB_lay = QtWidgets.QHBoxLayout(self)
self.Displaylabel = QtWidgets.QLabel('')
HB_lay.addWidget(self.Displaylabel)
HB_lay.addStretch()
#QtCore.pyqtSlot(int)
def Display(self, text):
try:
L_Display = int(text)
T_Display = int(text)
fmt = "L = {}mm\nT = {}mm"
self.Displaylabel.setText(fmt.format(L_Display, T_Display))
except ValueError:
print("Error")
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = WinDialog()
w.show()
sys.exit(app.exec_())
according to samples in the image you want to show different texts but you are converting the same number to whole: L_Display = int(text) and T_Display = int(text) so how do you expect to show 2 different texts?, obviously the function display needs 2 entries (2 different entries to self plus I have changed to lowercase since it is recommended that the functions have a lowercase name).
Now the logic is as follows: if any of the texts of length_edit or thick_edit changes then you must call display() passing the new texts. So the solution is to use a slot that connects to the textEdited signals of both QLineEdits and in it obtain the text and pass the texts.
Finally I see that you want the QLineEdits receive only numbers so one option is to use a QIntValidator so that only numbers are acceptable (another better option is to use QSpinBox instead of QLineEdit)
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class WinDialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super(WinDialog, self).__init__(parent)
self.setGeometry(300,300,350,300)
self.setWindowTitle("Signal & Slot")
self.propertyWidget = PropertyWidget()
section_lay = QtWidgets.QHBoxLayout()
section_label = QtWidgets.QLabel("Name: ")
section_edit = QtWidgets.QLineEdit('')
length_lay = QtWidgets.QHBoxLayout()
length_label = QtWidgets.QLabel("Input a number: L = ")
self.length_edit = QtWidgets.QLineEdit()
self.length_edit.setFocus(True)
val_lenght = QtGui.QIntValidator(0, 100000, self.length_edit)
self.length_edit.setValidator(val_lenght)
thick_lay = QtWidgets.QHBoxLayout()
thick_label = QtWidgets.QLabel("Input a text: T = ")
self.thick_edit = QtWidgets.QLineEdit()
val_thick = QtGui.QIntValidator(0, 100000, self.thick_edit)
self.thick_edit.setValidator(val_thick)
section_lay.addWidget(section_label)
section_lay.addWidget(section_edit)
length_lay.addWidget(length_label)
length_lay.addWidget(self.length_edit)
length_lay.addStretch()
thick_lay.addWidget(thick_label)
thick_lay.addWidget(self.thick_edit)
thick_lay.addStretch()
VB_lay = QtWidgets.QVBoxLayout()
VB_lay.addStretch()
VB_lay.addLayout(length_lay)
VB_lay.addLayout(thick_lay)
VB_lay.addStretch()
buttonBox = QtWidgets.QDialogButtonBox()
buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel
| QtWidgets.QDialogButtonBox.Ok)
buttonBox.accepted.connect(self.accept)
buttonBox.rejected.connect(self.reject)
grid = QtWidgets.QGridLayout(self)
grid.addLayout(section_lay, 0, 0, 1, 2)
grid.addLayout(VB_lay, 1, 0)
grid.addWidget(self.propertyWidget, 2, 0)
grid.addWidget(buttonBox, 3, 0, 1, 2)
self.length_edit.textEdited.connect(self.onTextEdited)
self.thick_edit.textEdited.connect(self.onTextEdited)
def onTextEdited(self):
l = self.length_edit.text()
t = self.thick_edit.text()
self.propertyWidget.display(l, t)
class PropertyWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(PropertyWidget, self).__init__(parent)
HB_lay = QtWidgets.QHBoxLayout(self)
self.Displaylabel = QtWidgets.QLabel('')
HB_lay.addWidget(self.Displaylabel)
HB_lay.addStretch()
def display(self, l, t):
try:
L_Display = int(l)
T_Display = int(t)
fmt = "L = {}mm\nT = {}mm"
self.Displaylabel.setText(fmt.format(L_Display, T_Display))
except ValueError:
self.Displaylabel.clear()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = WinDialog()
w.show()
sys.exit(app.exec_())
What I'm trying to do is add splitter to a QGridLayout in order to resize the layout with the mouse. So for instance with this :
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
class SurfViewer(QMainWindow):
def __init__(self, parent=None):
super(SurfViewer, self).__init__()
self.parent = parent
self.setFixedWidth(300)
self.setFixedHeight(100)
self.wid = QWidget()
self.setCentralWidget(self.wid)
self.grid = QGridLayout()
l_a = QLabel('A')
l_b = QLabel('B')
l_c = QLabel('C')
l_d = QLabel('D')
l_e = QLabel('E')
l_f = QLabel('F')
l_g = QLabel('G')
l_h = QLabel('H')
l_i = QLabel('I')
self.grid.addWidget(l_a, 0, 0)
self.grid.addWidget(l_b, 0, 1)
self.grid.addWidget(l_c, 0, 2)
self.grid.addWidget(l_d, 1, 0)
self.grid.addWidget(l_e, 1, 1)
self.grid.addWidget(l_f, 1, 2)
self.grid.addWidget(l_g, 2, 0)
self.grid.addWidget(l_h, 2, 1)
self.grid.addWidget(l_i, 2, 2)
self.wid.setLayout(self.grid)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = SurfViewer(app)
ex.setWindowTitle('window')
ex.show()
sys.exit(app.exec_( ))
I get this:
What I would like is instead of the colored line, have the possibility to click and drag vertically (for green lines) and horizontally (for red lines) the grid borders.
I tried something with QSplitter directly, but I end up with:
The Horizontal splits are okay, but the vertical ones are not aligned any more:
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
class SurfViewer(QMainWindow):
def __init__(self, parent=None):
super(SurfViewer, self).__init__()
self.parent = parent
self.setFixedWidth(300)
self.setFixedHeight(100)
self.wid = QWidget()
self.setCentralWidget(self.wid)
# self.grid = QGridLayout()
self.globallayout = QVBoxLayout()
self.split_V = QSplitter(Qt.Vertical)
l_a = QLabel('A')
l_b = QLabel('B')
l_c = QLabel('C')
l_d = QLabel('D')
l_e = QLabel('E')
l_f = QLabel('F')
l_g = QLabel('G')
l_h = QLabel('H')
l_i = QLabel('I')
split_H = QSplitter(Qt.Horizontal)
split_H.addWidget(l_a)
split_H.addWidget(l_b)
split_H.addWidget(l_c)
self.split_V.addWidget(split_H)
split_H = QSplitter(Qt.Horizontal)
split_H.addWidget(l_d)
split_H.addWidget(l_e)
split_H.addWidget(l_f)
self.split_V.addWidget(split_H)
split_H = QSplitter(Qt.Horizontal)
split_H.addWidget(l_g)
split_H.addWidget(l_h)
split_H.addWidget(l_i)
self.split_V.addWidget(split_H)
self.globallayout.addWidget(self.split_V)
self.wid.setLayout(self.globallayout)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = SurfViewer(app)
ex.setWindowTitle('window')
ex.show()
sys.exit(app.exec_( ))
Update
I think I almost found a solution where a function is used so that whenever the vertical splits are changed, it re-aligns them:
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
class SurfViewer(QMainWindow):
def __init__(self, parent=None):
super(SurfViewer, self).__init__()
self.parent = parent
self.setFixedWidth(300)
self.setFixedHeight(100)
self.wid = QWidget()
self.setCentralWidget(self.wid)
# self.grid = QGridLayout()
self.globallayout = QVBoxLayout()
self.split_V = QSplitter(Qt.Vertical)
l_a = QLabel('A')
l_b = QLabel('B')
l_c = QLabel('C')
l_d = QLabel('D')
l_e = QLabel('E')
l_f = QLabel('F')
l_g = QLabel('G')
l_h = QLabel('H')
l_i = QLabel('I')
self.split_H1 = QSplitter(Qt.Horizontal)
self.split_H1.addWidget(l_a)
self.split_H1.addWidget(l_b)
self.split_H1.addWidget(l_c)
self.split_V.addWidget(self.split_H1)
self.split_H2 = QSplitter(Qt.Horizontal)
self.split_H2.addWidget(l_d)
self.split_H2.addWidget(l_e)
self.split_H2.addWidget(l_f)
self.split_V.addWidget(self.split_H2)
self.split_H3 = QSplitter(Qt.Horizontal)
self.split_H3.addWidget(l_g)
self.split_H3.addWidget(l_h)
self.split_H3.addWidget(l_i)
self.split_V.addWidget(self.split_H3)
self.globallayout.addWidget(self.split_V)
self.wid.setLayout(self.globallayout)
self.split_H1.splitterMoved.connect(self.moveSplitter)
self.split_H2.splitterMoved.connect(self.moveSplitter)
self.split_H3.splitterMoved.connect(self.moveSplitter)
# self.split_H1.splitterMoved
# self.moveSplitter(0,self.split_H1.at )
def moveSplitter( self, index, pos ):
# splt = self._spltA if self.sender() == self._spltB else self._spltB
self.split_H1.blockSignals(True)
self.split_H2.blockSignals(True)
self.split_H3.blockSignals(True)
self.split_H1.moveSplitter(index, pos)
self.split_H2.moveSplitter(index, pos)
self.split_H3.moveSplitter(index, pos)
self.split_H1.blockSignals(False)
self.split_H2.blockSignals(False)
self.split_H3.blockSignals(False)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = SurfViewer(app)
ex.setWindowTitle('window')
ex.show()
sys.exit(app.exec_( ))
However, I still have an issue at the begining - the alignment is not correct :
I don't know How call the function moveSplitter in the __init__
It seems that directly calling moveSplitter (which is a protected method) may be problematic. Using Qt-5.10.1 with PyQt-5.10.1 on Linux, I found that it can often result in a core dump when called during __init__. There is probably a good reason why Qt provides setSizes as a public method for changing the position of the splitters, so it may be wise to prefer it over moveSplitter.
With that in mind, I arrived at the following implementation:
class SurfViewer(QMainWindow):
def __init__(self, parent=None):
...
self.split_H1.splitterMoved.connect(self.moveSplitter)
self.split_H2.splitterMoved.connect(self.moveSplitter)
self.split_H3.splitterMoved.connect(self.moveSplitter)
QTimer.singleShot(0, lambda: self.split_H1.splitterMoved.emit(0, 0))
def moveSplitter(self, index, pos):
sizes = self.sender().sizes()
for index in range(self.split_V.count()):
self.split_V.widget(index).setSizes(sizes)
The single-shot timer is needed because on some platforms the geometry of the window may not be fully initialized before it is shown on screen. And note that setSizes does not trigger splitterMoved, so there is no need to block signals when using it.
Why does my application crash when i run the function setup_controls() twice.
Am I missing a 'parent/self' somewhere that is critical in the design?
import sys
from PySide import QtGui, QtCore
class QCategoryButton(QtGui.QPushButton):
def __init__(self, Text, treeitem, parent=None):
super(QCategoryButton, self).__init__(Text, parent)
self.treeitem = treeitem
def mousePressEvent(self, event):
mouse_button = event.button()
if mouse_button == QtCore.Qt.LeftButton:
self.treeitem.setExpanded(not self.treeitem.isExpanded())
class Example(QtGui.QWidget):
def __init__(self,):
super(Example, self).__init__()
self.initUI()
def initUI(self):
# formatting
self.resize(300, 300)
self.setWindowTitle("Example")
# widgets
self.ui_treeWidget = QtGui.QTreeWidget()
self.ui_treeWidget.setRootIsDecorated(False)
self.ui_treeWidget.setHeaderHidden(True)
self.ui_treeWidget.setIndentation(0)
self.setup_controls()
# self.setup_controls()
# layout
self.mainLayout = QtGui.QGridLayout(self)
self.mainLayout.addWidget(self.ui_treeWidget)
self.show()
def setup_controls(self):
# Add Category
pCategory = QtGui.QTreeWidgetItem()
self.ui_treeWidget.addTopLevelItem(pCategory)
self.ui_toggler = QCategoryButton('Settings', pCategory)
self.ui_treeWidget.setItemWidget(pCategory, 0, self.ui_toggler)
pFrame = QtGui.QFrame(self.ui_treeWidget)
pLayout = QtGui.QVBoxLayout(pFrame)
self.ui_ctrl = QtGui.QPushButton('Great')
self.ui_ctrlb = QtGui.QPushButton('Cool')
pLayout.addWidget(self.ui_ctrl)
pLayout.addWidget(self.ui_ctrlb)
pContainer = QtGui.QTreeWidgetItem()
pContainer.setDisabled(False)
pCategory.addChild(pContainer)
self.ui_treeWidget.setItemWidget(pContainer, 0, pFrame)
# Main
# ------------------------------------------------------------------------------
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
The setItemWidget method takes ownership of the widget that is passed to it. If you don't keep a reference it, it could get garbage-collected by Python. But of course Qt knows nothing about Python, so when it subsequently tries to access the widget that is no longer there ... boom!
This is the problematic line:
self.ui_toggler = QCategoryButton('Settings', pCategory)
On the second call, the previous widget stored in self.ui_toggler will get deleted, because there is no other reference held for it (on the Python side). So instead you should do this:
ui_toggler = QCategoryButton('Settings', pCategory, self)
self.ui_treeWidget.setItemWidget(pCategory, 0, ui_toggler)
I'm trying to populate a QTableWidget with the attributes of a file opened elsewhere in the script. I've successfully set the file attributes read in from the file using the following code:
class FileHeader(object):
fileheader_fields=(
"filetype","fileversion","numframes",
"framerate","resolution","numbeams",
"samplerate","samplesperchannel","receivergain",
"windowstart","winlengthsindex","reverse",
"serialnumber","date","idstring","ID1","ID2",
"ID3","ID4","framestart","frameend","timelapse",
"recordInterval","radioseconds","frameinterval","userassigned")
fileheader_formats=(
'S3','B','i4','i4','i4','i4','f','i4','i4','i4',
'i4','i4','i4','S32','S256','i4','i4','i4','i4',
'i4','i4','i4','i4','i4','i4','S136')
IMAGE_HEADER_BYTES = 256
IMAGES_DATA_BYTES = 49152
def __init__(self,filename=''):
self.filename = filename
if filename:
self.setFile(filename)
else:
# initialize our attributes to None
for field in self.fileheader_fields:
setattr(self,field,None)
def setFile(self, f):
self.infile=open(f, 'rb')
dtype=dict(names=self.fileheader_fields, formats=self.fileheader_formats)
self.fileheader=np.fromfile(self.infile, dtype=dtype, count=1)
self.fileheader_length=self.infile.tell()
for field in self.fileheader_fields:
setattr(self,field,self.fileheader[field])
I've used this code to populate the table but I keep getting a "FileHeader has no attribute fileheader" error.
from fileheader import FileHeader, Frame
from echogram import QEchogram
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import os, sys
class MainWindow(QWidget):
def __init__(self, parent=None):
self.fileheader_fields=FileHeader.fileheader_fields
self.fileheader_values=FileHeader.fileheader[field]
self.fileheader={field: "value of" + field
for field in self.fileheader_fields}
super(MainWindow, self).__init__(parent)
self.fileheader_table=QTableWidget()
layout=QVBoxLayout()
layout.addWidget(self.fileheader_table)
self.setLayout(layout)
self.populate
def populate(self):
self.fileheader_table.setRowCount(len(self.fileheader_fields))
self.fileheader_table.sestColumnCount(2)
self.fileheader_table.setHorizontalHeaderLabels(['name','value'])
for i,field in enumerate(self.fileheader_fields):
name=QTableWidgetItem(field)
value=QTableWidgetItem(self.fileheader[field])
self.fileheader_table.setItem(i,0,name)
self.fileheader_table.setItem(i,1,value)
if __name__=="__main__":
app=QApplication(sys.argv)
filename=str(QFileDialog.getOpenFileName(None,"open file","C:/vprice/DIDSON/DIDSON Data","*.ddf"))
wnd=MainWindow()
wnd.resize(640,480)
wnd.show()
#echoGram=QEchogram()
#echoGram.initFromFile(filename)
#fileName="test.png"
#echoGram.processEchogram()
#dataH=echoGram.data
#print "Horizontal data", dataH
Bear with me-- I just started with all of the Python stuff about a month ago...
See populate method. Also there is some examples in documentation
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtCore, QtGui
class MainWindow(QtGui.QWidget):
def __init__(self, parent=None):
self.fileheader_fields=(
"filetype","fileversion","numframes",
"framerate","resolution","numbeams",
"samplerate","samplesperchannel","receivergain",
"windowstart","winlengthsindex","reverse",
"serialnumber","date","idstring","ID1","ID2",
"ID3","ID4","framestart","frameend","timelapse",
"recordInterval","radioseconds","frameinterval","userassigned"
)
# just for test
self.fileheader = {field: 'value of ' + field
for field in self.fileheader_fields}
super(MainWindow, self).__init__(parent)
self.table_widget = QtGui.QTableWidget()
layout = QtGui.QVBoxLayout()
layout.addWidget(self.table_widget)
self.setLayout(layout)
self.populate()
def populate(self):
self.table_widget.setRowCount(len(self.fileheader_fields))
self.table_widget.setColumnCount(2)
self.table_widget.setHorizontalHeaderLabels(['name', 'value'])
for i, field in enumerate(self.fileheader_fields):
name = QtGui.QTableWidgetItem(field)
value = QtGui.QTableWidgetItem(self.fileheader[field])
self.table_widget.setItem(i, 0, name)
self.table_widget.setItem(i, 1, value)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
wnd = MainWindow()
wnd.resize(640, 480)
wnd.show()
sys.exit(app.exec_())
UPD
Code for your concrete case:
from fileheader import FileHeader, Frame
from echogram import QEchogram
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import os, sys
class MainWindow(QWidget):
def __init__(self, filename, parent=None):
super(MainWindow, self).__init__(parent)
# here we are loading file
# now self.fileheader contains attributes
self.fileheader = FileHeader(filename)
self.fileheader_table = QTableWidget()
layout = QVBoxLayout()
layout.addWidget(self.fileheader_table)
self.setLayout(layout)
self.populate()
def populate(self):
self.fileheader_table.setRowCount(len(self.fileheader.fileheader_fields))
self.fileheader_table.sestColumnCount(2)
self.fileheader_table.setHorizontalHeaderLabels(['name','value'])
for i,field in enumerate(self.fileheader.fileheader_fields):
name=QTableWidgetItem(field)
value=QTableWidgetItem(getattr(self.fileheader, field))
self.fileheader_table.setItem(i,0,name)
self.fileheader_table.setItem(i,1,value)
if __name__=="__main__":
app=QApplication(sys.argv)
filename=str(QFileDialog.getOpenFileName(None,"open file","C:/vprice/DIDSON/DIDSON Data","*.ddf"))
wnd=MainWindow(filename)
wnd.resize(640,480)
wnd.show()
#echoGram=QEchogram()
#echoGram.initFromFile(filename)
#fileName="test.png"
#echoGram.processEchogram()
#dataH=echoGram.data
#print "Horizontal data", dataH
ok u have to add a QTableWidget to your custom QtGui.QMainWindow
i would create i whith 0 line and the right amount of collumn,like this:
self.tableArea=QtGui.QTableWidget(0,self.fileheader_length,self)
self.setHorizontalHeaderLabels(['nameOne','nameTwo'])#will name your top header
self.verticalHeader().setVisible(False)#will hide your left header if you don't need them
and add your line one by one
self.tableArea.insertRow(row)#it will add a line at the row position
after that u have to create a QTableWidgetItem for each cell
itemTo=QtGui.QTableWidgetItem('text')#replace text by your value
and place your item in the cell you want
self.tableArea.setItem(row, column, itemTo)