QTreeView not updating after inserting new data in QAbstractItemModel with a QSortFilterProxyModel - pyside6

I have a TreeView which is displaying items from an AbstractItemModel..
Now I wanted to add extra Filter functionality to my application, but somehow, the data is not visible in the TreeView (after calling newData()).
How does the interaction between the QAbstractItemModel and the QSortFilterProxyModel happens?
what should the QSortFilterProxyModel knows more the the setSource(QAbstractItemModel)
Here my code (copied from: https://stackoverflow.com/a/60910989/298487)
import logging
import sys
from PySide6 import QtCore, QtWidgets
from PySide6.QtCore import QSortFilterProxyModel
class DBObject:
def __init__(self, name, parent, children=None):
self.name = name
self.parent = parent
self.children = children or list()
def __repr__(self):
return f"name: {self.name}, parent: {self.parent.name if self.parent is not None else '-'}"
class Model(QtCore.QAbstractItemModel):
def __init__(self, parent=None):
super().__init__(parent)
self._root = DBObject("root", None)
def newData(self):
items = ["foo", "bar", "baz"]
for x in items:
child = DBObject(x + "0", self._root)
self._root.children.append(child)
for y in items:
child.children.append(DBObject(y + "1", child))
def columnCount(self, parent=QtCore.QModelIndex()):
return 1
def rowCount(self, parent=QtCore.QModelIndex()):
if not parent.isValid():
return 1
parentItem = parent.internalPointer()
rowCount = len(parentItem.children)
logging.info(f"rowCount({parentItem}): rowCount={rowCount}")
return rowCount
def parent(self, index):
if not index.isValid():
return QtCore.QModelIndex()
item = index.internalPointer()
parentItem = item.parent
logging.info(f"parent({item}): parent={parentItem}")
if parentItem is None:
return QtCore.QModelIndex()
else:
if parentItem.parent is None:
return self.createIndex(0, 0, parentItem)
else:
return self.createIndex(parentItem.parent.children.index(parentItem), 0, parentItem)
def index(self, row, column, parent=QtCore.QModelIndex()):
if not parent.isValid():
if row != 0 or column != 0:
return QtCore.QModelIndex()
else:
logging.info(f"index({row}, {column}, None): index={self._root}")
return self.createIndex(0, 0, self._root)
parentItem = parent.internalPointer()
if 0 <= row < len(parentItem.children):
logging.info(f"index({row}, {column}, {parentItem}): index={parentItem.children[row]}")
return self.createIndex(row, column, parentItem.children[row])
else:
logging.info(f"index({row}, {column}, {parentItem}): index=None")
return QtCore.QModelIndex()
def data(self, index, role=QtCore.Qt.ItemDataRole.DisplayRole):
if not index.isValid():
return None
item = index.internalPointer()
if role == QtCore.Qt.ItemDataRole.DisplayRole:
return item.name
else:
return None
def flags(self, index):
if not index.isValid():
return QtCore.Qt.ItemFlag.NoItemFlags
return (
QtCore.Qt.ItemFlag.ItemIsEnabled
| QtCore.Qt.ItemFlag.ItemIsSelectable)
class ProxyModel(QSortFilterProxyModel):
def __init__(self, parent=None):
super().__init__(parent)
self.setFilterKeyColumn(0)
self.setRecursiveFilteringEnabled(True)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setMinimumSize(640, 480)
centralWidget = QtWidgets.QWidget(self)
self.setCentralWidget(centralWidget)
layout = QtWidgets.QVBoxLayout(centralWidget)
self._treeView = QtWidgets.QTreeView(self)
layout.addWidget(self._treeView)
self._model = Model()
self._proxyModel = ProxyModel()
self._proxyModel.setSourceModel(self._model)
# this line will not work
self._treeView.setModel(self._proxyModel)
# if i replace it with this line, it is working
# but the filtering will not work
self._treeView.setModel(self._model)
self._proxyModel.setFilterFixedString("bar1")
button = QtWidgets.QPushButton("Add")
layout.addWidget(button)
button.clicked.connect(self._Clicked)
def _Clicked(self):
self._model.newData()
self._treeView.expandAll()
def main():
app = QtWidgets.QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
app.exec()
if __name__ == "__main__":
main()

Related

I want to show in django admin the data of a field that is in an intermediate many-to-many table

I want to obtain the data of a field that is in an intermediate table of a many-to-many relationship, the field I want is quantity, it is in PecosaMaterial
this is my code:
models.py
class Material(models.Model):
nombre_material = models.CharField(max_length=200, verbose_name="Nombre", null=True)
class Meta:
verbose_name = "Material"
verbose_name_plural = "Materiales"
def __str__(self):
texto_material = "{0} | {1}"
return texto_material.format(self.codigo_material, self.nombre_material, self.precio_unitario)
class Pecosa(models.Model):
matpecosa = models.ManyToManyField(Material, through="PecosaMaterial")
descripcion_pecosa = models.TextField(verbose_name="Justificación")
class Meta:
verbose_name = "pecosa"
verbose_name_plural = "pecosas"
def __str__(self):
return self.descripcion_pecosa
class PecosaMaterial(models.Model):
pecosa = models.ForeignKey(Pecosa, on_delete=models.CASCADE)
material = models.ForeignKey(Material, on_delete=models.CASCADE)
cantidad = models.IntegerField(verbose_name="Cantidad")
precio_total_material = models.DecimalField(max_digits=12, decimal_places=6, verbose_name="Precio total del material", null=True, blank=True)
def __unicode__(self):
return self.pecosa
admin.py:
class PecosaAdmin(ImportExportModelAdmin,admin.ModelAdmin):
inlines = [PecosaMaterialInline,]
list_per_page = 15
exclude = ('precio_total_pecosa',)
resource_class = PecosaAdminResource
# Solucionar el problema de las busquedas
# search_fields = ('solicitante',)
readonly_fields = ('creacion_pecosa', 'modificacion_pecosa')
list_display = ('descripcion_pecosa', 'cantidad', 'materiales', 'precio_unitario', 'precio_total_pecosa', 'solicitante', 'creacion_pecosa')
from .models import PecosaMaterial
def cantidad(self, obj):
cantidad = PecosaMaterial.objects.all()
return cantidad
def precio_unitario(self, obj):
return format_html("</br>".join([str(p.precio_unitario) for p in obj.matpecosa.all()]))
def materiales(self, obj):
return format_html("</br>".join([m.nombre_material for m in obj.matpecosa.all()]))
ordering = ('creacion_pecosa',)
list_filter = ('creacion_pecosa',)
As you can see with the cantidad function, I tried to obtain the data but it did not work, sorry for the bad code and thanks in advance
from .models import PecosaMaterial
def cantidad(self, obj):
cantidad = PecosaMaterial.objects.all()
return cantidad

How to print the value of lineEdit?

How to print the value of lineEdit
class MyApp(QMainWindow,MainUI):
def __init__(self):
super(MyApp, self).__init__()
QMainWindow.__init__(self,)
self.setupUi(self)
self._generator = None
self._timerId = None
sys.stdout = EmittingStream(textWritten=self.normalOutputWritten)
self.sinais_usados = []
self.lucro = 0
self.filtro = []
self.Exp = []
self.valor_Ad = float(self.lineEdit_valueEntry.text())
self.pushButton_4.clicked.connect(self.Login)
self.pushButton_7.clicked.connect(self.getFiles)
# self.check_porcentagem.stateChanged.connect(self.setarPorcentagem)
self.pushButton_3.clicked.connect(self.start)
self.pushButton.clicked.connect(self.sdd)
self.show()
# self.lineEdit_valueEntry = QLineEdit( float(self.lineEdit_valueEntry.text()))
def sdd(self):
valor_entrada = self.valor_Ad
print(valor_entrada)

ValueError Uploading Multiple FIles to Model with M2M Field

I have a Django 3.2, python 3.6 website. I am having issues uploading multiple files to a model that also has a M2M field. I get an error in the save_related method at the indicated line:
ValueError: "<Image: title>" needs to have a value for field "image_id" before this many-to-many relationship can be used.
I have used this same method to upload multiple files to models without an M2M field, so I am not sure where I am going wrong.
models.py
class Tags(models.Model):
tag_id = models.AutoField(primary_key=True)
tag_name = models.CharField(max_length=255)
class Image(models.Model):
image_id = models.AutoField(primary_key=True)
title = models.CharField(max_length=255)
description = models.TextField()
original_image = models.ImageField('original_image', upload_to=settings.ORIGINAL_IMAGE_PATH,)
exif_data = models.JSONField(default=dict)
computed_sha256 = models.CharField(editable=False, max_length=64, default="foobar")
tags = models.ManyToManyField(Tags, blank=True)
admin.py
class ImageForm(forms.ModelForm):
original_image = forms.ImageField(widget=forms.FileInput(attrs={'multiple': True}))
class ImageAdmin(admin.ModelAdmin):
form = ImageForm
class Meta:
model = Image
fields = '__all__'
def save_related(self, request, form, *args, **kwargs):
tags = form.cleaned_data.pop('tags', ())
image = form.instance
for tag in tags:
image.tags.add(tag) # error occurs here
super(ImageAdmin, self).save_related(request, form, *args, **kwargs)
def save_model(self, request, obj, form, change):
if form.is_valid():
if not change:
# Uploading one or more images))
files = request.FILES.getlist('original_image')
for f in files:
image = Image()
if "Title" not in form.cleaned_data:
form.cleaned_data['Title'] = clean_title(f.name)
image.computed_sha256 = image_processing_utils.compute_sha256(f)
image.original_image = f
image.description = form.cleaned_data['description']
image.exif_data = image_processing_utils.read_exif_data(f)
image.save()
else:
pass
I could not find a way to upload multiple files to a model with a M2M field, so I punted and took the M2M field out of the model.
models.py
class Image(models.Model):
image_id = models.AutoField(primary_key=True)
title = models.CharField(max_length=255, blank=True)
description = models.TextField()
original_image = models.FileField('original_image', upload_to=settings.ORIGINAL_IMAGE_PATH,)
exif_data = models.JSONField(default=dict)
computed_sha256 = models.CharField(editable=False, max_length=64, default="foobar")
def __str__(self):
return self.title
class Meta:
db_table = 'Image'
class ImageTags(models.Model):
image_id = models.ForeignKey(Image, on_delete=models.CASCADE)
tag_id = models.ForeignKey(Tags, on_delete=models.CASCADE)
class Meta:
db_table = 'ImageTags'
admin.py
class ImageAdminForm(forms.ModelForm):
original_image = forms.ImageField(widget=forms.ClearableFileInput(attrs={'multiple': True}))
def __init__(self, *args, **kwargs):
super(ImageAdminForm, self).__init__(*args, **kwargs)
tag_choices = Tags.objects.values_list('tag_id', 'tag_name')
self.fields['tags'] = forms.MultipleChoiceField(choices=tag_choices, widget=forms.SelectMultiple, required=False)
class ImageAdmin(admin.ModelAdmin):
list_display = ('image_id', 'title', 'description', 'views', 'original_image', 'get_tags', 'exif_data', 'created', 'updated')
readonly_fields = ('thumb_image', 'album_image', 'display_image', 'exif_data', 'views', )
form = ImageAdminForm
class Meta:
model = Image
fields = '__all__'
fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('title', 'description', 'original_image',)
}),
)
def get_form(self, request, obj=None, **kwargs):
# https://stackoverflow.com/questions/1057252/how-do-i-access-the-request-object-or-any-other-variable-in-a-forms-clean-met
logger.debug("get_form START")
kwargs['fields'] = flatten_fieldsets(self.fieldsets)
form = super(ImageAdmin, self).get_form(request, obj, **kwargs)
form.request_obj = request
logger.debug("get_form END")
return form
def get_fieldsets(self, request, obj=None):
logger.debug("get_fieldsets START")
import copy
fieldsets = copy.deepcopy(super(ImageAdmin, self).get_fieldsets(request, obj))
logger.debug("1 fieldsets=%s" % fieldsets)
change_page_fieldset = list(fieldsets[0][1]['fields'])
logger.debug("1 change_page_fieldset=%s" % change_page_fieldset)
#if obj:
if 'tags' not in change_page_fieldset:
change_page_fieldset.append('tags')
logger.debug('2 change_page_fieldset=%s' % change_page_fieldset)
fieldsets[0][1]['fields'] = tuple(change_page_fieldset)
logger.debug('2 fieldsets=%s' % fieldsets)
return fieldsets
def get_tags(self, obj):
tag_ids = list(ImageTags.objects.filter(image_id=obj).values_list("tag_id", flat=True))
tag_names = list(Tags.objects.filter(tag_id__in=tag_ids).values_list('tag_name', flat=True))
return ", ".join([t for t in tag_names])
def save_model(self, request, obj, form, change):
logger.debug("save_model START")
logger.debug("obj=%s, change=%s, valid=%s" % (obj, change, form.is_valid()))
logger.debug("changed fields=%s" % form.changed_data)
logger.debug("obj.original_image=%s" % obj.original_image)
if utils.is_celery_working():
if form.is_valid():
if not change:
# Uploading one or more images
logger.debug("\tvalid form")
logger.debug("form.cleaned_data=%s",form.cleaned_data)
logger.debug("files=%s" % request.FILES.getlist('original_image'))
files = request.FILES.getlist('original_image')
for f in files:
image = Image()
if not form.cleaned_data['title']:
image.title = clean_title(f.name)
else:
image.title = form.cleaned_data['title']
logger.debug("form.cleaned_data['title']=%s" % form.cleaned_data['title'])
logger.debug("f=%s" % f)
image.original_image = f
image.description = form.cleaned_data['description']
image.save()
# save the tags
tags = form.cleaned_data['tags']
for tag in tags:
ImageTags.objects.create(tag_id_id=int(tag), image_id_id=image.pk)
#super().save_model(request, obj, form, change)
else:
# processing a change form, so redo all the fields
pass
#super().save_model(request, obj, form, change)
else:
# error - form is invalid
pass
else:
# error - celery not working
pass
logger.debug("save_model END")

PySide & QWT object disable/destroy

I am just learning OOP and PySide. I have created a code as below.
The application doesn't do anything much (it's a development project in learning stages).
import numpy as np
import sys
from qtpy.QtWidgets import (
QWidget,
QMainWindow,
QVBoxLayout,
QAction,
QMenu,
QLabel,
QApplication,
QMessageBox,
QDesktopWidget,
)
from qtpy.QtCore import Qt, Slot, QPoint, QObject
from qwt import (
QwtPlot,
QwtPlotMarker,
QwtPlotGrid,
QwtLegend,
QwtPlotCurve,
QwtLegendData,
)
class contexMenuHelper(QObject):
def __init__(self, plot, legend, legendItem):
super(contexMenuHelper, self).__init__()
self.plot = plot
self.legend = legend
self.legendItem = legendItem
#Slot(QPoint)
def contextMenuSlot(self, pos):
context = QMenu(self.legendItem)
context.addAction(QAction("Delete", self))
context.exec_(self.legendItem.mapToGlobal(pos))
class Plot(QwtPlot, QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setAxisTitle(QwtPlot.xBottom, "X-axis")
self.setAxisTitle(QwtPlot.yLeft, "Y-axis")
self.setCanvasBackground(Qt.white)
self.setAxisScale(QwtPlot.yLeft, -2, 2)
QwtPlotGrid.make(self, color=Qt.lightGray, width=0, style=Qt.DotLine)
legend = QwtLegend()
legend.setDefaultItemMode(QwtLegendData.Checkable)
self.insertLegend(legend, QwtPlot.RightLegend)
x = np.arange(-5.0, 5.0, 0.1)
curves = []
curves.append(
QwtPlotCurve.make(
x, np.cos(x), "Cosinus", self, linecolor="red", antialiased=True
)
)
curves.append(
QwtPlotCurve.make(
x, np.sin(x), "Sinus", self, linecolor="blue", antialiased=True
)
)
self.helpers = dict()
for a in curves:
legend.legendWidget(a).setContextMenuPolicy(Qt.CustomContextMenu)
h = contexMenuHelper(self, legend, legend.legendWidget(a))
self.helpers[a] = h
legend.legendWidget(a).customContextMenuRequested.connect(h.contextMenuSlot)
QwtPlotMarker.make(
align=Qt.AlignRight | Qt.AlignTop,
linestyle=QwtPlotMarker.HLine,
color="black",
plot=self,
)
for keys, value in self.helpers.items():
print(keys)
print(value)
# insert a vertical marker at x = 0
QwtPlotMarker.make(
align=Qt.AlignRight | Qt.AlignTop,
linestyle=QwtPlotMarker.VLine,
color="black",
plot=self,
)
legend.checked.connect(self.showCurve)
self.replot()
#Slot(object, bool, int)
def showCurve(self, obj, condition, num):
obj.setVisible(not condition)
self.replot()
#Slot(object, bool, int)
def __del__(self, obj, condition):
print('Destructor called, vehicle deleted.')
class SimplePlot(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
layout = QVBoxLayout()
self.setLayout(layout)
plot = Plot()
plot.setTitle("Trigonometric")
self.setWindowTitle("Trigonometric")
layout.addWidget(plot)
label = QLabel("Press the legend to en/disable a curve")
layout.addWidget(label)
self.center()
def center(self):
qr = self.frameGeometry()
cp = QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
def closeEvent(self, event):
reply = QMessageBox.question(
self,
"Message",
"Are you sure to quit?",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No,
)
if reply == QMessageBox.Yes:
event.accept()
else:
event.ignore()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = SimplePlot()
window.show()
window.resize(800, 600)
sys.exit(app.exec_())
I made the active legend and the context menu:
I want to make it so that when I select "Delete" from the context menu, the corresponding function waveform in the graph and the corresponding object in the legend will be deleted.
I have implemented it as follows. Perhaps someone will find my thinking useful. It works correctly and as I expected although there is a tiny error in the operation itself .
Do you see what error I mean?
class contexMenuHelper(QObject):
def __init__(self, plot, legend, legendItem):
super(contexMenuHelper, self).__init__()
self.plot = plot
self.legend = legend
self.legendItem = legendItem
self.emlSel = QAction("Delete")
#Slot(QPoint)
def contextMenuSlot(self, pos):
context = QMenu(self.legendItem)
context.addAction(self.emlSel)
context.exec_(self.legendItem.mapToGlobal(pos))
self.emlSel.triggered.connect(self.destroy())
#Slot()
def destroy(self):
QwtPlotCurve.detach(self.legend)
class Plot(QwtPlot, QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setAxisTitle(QwtPlot.xBottom, "X-axis")
self.setAxisTitle(QwtPlot.yLeft, "Y-axis")
self.setCanvasBackground(Qt.white)
self.setAxisScale(QwtPlot.yLeft, -2, 2)
QwtPlotGrid.make(self, color=Qt.lightGray, width=0, style=Qt.DotLine)
legend = QwtLegend()
legend.setDefaultItemMode(QwtLegendData.Checkable)
legend.resize(100,100)
self.insertLegend(legend, QwtPlot.RightLegend)
x = np.arange(-5.0, 5.0, 0.1)
curves = []
curves.append(
QwtPlotCurve.make(
x, np.cos(x), "Cosinus", self, linecolor="red", antialiased=True
)
)
curves.append(
QwtPlotCurve.make(
x, np.sin(x), "Sinus", self, linecolor="blue", antialiased=True
)
)
self.helpers = dict()
for a in curves:
legend.legendWidget(a).setContextMenuPolicy(Qt.CustomContextMenu)
h = contexMenuHelper(self, a, legend.legendWidget(a))
self.helpers[a] = h
legend.legendWidget(a).customContextMenuRequested.connect(h.contextMenuSlot)
QwtPlotMarker.make(
align=Qt.AlignRight | Qt.AlignTop,
linestyle=QwtPlotMarker.HLine,
color="black",
plot=self,
)
QwtPlotMarker.make(
align=Qt.AlignRight | Qt.AlignTop,
linestyle=QwtPlotMarker.VLine,
color="black",
plot=self,
)
legend.checked.connect(self.showCurve)
self.replot()
#Slot(object, bool, int)
def showCurve(self, obj, condition, num):
obj.setVisible(not condition)
self.replot()
class SimplePlot(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
layout = QVBoxLayout()
self.setLayout(layout)
plot = Plot()
plot.setTitle("Trigonometric")
self.setWindowTitle("Trigonometric")
layout.addWidget(plot)
label = QLabel("Press the legend to en/disable a curve")
layout.addWidget(label)
self.center()
def center(self):
qr = self.frameGeometry()
cp = QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
def closeEvent(self, event):
reply = QMessageBox.question(
self,
"Message",
"Are you sure to quit?",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No,
)
if reply == QMessageBox.Yes:
event.accept()
else:
event.ignore()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = SimplePlot()
window.show()
window.resize(850, 600)
sys.exit(app.exec_())

Kivy: FileChooser redrawing

I want to use FileChooser for basic operation. I select folder using FileChooser, I will remove it using own function. Folder is removed and I want to show new contents of disc but contents is incorrect How do I show current right contents of disc?
My question based from problem with next code. When I removed folder in root directory, contens of FileChooserListView was incorrect. Source of problem was in discname. Last symbol in disc name wasn't '**'. After added this ( function delete_dir() isn't problem.
Builder.load_string('''
<ConfirmPopup>:
cols:1
Label:
text: root.text
GridLayout:
cols: 2
size_hint_y: None
height: '44sp'
Button:
text: 'Yes'
on_release: root.dispatch('on_answer','yes')
Button:
text: 'No'
on_release: root.dispatch('on_answer', 'no')
''')
class ConfirmPopup(GridLayout):
text = StringProperty('')
def __init__(self,**kwargs):
self.register_event_type('on_answer')
super(ConfirmPopup,self).__init__(**kwargs)
def on_answer(self, *args):
pass
class PopupYesNo(GridLayout):
def __init__(self, save_as, task):
self.save_as = save_as
self.task = task
def show_widget(self, question):
self.content = ConfirmPopup(text= question)
self.content.bind(on_answer = self._on_answer)
self.popup = Popup(title="Answer Question",
content=self.content,
size_hint=(None, None),
size=(480,400),
auto_dismiss= False)
self.popup.open()
def _on_answer(self, instance, answer):
if answer == 'yes':
self.save_as.act_task()
self.popup.dismiss()
return
class SaveAs(BoxLayout):
def __init__(self, **kwargs):
super(SaveAs,self).__init__(**kwargs)
self.orientation = 'vertical'
self.fichoo = FileChooserListView(size_hint_y = 0.8)
self.add_widget(self.fichoo)
control = GridLayout(cols = 5, row_force_default=True, row_default_height=35, size_hint_y = 0.14)
self.tein_dir = TextInput(size_hint_x = None, width = 350)
self.tein_dir.multiline = False
bt_del_dir = Button(text = 'Remove',size_hint_x = None, width = 80)
bt_del_dir.bind(on_release = self.on_delete_dir)
control.add_widget(self.tein_dir)
control.add_widget(bt_del_dir)
self.fichoo.bind(path = self.on_path_select)
self.add_widget(control)
return
def on_path_select(self, inst, val):
self.tein_dir.text = str(self.fichoo.path)
return
def on_delete_dir(self, obj):
question = 'Do You want to remove: '+ self.tein_dir.text+ '?'
self.act_task = self.delete_dir
popup = PopupYesNo(self, SaveAs.delete_dir)
popup.show_widget(question)
return
def delete_dir(self):
pos = self.fichoo.path.rfind('\\', 0, len(self.fichoo.path))
new_path = str(self.fichoo.path)[0:pos]
if new_path[-1] == ':':
new_path += '\\' # last symbol in discname is '\'
self.tein_dir.text = new_path
os.chdir(new_path)
shutil.rmtree(str(self.fichoo.path))
self.fichoo.path = new_path
return
class ExplorerApp(App):
def build(self):
self.save_as = SaveAs()
return self.save_as
if __name__ == '__main__':
ExplorerApp().run()

Resources