QCompleter on QLineEdit for parts of the inserted text - python

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().

Related

How to quickly print data in a Qt model for debugging?

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.

How to add a border to a python console-menu

I'm having some issues using the console-menu module for Python. I made my menu with the constructor and added a few items to it, but i'm having a hard time figuring out how to add formatting to it. There is a MenuStyle class in the documentation that I think I need to use:
classconsolemenu.format.MenuStyle(margins=None, padding=None, border_style=None, border_style_type=None, border_style_factory=None)
The full documentation is available here: https://console-menu.readthedocs.io/en/latest/consolemenu.html
It's pretty short and to the point. I just don't understand what to do. Do I need to construct the border object and then use it in the ConsoleMenu() constructor? or add it in later?
From reading the documentation it looks like you need to set ConsoleMenu's formatter argument to an instance of MenuFormatBuilder. example2.py has the following that might help you:
menu_format = MenuFormatBuilder().set_border_style_type(MenuBorderStyleType.HEAVY_BORDER)
...
menu = ConsoleMenu("Root Menu", "This is the Root Menu Subtitle", formatter=menu_format)

PyQt: is there an better way to set objectName in code?

When you use Qt_Designer or Qt_Creator to design a form, objectName for any given widget is always set to something. But if you create a widget in code AND you need the objectName later, you have to assign it explicitly. So then the widget assignment takes at least two lines of code. This seems very inelegant.
Example:
button1 = QPushButton('button caption') # at this point objectName is some kind of empty string
button1.setObjectName('button1')
If you need to find the widget later (i.e. with findChild), you must have objectName set, otherwise you're out of luck.
Is there some way to automatically set objectName without extra lines of code? I looked at the PyQt5 Reference Guide and could not find such a feature. A similar question was asked on Code Review, but got no answers. Two lines of code isn't the end of the world, but it seems redundant. Somehow I'm required to assign this name twice once for Python (first line) and again for Qt.
You can pass objectName as a keyword argument when creating the button.
button1 = QPushButton('button caption', objectName='button1')
This can extend this to any Qt property during initialization:
button1 = QPushButton(text='button caption', objectName='button1', icon=icon1)
Moreover, signals can be connected when constructing an object, too:
button1 = QPushButton(text='button caption', objectName='button1', clicked=someMethod)
The added named argument is equivalent to button1.clicked.connect(someMethod)

How do I separate the functions from the gui class in PySide?

In my PySide project I have 3 files:
one that contains all the gui stuff converted to python from Qt Designer,
another which has the signals, all the logic and functions and
one more that starts the whole application.
I think it's better to separate the logic from the functions.
The following is a simple function inserting items in a tablewidget:
# my_functions.py
def fill_table():
for row in range(10):
for col in range(10):
item_value = "...."
item = QtGui.QTableWidgetItem()
item.setText(str(item_value))
table_widget.setItem(row, col, item)
My main problem is how would you reference a widget from your application in a separate module.
Assuming my_functions.py is second item in your list of files, it appears that fill function uses a module-global table_widget. I don't recommend that but if that's really what you want, then just access it. So with
# my_functions.py
table_widget = ... # somewhere in that file, not shown in your post
def fill_table():
...populate table_widget...
Then:
# main.py
import my_functions
...
... somewhere, use my_functions.table_widget...
Better would be to define a custom class in my_functions.py, instantiate in main.py, and make fill_table() a method on custom class:
# my_functions.py
class MyTableWidget(QTableWidget):
...
def fill(self):
for row in range(10):
for col in range(10):
item_value = "...."
item = QtGui.QTableWidgetItem()
item.setText(str(item_value))
self.setItem(row, col, item)
# main.py
from my_functions import MyTableWidget
table = MyTableWidget()
table.fill()
There are lots of ways, but basically it seems that your current design is procedural rather than object-oriented. Nothing wrong with that, but you will find it rather clashes with the rest of PyQt and Python after a while, and is not as easy to maintain and debug once your app passes the stage of prototyping. So I recommend second approach.
The objects in a typical Qt application are connected together in parent/child relationships. Most often, there is a top-level main-window which functions as the root object, with all the other objects (widgets, layouts, etc) arranged in a hierarchy below it.
Given this, it is very natural to put all the gui-related program logic in the main-window class, because all the other objects will then be accessible via self. But if you put all the gui-related logic into functions in separate modules, there is no self available. So it would be up to you to provide that missing functionality.
The most obvious way to do this would be to keep a reference to the top-level window in the module that starts the application, so that the other modules can import it:
from app_module import main_window
# my_functions.py
def fill_table():
for row in range(10):
for col in range(10):
item_value = "...."
item = QtGui.QTableWidgetItem()
item.setText(str(item_value))
main_window.table_widget.setItem(row, col, item)
Alternatively, you could re-design all the functions so that they operate on only one object (or class of objects), and then explicitly pass in an instance:
# my_functions.py
def fill_table(table_widget):
...
However, whatever way you do it, it's hard to see how this could ever be the "best" way to structure the code.
Most gui applications consist of several largish sub-components that work more-or-less independently of one another, along with a central controller/manager that organises them all into a single functioning unit. The sub-components will usually be widget sub-classes (probably living in separate modules), which will become children of a main-window which may also function as the controller/manager. Organising your code along these lines is much more in line with the way Qt is designed to work, and will automatically avoid most of the potential communication problems between the various parts of the application.

PyQt4: Databinding?

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)

Categories

Resources