Sometimes, I'd find it useful to dump some data from a Qt model (inheriting from QAbstractItemModel) to the console, to get a rough idea of what I'm dealing with, while debugging. The sort of thing you'd do with objects implementing __repr__. It would use the data in the display role.
Is there a quick way to do something like that? If there isn't, am I missing a point somewhere? How else would you quickly check what data a model contains, without going through the steps of implementing a view, widget, etc, and relaunching the GUI tool? How would you debug models/data in the Qt model/view framework?
I know I'm not an expert in Qt and there might be obvious contradictions in my question, but this is something I have yet to find a satisfying answer to, despite having searched the topic multiple times. Thank you for any help you're able to provide.
Here's a rough implementation in python of the type of functionality I was looking for. It works with Qt.py, with model being of a class inheriting from QAbstractItemModel:
from Qt import QtCore
def print_model_data(model, parent=None, level=0, role=QtCore.Qt.DisplayRole):
parent = parent or QtCore.QModelIndex()
for r in range(model.rowCount(parent)):
for c in range(model.columnCount(parent)):
index = model.index(r, c, parent)
if index is None:
continue
print(" " * level + str(index.data(role)))
print_model_data(model, index, level+1)
Usage:
>>> print_model_data(model)
root_0
root_0_elem_0
root_0_elem_1
root_1
root_1_elem_0
This might not cover all use cases yet, I'll update my answer if needed.
Related
Please read this whole question before answering, as it's not what you think... I'm looking at creating python object wrappers that represent hardware devices on a system (trimmed example below).
class TPM(object):
#property
def attr1(self):
"""
Protects value from being accidentally modified after
constructor is called.
"""
return self._attr1
def __init__(self, attr1, ...):
self._attr1 = attr1
...
#classmethod
def scan(cls):
"""Calls Popen, parses to dict, and passes **dict to constructor"""
Most of the constructor inputs involve running command line outputs in subprocess.Popen and then parsing the output to fill in object attributes. I've come up with a few ways to handle these, but I'm unsatisfied with what I've put together just far and am trying to find a better solution. Here are the common catches that I've found. (Quick note: tool versions are tightly controlled, so parsed outputs don't change unexpectedly.)
Many tools produce variant outputs, sometimes including fields and sometimes not. This means that if you assemble a dict to be wrapped in a container object, the constructor is more or less forced to take **kwargs and not really have defined fields. I don't like this because it makes static analysis via pylint, etc less than useful. I'd prefer a defined interface so that sphinx documentation is clearer and errors can be more reliably detected.
In lieu of **kwargs, I've also tried setting default args to None for many of the fields, with what ends up as pretty ugly results. One thing I dislike strongly about this option is that optional fields don't always come at the end of the command line tool output. This makes it a little mind-bending to look at the constructor and match it up to tool output.
I'd greatly prefer to avoid constructing a dictionary in the first place, but using setattr to create attributes will make pylint unable to detect the _attr1, etc... and create warnings. Any ideas here are welcome...
Basically, I am looking for the proper Pythonic way to do this. My requirements, for a re-summary are the following:
Command line tool output parsed into a container object.
Container object protects attributes via properties post-construction.
Varying number of inputs to constructor, with working static analysis and error detection for missing required fields during runtime.
Is there a good way of doing this (hopefully without a ton of boilerplate code) in Python? If so, what is it?
EDIT:
Per some of the clarification requests, we can take a look at the tpm_version command. Here's the output for my laptop, but for this TPM it doesn't include every possible attribute. Sometimes, the command will return extra attributes that I also want to capture. This makes parsing to known attribute names on a container object fairly difficult.
TPM 1.2 Version Info:
Chip Version: 1.2.4.40
Spec Level: 2
Errata Revision: 3
TPM Vendor ID: IFX
Vendor Specific data: 04280077 0074706d 3631ffff ff
TPM Version: 01010000
Manufacturer Info: 49465800
Example code (ignore lack of sanity checks, please. trimmed for brevity):
def __init__(self, chip_version, spec_level, errata_revision,
tpm_vendor_id, vendor_specific_data, tpm_version,
manufacturer_info):
self._chip_version = chip_version
...
#classmethod
def scan(cls):
tpm_proc = Popen("/usr/sbin/tpm_version")
stdout, stderr = Popen.communicate()
tpm_dict = dict()
for line in tpm_proc.stdout.splitlines():
if "Version Info:" in line:
pass
else:
split_line = line.split(":")
attribute_name = (
split_line[0].strip().replace(' ', '_').lower())
tpm_dict[attribute_name] = split_line[1].strip()
return cls(**tpm_dict)
The problem here is that this (or a different one that I may not be able to review the source of to get every possible field) could add extra things that cause my parser to work, but my object to not capture the fields. That's what I'm really trying to solve in an elegant way.
I've been working on a more solid answer to this the last few months, as I basically work on hardware support libraries and have finally come up with a satisfactory (though pretty verbose) answer.
Parse the tool outputs, whatever they look like, into objects structures that match up to how the tool views the device. These can have very generic dict structures, but should be broken out as much as possible.
Create another container class on top of that that which uses attributes to access items in the tool-container-objects. This enforces an API and can return sane errors across multiple versions of the tool, and across differing tool outputs!
I made a QLineEdit for reading an infix maths expression. Operators are limited to the +-*/ and brackets. Values can be numeric or a variable name representing a numeric value. I want to autocomplete for variable names.
The problem is that apparently simple QComplete only works for single pre-defined words/phrases. They don't work in between other words (As you might expect to do when modifying an expression).
I tried reading the Tree Model Completer, but since I'm programming in Python that wasn't too helpful to me. Does anyone know of a simple Tree Model Completer example coded in python?
After reading ekhumoros comment I decided to make a short example for a custom Completer.
Here is the example:
from PySide import QtGui
class CustomCompleter(QtGui.QCompleter):
def __init__(self):
super().__init__()
def splitPath(self, path):
if path.endswith('ha'):
self.setModel(QtGui.QStringListModel([path + 'llo']))
return [path]
app = QtGui.QApplication([])
e = QtGui.QLineEdit()
c = CustomCompleter()
e.setCompleter(c)
e.show()
app.exec_()
Everytime the text ends with 'ha' it proposes to continue it with 'llo'. It looks for example like:
All of the work is done in splitPath(path) of QCompleter which is called twice(?) everytime I change the text of my edit field. After some processing of the text one should set the model new with a simple string list containing one or more proposals. It seems the model has to be set again everytime. See also QCompleter Custom Completion Rules.
This is not yet the full formula parsing and variable names completion, but a reasonable step towards this. It just explains how QCompleter can be used for that goal. To summarize: Subclass QCompleter and put all your custom logic into splitpath().
after spending hours in googling, I learned a lot, but did not find any close topic to the following:
I am currently programming on dynamically created classes via a factory method. The reason is, that the method names should be populated according to a separate yaml-file. It comes down to this problem:
def create_class_with_dynamic_fuction_name(fun_name):
def fun(self):
print "Hello, I am fun"
class X:
pass
setattr(X, fun_name, fun)
return X
Kls1= create_class_with_dynamic_fuction_name('my_function_name')
kls1= Kls1()
kls1.my_function_name()
This code works. However, if you have this code in the editor of sypder (aka. Spider IDE, spyderlib), the autocomplete will not show my_function_name in its context box. Neither is it possible to inspect it via Ctrl+I for easy retrieval of the docstring-help.
The same problem arises, if a class is created with type(classname, (), clsdict). In that case, one only finds mro inside the autocomplete-context-menu.
Also I found, that it is not a problem of the closure pattern above. You can do
def create_class_with_static_function_name():
def fun(self):
print "Hello, I am fun"
class X:
my_function_name = fun
return X
Kls2= create_class_with_static_function_name()
kls2= Kls2()
kls2.my_function_name()
In such case, the my_function_name shows correctly up, but it is not what I aimed for.
Would be glad to here any suggestions to accomplish dynamic creation of classes with dynamic method names, which are correctly handled in spyder's autocompletion within the editor window.
(Spyder dev here) Sorry, it seems the completion library we use (called rope) can't do this kind of completions on dynamic classes and attributes.
You're welcome to ask on its mailing list about it, and if there's something we can do in our side, we'll be happy to help.
I've been trying to get this to work for so long now, I've read the docs here, but I can't seem to understand how to implement the GeometryConstraint.
Normally, the derivative version of this would be:
geometryConstraintNode = pm.geometryConstraint(target, object)
However, in Pymel, It looks a little nicer when setting attributes, which is why I want to use it, because it's much more readable.
I've tried this:
geometryConstraintNode = nt.GeometryConstraint(target, object).setName('geoConstraint')
But no luck, can someone take a look?
Shannon
this doesn't work for you?
import pymel.core as pm
const = pm.geometryConstraint('pSphere1', 'locator1', n='geoConstraint')
print const
const.rename('fred')
print const
output would be
geoConstraint
fred
and a constraint object named 'fred'.
The pymel node is the return value that comes back from the command defined in pm.animation.geometryConstraint. What it returns is a class wrapper for the actual in-scene constraint, which is defined in pm.nodetypes.GeometryConstraint. It's the class version where you get to do all the attribute setting, etc; the command version is a match for the same thing in maya.cmds with sometimes a little syntactic sugar added.
In this case, the pymel node is like any other pymel node, so things like renamimg use the same '.rename' functionality inherited from DagNode. You could also use functions inherited from Transform, like 'getChildren()' or 'setParent()' The docs make this clear in a round-about way by including the inheritance tree at the top of the nodetype's page. Basically all pynode returns will share at least DagNode (stuff like naming) and usually Transform (things like move, rotate, parent) or Shape (query components, etc)
Coming from the .NET world over to Python and PyQt4. Was wondering if anyone is familiar with any functionality that would allow me to bind data to Qt widgets? For example (using sqlalchemy for data):
gems = session.query(Gem).all()
list = QListWidget()
list.datasource = gems
Is such a thing possible?
Although not a direct replacement, you might find it useful to look at the QDataWidgetMapper class:
http://pyqt.sourceforge.net/Docs/PyQt4/qdatawidgetmapper.html
If you're not scared of reading C++ code, this example might also prove to be helpful:
https://doc.qt.io/qt-4.8/qt-sql-sqlwidgetmapper-example.html
Note that the mapper operates within Qt's Model/View framework. In this example, the model just happens to be a SQL database model.
One option would have a function that returns a list (or tuple) object from a query, and then use that to update the QListWidget. Remember that the QListWidget stores QListStrings. Your update function might look like this:
def updateQListWidget(qlistwidget, values):
""" Updates a QListWidget object with a list of values
ARGS:
qlistwidget - QListWidget object
values - list of values to add to list widget
"""
qlistwidget.clear()
qlist = QtCore.QStringList()
for v in values:
s = QtCore.QString(v)
qlist.append(s)
qlistwidget.addItems(qlist)