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()
After updating an old project after a couple of years, I got an error:
TypeError: MDDropdownMenu.open() takes 1 positional argument but 2 were given
The relevant code (it used to work, two years ago), main.py:
def build(self):
...
return Builder.load_file("main.kv")
def on_start(self):
...
self.menu_lang_append()
...
...
def menu_lang_append(self):
self.menu_lang = MDDropdownMenu(width_mult=2)
for lng in co_lang.LANG:
self.menu_lang.items.append(
{
"viewclass": "MDMenuItem",
"text": lng,
"callback": self.menu_lang_callback,
}
)
main.kv:
MDBottomAppBar:
MDToolbar:
id: ps_toolbar
title: T["co-toolbar-title"]
icon: ACTION_ICON
type: "bottom"
animate_action_button: False
next_icon: app.pulse_icon_counter() # Not a callback.
on_action_button: app.discovery_request()
right_action_items: [ ['minus-box-outline', lambda x: app.discovery_clean()], ['earth-box', lambda x: app.menu_lang.open(x)], ['dots-vertical', lambda x: app.menu_main.open(x)], ]
A very similar case here, unresolved.
I tried to add the caller like this, no luck and no wonder; runtime errors
main.py:
self.menu_lang = MDDropdownMenu(width_mult=2, caller=self.root.ids.ps_toolbar)
or
self.menu_lang = MDDropdownMenu(width_mult=2, caller=self.root.ids.ps_toolbar.right_action_items)
main.kv:
right_action_items: [... , ['earth-box', lambda x: app.menu_lang.open()], ...]
Software:
[tool.poetry.dependencies]
python = "^3.10"
Kivy = "^2.1.0"
kivymd = "^0.104.2"
According to #1203, copy/paste/run example:
from kivy.lang import Builder
from kivy.properties import ObjectProperty
from kivy.clock import Clock
from kivy.metrics import dp
from kivymd.app import MDApp
from kivymd.uix.menu import MDDropdownMenu
KV = """
BoxLayout:
MDBottomAppBar:
MDToolbar:
id: toolbar
title: "co-toolbar-title"
icon: "eye"
type: "bottom"
animate_action_button: False
right_action_items: [['earth-box', lambda x: app.menu_lang_open(x)]]
"""
class Contero(MDApp):
menu_lang = ObjectProperty()
def menu_lang_open(self, button):
self.menu_lang.caller = button
self.menu_lang.open()
def menu_item_lang_callback(self, lng):
self.menu_lang.dismiss()
# Handle lng.
def menu_lang_append(self):
items = []
for lng in ["EN", "RU"]:
items.append(
{
"viewclass": "OneLineListItem",
"text": lng,
"height": dp(48),
"on_release": lambda x=lng: self.menu_item_lang_callback(x),
}
)
self.menu_lang = MDDropdownMenu(
width_mult=2,
items=items,
caller=self.root.ids.toolbar.ids.right_actions.children[0],
)
def build(self):
return Builder.load_string(KV)
def on_start(self):
def on_start(interval):
self.menu_lang_append()
Clock.schedule_once(on_start)
Contero().run()
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_())
'''
Does not appear the buttons inside recycleview
'''
class RV(RecycleView):
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
self.bx = RecycleBoxLayout(default_size=(None, dp(56)), default_size_hint=(1, None),
size_hint=(1, None), orientation='vertical',)
self.but = Button(text= 'hola')
self.bx.add_widget(self.but)
self.bx.bind(minimum_height=self.bx.setter("height"))
self.data = [{'text': str(x)} for x in range(100)]
class TestApp(App):
def build(self):
return RV()
if __name__ == '__main__':
TestApp().run()
The class RecycleView uses the attribute viewclass as data container, so you have to use self.viewclass = Button here.
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
self.bx = RecycleBoxLayout(
default_size=(None, dp(56)),
default_size_hint=(1, None),
size_hint=(1, None),
orientation='vertical',
)
self.bx.bind(minimum_height=self.bx.setter("height"))
self.add_widget(self.bx)
Clock.schedule_once(self.update_view)
def update_view(self, *args):
#Items that will be used as data-container.
self.viewclass = Button # Or, "Button"
self.data = [{'text': str(x)} for x in range(100)]
Also note that you've to schedule the data updation in order to get the view. Alternatively, you can define (almost) everything in kivy-lang without the need of scheduling. You can find an example in Kivy documentation.
infix to postfix conversion using python
I am using deque for creating stack
from collections import deque
class Infix_to_prefix:
def __init__(self):
self.container = deque()
self.output = deque()
def push(self,val):
self.container.append(val)
def pop(self):
self.container.pop()
def peek(self):
return self.container[-1]
def is_empty(self):
if len(self.container)==0:
return True
else:
return False
def is_operator(self, ch):
if ch in "+-*/^":
return True
else:
return False
def is_operand(self,ch):
if (ch>='A' and ch<='Z') or (ch>='a' and ch<='z'):
return True
else:
False
def ranking(self, top, ch):
rank = {
'+': 1,
'-': 1,
'*': 2,
'/': 2,
'^': 3
}
if rank[top]<rank[ch]:
return True
else:
return False
def Conversion(self, exp):
for ch in exp:
if self.is_operand(ch):
self.output.append(ch)
elif self.is_operator(ch):
while True:
if self.is_empty():
self.push(ch)
else:
top = self.peek()
if top == '(':
self.push(ch)
break
elif self.ranking(top, ch):
self.push(ch)
break
else:
cpop = self.pop()
self.output.append(cpop)
elif ch == '(':
self.push(ch)
elif ch == ')':
cpop = self.pop()
while cpop!='(':
self.output.append(cpop)
cpop = self.pop()
while not self.is_empty():
cpop = self.pop()
self.output.append(cpop)
print(("").join(self.output))
exp = "a+b*(c^d-e)^(f+g*h)-i"
s = Infix_to_prefix()
s.Conversion(exp)
When I am running this code it just keep running.
Is there is another method or another program for solving infix to postfix.
point out what is wrong in this code.
Please ignore from this line
also tell me why stack flow always keeps telling me to add some more detail every time i atry to ask a question.