Can a QListWidget have groupings? - qt

I currently have a QListWidget that displays many items that are user selectable (and dragable). In my application, when an item is checked it will be reordered above the unchecked items. The user can also drag/drop to adjust the order of the checked items.
The problem the users have is that there are a TON of these check boxes and they are not grouped logically on the screen. Thus, I'd like to introduce grouping of some kind. Here is an example of how it currently works.
from PyQt4 import QtGui, QtCore
import sys
rows = [
{'text': 'Row1', 'value': 1, 'group': 1},
{'text': 'Row2', 'value': 2, 'group': 1},
{'text': 'Row3', 'value': 3, 'group': 1},
{'text': 'Row4', 'value': 4, 'group': 2},
{'text': 'Row5', 'value': 5, 'group': 2},
{'text': 'Row6', 'value': 6, 'group': 3},
{'text': 'Row7', 'value': 7, 'group': 3},
{'text': 'Row8', 'value': 8, 'group': 3},
{'text': 'Row9', 'value': 9, 'group': 2},
{'text': 'Row10', 'value': 10, 'group': 'testing'}
]
class MyList(QtGui.QListWidget):
def __init__(self):
QtGui.QListWidget.__init__(self)
for row in rows:
item = QtGui.QListWidgetItem(row['text'])
# These are utilizing the ItemDataRole; 33 and 34 are among the first user defined values
# http://pyqt.sourceforge.net/Docs/PyQt4/qt.html#ItemDataRole-enum
item.setData(33, row['value'])
item.setData(34, row['group'])
item.setFlags(QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable)
item.setCheckState(QtCore.Qt.Unchecked)
self.addItem(item)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
my_list = MyList()
my_list.show()
sys.exit(app.exec_())
This produces an application like this:
What I want to do is group the items that have similar groups under a similar heading. When an item is checked, it'd appear above the groups. Initially, this looks like a QTreeWidget/View, except that the checked items need to appear outside of the existing tree.
Example (text output):
[CHECKED ITEMS APPEAR HERE]
Group 1
Row1
Row2
Row3
Group 2
Row4
Row5
Row9
Group 3
Row6
Row7
Row8
Group testing
Row10
Is there a way to group items in a QListWidget, preferable so that the 'header' can be selected and all child elements can be autoselected?

to list items in groups here a simalar question: How to list items as groups in QListWidget
if headeritems and normal items are different in one property they can be handled differently in slot.
I'm not quite certain, if it's the way you want. I tried to place checked items on the top and select all items of a group by click on the headeritem.
By selecting another signal and modyfiing the slot there are many possibilities, to change the behaviour. Here my code (i tried it in PyPt5 by replacing QtGui by QtWidgets
from PyQt4 import QtGui, QtCore
import sys
rows = [
{'text': 'Row1', 'value': 1, 'group': 1},
{'text': 'Row2', 'value': 2, 'group': 1},
{'text': 'Row3', 'value': 3, 'group': 1},
{'text': 'Row4', 'value': 4, 'group': 2},
{'text': 'Row5', 'value': 5, 'group': 2},
{'text': 'Row6', 'value': 6, 'group': 3},
{'text': 'Row7', 'value': 7, 'group': 3},
{'text': 'Row8', 'value': 8, 'group': 3},
{'text': 'Row9', 'value': 9, 'group': 2},
{'text': 'Row10', 'value': 10, 'group': 'testing'}
]
grouptitles = [1, 2, 3,'testing'] # list of grouptitles
def gruppe(d): # function for sorting the itemlist
return str(d['group'])
rows.sort(key=gruppe,reverse=False) # sort rows by groups
class MyList(QtGui.QListWidget):
def __init__(self):
QtGui.QListWidget.__init__(self)
self.setMinimumHeight(270)
for t in grouptitles:
item = QtGui.QListWidgetItem('Group {}'.format(t))
item.setData(33, 'header')
item.setData(34, t)
item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable)
self.addItem(item)
for row in rows:
if row['group'] == t:
item = QtGui.QListWidgetItem(row['text'])
# These are utilizing the ItemDataRole; 33 and 34 are among the first user defined values
# http://pyqt.sourceforge.net/Docs/PyQt4/qt.html#ItemDataRole-enum
item.setData(33, row['value'])
item.setData(34, row['group'])
item.setFlags(QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable)
item.setCheckState(QtCore.Qt.Unchecked)
self.addItem(item)
else:
pass
self.setSelectionMode(QtGui.QAbstractItemView.MultiSelection) #
self.itemClicked.connect(self.selManager) # select an appropriate signal
def selManager(self, item):
if item.data(33) == 'header':
groupcode = item.data(34)
for i in range(0,self.count()):
if self.item(i).data(34) == groupcode and self.item(i).data(33) != 'header':
b = True if self.item(i).isSelected() == False else False
self.item(i).setSelected(b)
else:
if item.checkState() == QtCore.Qt.Unchecked:
item.setCheckState(QtCore.Qt.Checked)
self.moveItem(self.currentRow(),0)
else:
item.setCheckState(QtCore.Qt.Unchecked)
text = 'Group {}'.format(item.data(34))
new = self.indexFromItem(self.findItems(text, QtCore.Qt.MatchExactly)[0]).row() # find the row of the headeritem
self.moveItem(self.currentRow(), new) # moving back to group
def moveItem(self, old, new): # from row(old) to row(new)
ni = self.takeItem(old)
self.insertItem(new,ni)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
my_list = MyList()
my_list.show()
sys.exit(app.exec_())

Related

Company name extraction with bert-base-ner: easy way to know which words relate to which?

Hi I'm trying to extract the full company name from a string description about the company with bert-base-ner. I am also open to trying other methods but I couldn't really find one. The issue is that although it tags the orgs correctly, it tags it by word/token so I can't easily extract the full company name without having to concat and build it myself.
Is there an easier way or model to do this?
Here is my code:
from transformers import AutoTokenizer, AutoModelForTokenClassification
from transformers import pipeline
tokenizer = AutoTokenizer.from_pretrained("dslim/bert-base-NER")
model = AutoModelForTokenClassification.from_pretrained("dslim/bert-base-NER")
nlp = pipeline("ner", model=model, tokenizer=tokenizer)
ner_results = nlp(text1)
print(ner_results)
Here is my output for one text string:
[{'entity': 'B-ORG', 'score': 0.99965024, 'index': 1, 'word': 'Orion', 'start': 0, 'end': 5}, {'entity': 'I-ORG', 'score': 0.99945647, 'index': 2, 'word': 'Metal', 'start': 6, 'end': 11}, {'entity': 'I-ORG', 'score': 0.99943095, 'index': 3, 'word': '##s', 'start': 11, 'end': 12}, {'entity': 'I-ORG', 'score': 0.99939036, 'index': 4, 'word': 'Limited', 'start': 13, 'end': 20}, {'entity': 'B-LOC', 'score': 0.9997398, 'index': 14, 'word': 'Australia', 'start': 78, 'end': 87}]
I have faced a similar issue and solved it by using a better model called "xlm-roberta-large-finetuned-conll03-English" which is much better than the one you're using right now and will render the complete organization's name rather than the broken pieces. Feel free to test out the below-mentioned code which will extract the full organization's list from the document. Accept my answer by clicking on tick button if it founds useful.
from transformers import pipeline
from subprocess import list2cmdline
from pdfminer.high_level import extract_text
import docx2txt
import spacy
from spacy.matcher import Matcher
import time
start = time.time()
nlp = spacy.load('en_core_web_sm')
matcher = Matcher(nlp.vocab)
model_checkpoint = "xlm-roberta-large-finetuned-conll03-english"
token_classifier = pipeline(
"token-classification", model=model_checkpoint, aggregation_strategy="simple"
)
def text_extraction(file):
""""
To extract texts from both pdf and word
"""
if file.endswith(".pdf"):
return extract_text(file)
else:
resume_text = docx2txt.process(file)
if resume_text:
return resume_text.replace('\t', ' ')
return None
# Organisation names extraction
def org_name(file):
# Extract the complete text in the resume
extracted_text = text_extraction(file)
classifier = token_classifier(extracted_text)
# Get the list of dictionary with key value pair "entity":'ORG'
values = [item for item in classifier if item["entity_group"] == "ORG"]
# Get the list of dictionary with key value pair "entity":'ORG'
res = [sub['word'] for sub in values]
final1 = list(set(res)) # Remove duplicates
final = list(filter(None, final1)) # Remove empty strings
print(final)
org_name("your file name")
end = time.time()
print("The time of execution of above program is :", round((end - start), 2))

jq (map(select))Cannot index string with string "ID" exit status 5

data:
[{"id": "first", "val": 1}, {"id": "second", "val": 2}, {"id": "second", "val": 3}]
query:
.[] | map(select(.id == "second"))
expected:
{"id": "second", "val": 2}, {"id": "second", "val": 3}
result:
Cannot index string with string "id"
https://jqplay.org/jq?q=.%5B%5D%20%7C%20select(.id%20%3D%3D%20%22second%22)&j=%5B%7B%22id%22%3A%20%22first%22%2C%20%22val%22%3A%201%7D%2C%20%7B%22id%22%3A%20%22second%22%2C%20%22val%22%3A%202%7D%5D
Why could be this result?
map(select(.id == "second")) expects an array of objects, however .[] takes them out of the array so everything after that must be able to work on single item.
In your jqplay example your filter is .[] | select(.id == "second") and is working fine.
It should be obvious now how to fix your issue ;)

ipywidgets dropdown onclick

It seems that there is no on_click option with dropdown widgets, I was wondering if there is some sort of workaround. One method I was thinking was, everytime an option is chosen, to flush the options and start the dropdown from the top again, where the top option would be the empty "".
For instance suppose I have:
from IPython.html import widgets
from IPython.display import display
def dropdown_event_handler(change):
print(change.new)
# flush the options and start from "" again
options = ["", "A", "B"]
dropdown = widgets.Dropdown(options=options, description="Categories")
dropdown.observe(dropdown_event_handler, names="value")
display(dropdown)
So the desired behaviour is that if I press "A" and "A" again, A would be printed out twice.
As you already suggested, you could set the value of the widget to "" after each change:
from IPython.html import widgets
from IPython.display import display
def dropdown_event_handler(change):
print(change.new)
dropdown.value = ""
options = ["", "A", "B"]
dropdown = widgets.Dropdown(options=options, description="Categories")
dropdown.observe(dropdown_event_handler, names='value')
display(dropdown)
And I fear that is your only option. The Dropdown widget has no other type than "change". You can see all available types by printing them with type=All.
from IPython.html import widgets
from IPython.display import display
from traitlets import All
def dropdown_event_handler(change):
print(change)
options = ["", "A", "B"]
dropdown = widgets.Dropdown(options=options, description="Categories")
dropdown.observe(dropdown_event_handler, type=All)
display(dropdown)
Output:
{'name': '_property_lock', 'old': traitlets.Undefined, 'new': {'index': 1}, 'owner': Dropdown(description='Categories', options=('', 'A', 'B'), value=''), 'type': 'change'}
{'name': 'label', 'old': '', 'new': 'A', 'owner': Dropdown(description='Categories', index=1, options=('', 'A', 'B'), value=''), 'type': 'change'}
{'name': 'value', 'old': '', 'new': 'A', 'owner': Dropdown(description='Categories', index=1, options=('', 'A', 'B'), value='A'), 'type': 'change'}
{'name': 'index', 'old': 0, 'new': 1, 'owner': Dropdown(description='Categories', index=1, options=('', 'A', 'B'), value='A'), 'type': 'change'}
{'name': '_property_lock', 'old': {'index': 1}, 'new': {}, 'owner': Dropdown(description='Categories', index=1, options=('', 'A', 'B'), value='A'), 'type': 'change'}
So you can't observe a value in a Dropdown widget if it did not change. For more information see the Traitlets documentation.

collection findall in an array list

I am using groovy and I have a collection :
person 1: age - 1, weight - 25
person 2: age - 2, weight - 20
person 3: age - 3, weight - 25
I need to find all persons whose age or weight is in the list of valid age/weight returned by a method called getValidAgeForSchool() or getValidWeightForSchool() ex. ages [2,3] or weight [20,25]
I know there is something like this (not working too)
persons.findAll{ it.age == 2 || it.weight == 20}
but how I can say (like the IN Clause)
persons.findAll {it.age in [2,3] || it.weight in [20,25]}.
I also tried this (ignoring the weight for now) but not returning the list when it is supposed to
persons.age.findAll{ it == 2 || it == 3}
thanks.
The code you have works:
def people = [
[ id: 1, age: 1, weight: 25 ],
[ id: 2, age: 2, weight: 20 ],
[ id: 3, age: 3, weight: 25 ]
]
// This will find everyone (as everyone matches your criteria)
assert people.findAll {
it.age in [ 2, 3 ] || it.weight in [ 20, 25 ]
}.id == [ 1, 2, 3 ]
It also works if you have a list of instances like so:
class Person {
int id
int age
int weight
}
def people = [
new Person( id: 1, age: 1, weight: 25 ),
new Person( id: 2, age: 2, weight: 20 ),
new Person( id: 3, age: 3, weight: 25 )
]
I'm assuming your problem is that you have weight as a double or something?
If weight is a double, you'd need to do:
people.findAll { it.age in [ 2, 3 ] || it.weight in [ 20d, 25d ] }.id
But beware, this is doing double equality comparisons, so if you are doing any arithmetic on the weight, you may fall victim to rounding and accuracy errors

How to update a Python dictionary with a reference dictionary the Pythonic way?

I think it is pretty straightforward. All I am trying to do is update the original dictionary's 'code' with that of another dictionary which has the value. I get a feeling 2 for loops and an IF loop can be further shortened to get the answer. In my actual problem, I have few 1000's of dicts that I have to update. Thanks guys!
Python:
referencedict = {'A': 'abc', 'B': 'xyz'}
mylistofdict = [{'name': 'John', 'code': 'A', 'age': 28}, {'name': 'Mary', 'code': 'B', 'age': 32}, {'name': 'Joe', 'code': 'A', 'age': 43}]
for eachdict in mylistofdict:
for key, value in eachdict.items():
if key == 'code':
eachdict[key] = referencedict[value]
print mylistofdict
Output:
[{'age': 28, 'code': 'abc', 'name': 'John'}, {'age': 32, 'code': 'xyz', 'name': 'Mary'}, {'age': 43, 'code': 'abc', 'name': 'Joe'}]
There is no need to loop over all values of eachdict, just look up code directly:
for eachdict in mylistofdict:
if 'code' not in eachdict:
continue
eachdict['code'] = referencedict[eachdict['code']]
You can probably omit the test for code being present, your example list always contains a code entry, but I thought it better to be safe. Looking up the code in the referencedict structure assumes that all possible codes are available.
I used if 'code' not in eachdict: continue here; the opposite is just as valid (if 'code' in eachdict), but this way you can more easily remove the line if you do not need it, and you save yourself an indent level.
referencedict = {'A': 'abc', 'B': 'xyz'}
mylistofdict = [{'name': 'John', 'code': 'A', 'age': 28}, {'name': 'Mary', 'code': 'B', 'age': 32}, {'name': 'Joe', 'code': 'A', 'age': 43}]
for x in mylistofdict:
try:
x['code']=referencedict.get(x['code'])
except KeyError:
pass
print(mylistofdict)

Resources