When you print out a QModelIndex in Pyside, the object representation shows the row, column, parent, model, and memory address. However, if you print out index.model(), the memory address for the model is different.
Here is some code that demonstrates what I mean:
from PySide import QtGui, QtCore
class TestQModelIndexModelWin(QtGui.QMainWindow):
def __init__(self, parent=None):
super(TestQModelIndexModelWin, self).__init__(parent)
self.listView = QtGui.QListView()
self.setCentralWidget(self.listView)
listModel = QtGui.QStringListModel(['foo', 'bar', 'baz'])
self.listView.setModel(listModel)
numItems = len(listModel.stringList())
for i in range(numItems):
index = listModel.index(i, 0)
print index
print index.model()
When running this code, the results look something like the following:
<PySide.QtCore.QModelIndex(0,0,0x0,QStringListModel(0xef1b7e0) ) at 0x0000000017656D08>
<PySide.QtGui.QStringListModel object at 0x0000000017656948>
<PySide.QtCore.QModelIndex(1,0,0x0,QStringListModel(0xef1b7e0) ) at 0x00000000176564C8>
<PySide.QtGui.QStringListModel object at 0x0000000017656948>
<PySide.QtCore.QModelIndex(2,0,0x0,QStringListModel(0xef1b7e0) ) at 0x0000000017656D08>
<PySide.QtGui.QStringListModel object at 0x0000000017656948>
Why does the QModelIndex show the QStringListModel hex value as 0xef1b7e0 but the QStringListModel shows its address as 0x0000000017656948?
The repr for index is showing the C++ address of the model it is associated with. Whereas the repr for index.model() is showing the address of the python object that wraps the C++ model.
You can verify this by using the shiboken module:
import shiboken
...
print index
print index.model()
print shiboken.dump(index.model())
which will produce output like this:
<PySide.QtCore.QModelIndex(2,0,0x0,QStringListModel(0x17b0b40) ) at 0x7ff1a3715998>
<PySide.QtGui.QStringListModel object at 0x7ff1a3715950>
C++ address....... PySide.QtGui.QStringListModel/0x17b0b40
hasOwnership...... 1
containsCppWrapper 1
validCppObject.... 1
wasCreatedByPython 1
Related
I want to load a YAML file into Python as an OrderedDict. I am using yamlordereddictloader to preserve ordering.
However, I notice that the aliased object is placed "too soon" in the OrderedDict in the output.
How can I preserve the order of this mapping when read into Python, ideally as an OrderedDict? Is it possible to achieve this result without writing some custom parsing?
Notes:
I'm not particularly concerned with the method used, as long as the end result is the same.
Using sequences instead of mappings is problematic because they can result in nested output, and I can't simply flatten everything (some nestedness is appropriate).
When I try to just use !!omap, I cannot seem to merge the aliased mapping (d1.dt) into the d2 mapping.
I'm in Python 3.6, if I don't use this loader or !!omap order is not preserved (apparently contrary to the top 'Update' here: https://stackoverflow.com/a/21912744/2343633)
import yaml
import yamlordereddictloader
yaml_file = """
d1:
id:
nm1: val1
dt: &dt
nm2: val2
nm3: val3
d2: # expect nm4, nm2, nm3
nm4: val4
<<: *dt
"""
out = yaml.load(yaml_file, Loader=yamlordereddictloader.Loader)
keys = [x for x in out['d2']]
print(keys) # ['nm2', 'nm3', 'nm4']
assert keys==['nm4', 'nm2', 'nm3'], "order from YAML file is not preserved, aliased keys placed too early"
Is it possible to achieve this result without writing some custom parsing?
Yes. You need to override the method flatten_mapping from SafeConstructor. Here's a basic working example:
import yaml
import yamlordereddictloader
from yaml.constructor import *
from yaml.reader import *
from yaml.parser import *
from yaml.resolver import *
from yaml.composer import *
from yaml.scanner import *
from yaml.nodes import *
class MyLoader(yamlordereddictloader.Loader):
def __init__(self, stream):
yamlordereddictloader.Loader.__init__(self, stream)
# taken from here and reengineered to keep order:
# https://github.com/yaml/pyyaml/blob/5.3.1/lib/yaml/constructor.py#L207
def flatten_mapping(self, node):
merged = []
def merge_from(node):
if not isinstance(node, MappingNode):
raise yaml.ConstructorError("while constructing a mapping",
node.start_mark, "expected mapping for merging, but found %s" %
node.id, node.start_mark)
self.flatten_mapping(node)
merged.extend(node.value)
for index in range(len(node.value)):
key_node, value_node = node.value[index]
if key_node.tag == u'tag:yaml.org,2002:merge':
if isinstance(value_node, SequenceNode):
for subnode in value_node.value:
merge_from(subnode)
else:
merge_from(value_node)
else:
if key_node.tag == u'tag:yaml.org,2002:value':
key_node.tag = u'tag:yaml.org,2002:str'
merged.append((key_node, value_node))
node.value = merged
yaml_file = """
d1:
id:
nm1: val1
dt: &dt
nm2: val2
nm3: val3
d2: # expect nm4, nm2, nm3
nm4: val4
<<: *dt
"""
out = yaml.load(yaml_file, Loader=MyLoader)
keys = [x for x in out['d2']]
print(keys)
assert keys==['nm4', 'nm2', 'nm3'], "order from YAML file is not preserved, aliased keys placed too early"
This has not the best performance as it basically copies all key-value pairs from all mappings once each during loading, but it's working. Performance enhancement is left as an exercise for the reader :).
Sphinx-autodoc flattens dicts, lists, and tuples - making long ones barely readable. Pretty-print format isn't always desired either, as some nested containers are better kept flattened than columned. Is there a way to display iterables as typed in source code?
Get it straight from source, and add an .rst command for it:
# conf.py
from importlib import import_module
from docutils import nodes
from sphinx import addnodes
from inspect import getsource
from docutils.parsers.rst import Directive
class PrettyPrintIterable(Directive):
required_arguments = 1
def run(self):
def _get_iter_source(src, varname):
# 1. identifies target iterable by variable name, (cannot be spaced)
# 2. determines iter source code start & end by tracking brackets
# 3. returns source code between found start & end
start = end = None
open_brackets = closed_brackets = 0
for i, line in enumerate(src):
if line.startswith(varname):
if start is None:
start = i
if start is not None:
open_brackets += sum(line.count(b) for b in "([{")
closed_brackets += sum(line.count(b) for b in ")]}")
if open_brackets > 0 and (open_brackets - closed_brackets == 0):
end = i + 1
break
return '\n'.join(src[start:end])
module_path, member_name = self.arguments[0].rsplit('.', 1)
src = getsource(import_module(module_path)).split('\n')
code = _get_iter_source(src, member_name)
literal = nodes.literal_block(code, code)
literal['language'] = 'python'
return [addnodes.desc_name(text=member_name),
addnodes.desc_content('', literal)]
def setup(app):
app.add_directive('pprint', PrettyPrintIterable)
Example .rst and result:
(:autodata: with empty :annotation: is to exclude the original flattened dictionary).
Some code borrowed from this answer.
I am a teacher that is writing a program to read an 8-digit ID barcode for students who are late to school. I am an experienced programmer, but new to Python and very new to Tkinter (about 36 hours experience) I have made heavy use of this site so far, but I have been unable to find the answer to this question:
How can I read exactly 8 digits, and display those 8 digits in a textbox immediately. I can do 7, but can't seem to get it to 8. Sometimes, I will get nothing in the text box. I have used Entry, bind , and everything works OK, except I can't seem to get the keys read in the bind event to place the keys in the textbox consistently that were inputted. The ID seems to be always correct when I PRINT it, but it is not correct in the textbox. I seem unable to be allowed to show the tkinter screen, so it shows only 7 digits or nothing in the text box upon completion.
Here is a snippet of my code, that deals with the GUI
from tkinter import *
from collections import Counter
import time
i=0
class studentNumGUI():
def __init__(self, master):
master.title("Student ID Reader")
self.idScanned = StringVar()
localTime = time.asctime(time.localtime(time.time()))
self.lblTime = Label(master, text=localTime)
self.lblTime.pack()
self.lbl = Label(master, text="Enter Student ID:")
self.lbl.pack()
self.idScanned.set("")
self.idScan = Entry(master,textvariable=self.idScanned,width=12)
self.idScan.pack()
self.frame=Frame(width=400,height=400)
self.frame.pack()
self.frame.focus()
self.frame.bind('<Key>',self.key)
def key(self,event):
global i
self.frame.focus()
self.idScan.insert(END,event.char)
print(repr(event.char)," was pressed") #just to make sure that my keystrokes are accepted
if (i < 7):
i += 1
else:
#put my other python function calls here once I fix my problem
self.frame.after(2000)
#self.idScan.delete(0,END) #Then go blank for the next ID to be read
i=0
root = Tk()
nameGUI = studentNumGUI(root)
root.mainloop()
enter image description here
You are doing some unusual things in order to place text inside the Entry field based on keypresses. I've changed your code so that it sets the focus on the Entry widget and will check the contents of the Entry field each time a key is pressed (while the Entry has focus). I'm then getting the contents of the Entry field and checking if the length is less than 8. If it is 8 (or greater) it will clear the box.
How does this work for you?
I've left in the commented out code
from tkinter import *
from collections import Counter
import time
class studentNumGUI():
def __init__(self, master):
master.title("Student ID Reader")
self.idScanned = StringVar()
localTime = time.asctime(time.localtime(time.time()))
self.lblTime = Label(master, text=localTime)
self.lblTime.pack()
self.lbl = Label(master, text="Enter Student ID:")
self.lbl.pack()
self.idScanned.set("")
self.idScan = Entry(master,textvariable=self.idScanned,width=12)
self.idScan.pack()
self.idScan.focus_set()
self.frame=Frame(width=400,height=400)
self.frame.pack()
#self.frame.focus()
#self.frame.bind('<Key>',self.key)
self.idScan.bind('<Key>',self.key)
def key(self,event):
#self.frame.focus()
#self.idScan.insert(END,event.char)
print(repr(event.char)," was pressed") #just to make sure that my keystrokes are accepted
len(self.idScanned.get())
if (len(self.idScanned.get())<8):
pass
else:
#put my other python function calls here once I fix my problem
self.idScan.delete(0,END) #Then go blank for the next ID to be read
#self.frame.after(2000)
root = Tk()
nameGUI = studentNumGUI(root)
root.mainloop()
I have some data which are represented by a class of my own ; to fix the ideas I give an example.
class MyOwnModel():
def __init__(self, name="", number=0):
self.name = name
self.number = number
I then have a list of such instances, that I want to represent in a QTableView.
li = [MyOwnModel("a", 1), MyOwnModel("b", 2)]
Then I see two strategies to make a QTableView from that :
change MyOwnModel so that it subclasses QAbstractTableModel
build a new QAbstractTableModel which mimics MyOwnModel in a way that its attributes are for instance two QString and connect the dataChanged signal to a function which updates the instance of MyOwnModel
I am not completely satisfied with any of these, but I have no other idea for the moment.
Which one is the most suitable to my problem ? (I have a more complex class in practice but I would like to use the same framework)
As stated in the comment, your model is your list of object. You should subclass QAbstractTableModel to use this list.
Here's my code snippet for this:
import sys
import signal
import PyQt4.QtCore as PCore
import PyQt4.QtGui as PGui
class OneRow(PCore.QObject):
def __init__(self):
self.column0="text in column 0"
self.column1="text in column 1"
class TableModel(PCore.QAbstractTableModel):
def __init__(self):
super(TableModel,self).__init__()
self.myList=[]
def addRow(self,rowObject):
row=len(self.myList)
self.beginInsertRows(PCore.QModelIndex(),row,row)
self.myList.append(rowObject)
self.endInsertRows()
#number of row
def rowCount(self,QModelIndex):
return len(self.myList)
#number of columns
def columnCount(self,QModelIndex):
return 2
#Define what do you print in the cells
def data(self,index,role):
row=index.row()
col=index.column()
if role==PCore.Qt.DisplayRole:
if col==0:
return str( self.myList[row].column0)
if col==1:
return str( self.myList[row].column1)
#Rename the columns
def headerData(self,section,orientation,role):
if role==PCore.Qt.DisplayRole:
if orientation==PCore.Qt.Horizontal:
if section==0:
return str("Column 1")
elif section==1:
return str("Column 2")
if __name__=='__main__':
PGui.QApplication.setStyle("plastique")
app=PGui.QApplication(sys.argv)
#Model
model=TableModel()
model.addRow(OneRow())
model.addRow(OneRow())
#View
win=PGui.QTableView()
win.setModel(model)
#to be able to close wth ctrl+c
signal.signal(signal.SIGINT, signal.SIG_DFL)
#to avoid warning when closing
win.setAttribute(PCore.Qt.WA_DeleteOnClose)
win.show()
sys.exit(app.exec_())
Each element of myList is a row in the table.
Does anyone know of a YAML deserializer that can provide position information for the constructed objects?
I know how to deserialize a YAML file into a Java object. Simple instructions on http://yamlbeans.sourceforge.net/.
However, I want to do some algorithmic validation on the deserialized object and report error back to the user pointing to the position in the YAML that cause the error.
Example:
=========YAML file==========
name: Nathan Sweet
age: 28
address: 4011 16th Ave S
=======JAVA class======
public class Contact {
public String name;
public int age;
public String address;
}
Imagine if I want to first load the yaml into Contact class and then validate the address against some repository and error back if its invalid. Something like:
'Line 3 Column 9: The address does not match valid entry in the database'
The problem is, currently there is no way to get the position inside a deserialized object from YAML.
Anyone know a solution to this issue?
Most YAML parsers, if they keep any information about positions around they drop it while constructing the language native objects.
In ruamel.yaml ¹, I keep more information around because I want to be able to round-trip with minimal loss of original layout (e.g. keeping comments and key order in mappings).
I don't keep information on individual key-value pairs, but I do on the "upper-left" position of a mapping². Because of the kept order of the mapping items you can give some rather nice feedback. Given an input file:
- name: anthon
age: 53
adres: Rijn en Schiekade 105
- name: Nathan Sweet
age: 28
address: 4011 16th Ave S
And a program that you call with the input file as argument:
#! /usr/bin/env python2.7
# coding: utf-8
# http://stackoverflow.com/questions/30677517/yaml-deserializer-with-position-information?noredirect=1#comment49491314_30677517
import sys
import ruamel.yaml
up_arrow = '↑'
def key_error(key, value, line, col, error, e='E'):
print('E[{}:{}]: {}'.format(line, col, error))
print('{}{}: {}'.format(' '*col, key, value))
print('{}{}'.format(' '*(col), up_arrow))
print('---')
def value_error(key, value, line, col, error, e='E'):
val_col = col + len(key) + 2
print('{}[{}:{}]: {}'.format(e, line, val_col, error))
print('{}{}: {}'.format(' '*col, key, value))
print('{}{}'.format(' '*(val_col), up_arrow))
print('---')
def value_warning(key, value, line, col, error):
value_error(key, value, line, col, error, e='W')
class Contact(object):
def __init__(self, vals):
for offset, k in enumerate(vals):
self.check(k, vals[k], vals.lc.line+offset, vals.lc.col)
for k in ['name', 'address', 'age']:
if k not in vals:
print('K[{}:{}]: {}'.format(
vals.lc.line+offset, vals.lc.col, "missing key: "+k
))
print('---')
def check(self, key, value, line, col):
if key == 'name':
if value[0].lower() == value[0]:
value_error(key, value, line, col,
'value should start with uppercase')
elif key == 'age':
if value < 50:
value_warning(key, value, line, col,
'probably too young for knowing ALGOL 60')
elif key == 'address':
pass
else:
key_error(key, value, line, col,
"unexpected key")
data = ruamel.yaml.load(open(sys.argv[1]), Loader=ruamel.yaml.RoundTripLoader)
for x in data:
contact = Contact(x)
giving you E(rrors), W(arnings) and K(eys missing):
E[0:8]: value should start with uppercase
name: anthon
↑
---
E[2:2]: unexpected key
adres: Rijn en Schiekade 105
↑
---
K[2:2]: missing key: address
---
W[4:7]: probably too young for knowing ALGOL 60
age: 28
↑
---
Which you should be able to parser in a calling program in any language to give feedback. The check method of course need adjusting to your requirements. This is not as nice as being to do that in the language the rest of your application is in, but it might be better than nothing.
In my experience handling the above format is certainly simpler than extending an existing (open source) YAML parser.
¹ Disclaimer: I am the author of that package
² I want to use that kind of information at some point to preserve spurious newlines, inserted for readability
In python, you can readily write custom Dumper/Loader objects and use them to load (or dump) your yaml code. You can have these objects track the file/line info:
import yaml
from collections import OrderedDict
class YamlOrderedDict(OrderedDict):
"""
An OrderedDict that was loaded from a yaml file, and is annotated
with file/line info for reporting about errors in the source file
"""
def _annotate(self, node):
self._key_locs = {}
self._value_locs = {}
nodeiter = node.value.__iter__()
for key in self:
subnode = nodeiter.next()
self._key_locs[key] = subnode[0].start_mark.name + ':' + \
str(subnode[0].start_mark.line+1)
self._value_locs[key] = subnode[1].start_mark.name + ':' + \
str(subnode[1].start_mark.line+1)
def key_loc(self, key):
try:
return self._key_locs[key]
except AttributeError, KeyError:
return ''
def value_loc(self, key):
try:
return self._value_locs[key]
except AttributeError, KeyError:
return ''
# Use YamlOrderedDict objects for yaml maps instead of normal dict
yaml.add_representer(OrderedDict, lambda dumper, data:
dumper.represent_dict(data.iteritems()))
yaml.add_representer(YamlOrderedDict, lambda dumper, data:
dumper.represent_dict(data.iteritems()))
def _load_YamlOrderedDict(loader, node):
rv = YamlOrderedDict(loader.construct_pairs(node))
rv._annotate(node)
return rv
yaml.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, _load_YamlOrderedDict)
Now when you read a yaml file, any mapping objects will be read as a YamlOrderedDict, which allows looking up the file location of keys in the mapping object. You can also add an iterator method like:
def iter_with_lines(self):
for key, val in self.items():
yield (key, val, self.key_loc(key))
...and now you can write a loop like:
for key,value,location in obj.iter_with_lines():
# iterate through the key/value pairs in a YamlOrderedDict, with
# the source file location