TabularAdapter customization/notifications? - enthought

Thanks to another user here on SO (Warren Weckesser), I found a nice way to format my TabularAdapter columns. There are some other customizations I'd like to accomplish, so I thought I'd put this out to SO to see if I can get more help.
The following code puts up a couple of TabularAdapter tables in the format that I want to use. What I'd like to be able to do are 2 things:
I'd like to set the first column as non-editable. I've found how to set a row to non-editable, but not a column -- is this possible?
What I'd really like (even more than #1 above) it to get a notification if one of the values in any of my columns changes! I've heard that there are some 'tweaks' that can be done with numpy arrays to accomplish this, but I'm way too inexperienced yet to pull this off. Is there any TraitsAdapter mentods that might be used to accomplish this feat?
Here's my code so far (thanks to Warren's modifications):
from traits.api import HasTraits, Array, Str
from traitsui.api import View, Item, TabularEditor
from traitsui.tabular_adapter import TabularAdapter
from numpy import dtype
test_dtype = dtype([('Integer#1', 'int'),
('Integer#2', 'int'),
('Float', 'float')])
class TestArrayAdapter1(TabularAdapter):
columns = [('Col1 #', 0), ('Col2', 1), ('Col3', 2)]
even_bg_color = 0xf4f4f4 # very light gray
width = 125
def get_format(self, object, name, row, column):
formats = ['%d', '%d', '%.4f']
return formats[column]
class TestArrayAdapter2(TabularAdapter):
columns = [('Col1 #', 0), ('Col2', 1), ('Col3', 2)]
even_bg_color = 0xf4f4f4 # very light gray
width = 125
object_0_format = Str("%d")
object_1_format = Str("%d")
object_2_format = Str("%.4f")
class Test(HasTraits):
test_array = Array(dtype=test_dtype)
view = \
View(
Item(name='test_array', show_label=False,
editor=TabularEditor(adapter=TestArrayAdapter1())),
Item(name='test_array', show_label=False,
editor=TabularEditor(adapter=TestArrayAdapter2())),
)
test = Test()
test.test_array.resize(5, refcheck=False)
test.configure_traits()

For your item #2, after talking to Enthought folks, I confirmed there isn't an official way to do this yet but:
I created a ticket for it: https://github.com/enthought/traitsui/issues/387
I worked around the issue, by keeping a handle on the ArrayAdapter, subclass it, and override the set_text method like so:
.
class NotifyingArrayAdapter(ArrayAdapter):
value_changed = Event
def set_text(self, object, trait, row, column, text):
super(NotifyingArrayAdapter, self).set_text(object, trait, row,
column, text)
self.value_changed = True
That way, I can just listen to the value_changed event, and do what I need with it.
You can get fancier, and make the event be a more complex object, for example storing information about the old/new values, and the row and column changed:
class ArrayAdapterEvent(HasStrictTraits):
row = Int
column = Int
old = Str
new = Str
class NotifyingArrayAdapter(ArrayAdapter):
value_changed = Event(Instance(ArrayAdapterEvent))
def set_text(self, object, trait, row, column, text):
old = self.get_text(object, trait, row, column)
super(NotifyingArrayAdapter, self).set_text(object, trait, row,
column, text)
event = ArrayAdapterEvent(old=old, new=text, row=row, column=column)
self.value_changed = event

Related

How to modify double click on ALV event?

Hi guys!
I found a program on the internet that runs row details when I double-click the 'CARRID' cell on ALV.
How to rewrite this code so that the details are displayed no matter what cell in the table I click, not only in 'CARRID'?
CLASS lcl_handle_dc DEFINITION.
PUBLIC SECTION.
METHODS double_click
FOR EVENT double_click OF if_salv_gui_table_display_opt
IMPORTING ev_field_name eo_row_data.
ENDCLASS.
CLASS lcl_handle_dc IMPLEMENTATION.
METHOD double_click.
DATA: ls_sflight TYPE sflight.
CHECK ev_field_name = 'CARRID'.
* read the row data
eo_row_data->get_row_data(
EXPORTING
iv_request_type = if_salv_gui_selection_ida=>cs_request_type–all_fields
IMPORTING
es_row = ls_sflight ).
* Display the row data
cl_salv_ida_show_data_row=>display( iv_text = 'Flight Row Info' is_data = ls_sflight ).
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
DATA: lr_salv TYPE REF TO if_salv_gui_table_ida,
lr_handle TYPE REF TO lcl_handle_dc.
cl_salv_gui_table_ida=>create(
EXPORTING
iv_table_name = 'SFLIGHT'
RECEIVING
ro_alv_gui_table_ida = lr_salv ).
DATA(lr_disp) = lr_salv->display_options( ).
* Enable double click
lr_disp->enable_double_click( ).
CREATE OBJECT lr_handle.
SET HANDLER lr_handle->double_click FOR ALL INSTANCES.
* Display ALV
lr_salv->fullscreen( )->display( ).
Remove the below line of code.
CHECK ev_field_name = 'CARRID'.
The CHECK statement ensures that the logical expression that follows evaluates to abap_true before continuing executing the next statements.

Write scale/nested conversion with ASAMMDF

I am using this snippet in order to create a mf4 file with a value to text table, found in the examples from asammdf's github.
vals = 5
conversion = {
'val_{}'.format(i): i
for i in range(vals)
}
conversion.update(
{
'text_{}'.format(i): 'key_{}'.format(i).encode('ascii')
for i in range(vals)
}
)
sig = Signal(
np.arange(cycles, dtype=np.uint32) % 30,
t,
name='Channel_value_to_text',
conversion=conversion,
comment='Value to text channel',
)
sigs.append(sig)
mdf.append(sigs, comment='arrays', common_timebase=True)
Is there a way to create a table with ##TX blocks and also ##CC blocks?(in order to simulate a scale conversion)
Thank you!
If anyone needs it, I found the answer and it was simpler than expected.
Create another conversion (i.e conversion2) in the same manner as the first one, then reassign the value of one of the references to it.
conversionBlock = from_dict(conversion)
conversionBlock.referenced_blocks["text_4"] = from_dict(conversion2)

String category names with Wisp scala plotting library

I am trying to plot bars for String categories with Wisp. In other words, I have a string (key) and a count (value) in my repl and I want to have a bar chart of the count versus the key.
I don't know if something easy exists. I went as far as the following hack:
val plot = bar(topWords.map(_._2).toList)
val axisType: com.quantifind.charts.highcharts.AxisType.Type = "category"
val newPlot = plot.copy(xAxis = plot.xAxis.map {
axisArray => axisArray.map { _.copy(axisType = Option(axisType),
categories = Option(topWords.map(_._1))) }
})
but I don't know if it works because I don't find a way to visualize newPlot. Or maybe adding a method to the Wisp source implementing the above is the way to go?
Thanks for any help.
PS : I don't have the reputation to create the wisp tag, but I would have...
Update: in wisp 0.0.5 or later this will be supported directly:
column(List(("alpha", 1), ("omega", 5), ("zeta", 8)))
====
Thanks for trying out wisp (I am the author). I think a problem that you may have encountered is by naming your variable plot, you overrode access to the plot method defined by Highcharts, which allows you to plot any Highchart object. (now that I see it, naming your plot "plot" is an unfortunately natural thing to do!)
I'm filing this as a github issue, as category names are very common.
This works for me. I used column for vertical bars:
import com.quantifind.charts.Highcharts._
val topWords = Array(("alpha", 14), ("beta", 23), ("omega", 18))
val numberedColumns = column(topWords.map(_._2).toList)
val axisType: com.quantifind.charts.highcharts.AxisType.Type = "category"
val namedColumns = numberedColumns.copy(xAxis = numberedColumns.xAxis.map {
axisArray => axisArray.map { _.copy(axisType = Option(axisType),
categories = Option(topWords.map(_._1))) }
})
plot(namedColumns)
producing this visualization:
(You could also consider creating one bar at a time, and use the legend to name them)

PySide - QSortFilterProxyModel and QListView - indexWidget pointer get deleted when filtering

I've a problem with a custom QListView I'm trying to make, here the problem:
I'm using QListView to show a list of QWidget by using QListView.setIndexWidget(index,widget).
This is working pretty fine, but now I want to filter the items model by using QSortFilterProxyModel()
with .setFilterWildcard()
It is not working very well because the second time the model is filtered
I got error like this :
RuntimeError: Internal C++ object (PySide.QtGui.QLabel) already deleted.
Without using filtering and QSortFilterProxyModel everything works fine, but it seems I'm missing
something with the filtering operation, the indexWidget() is deleted when using filtering :(
here a sample code where you can reproduce the bug, when list view is shown, hit 1,2 or 3 keyboard
key to activate filtering ( Backspace to set filtering empty to show all items )
Here the sample code to reproduce the problem:
import PySide.QtGui as QtGui
import PySide.QtCore as QtCore
_DEFAULT_ITEM_SIZE = QtCore.QSize(100, 85)
_USER_ROLE = QtGui.QStandardItem.UserType + 1
class CustomItemWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(CustomItemWidget, self).__init__(parent=parent)
#self.setAutoFillBackground(True)
self.main_layout = QtGui.QVBoxLayout(self)
self.label = QtGui.QLabel(self)
self.main_layout.addWidget(self.label)
def paintEvent(self, event):
painter = QtGui.QPainter(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
# Default brush and pen
bg_brush = QtGui.QBrush(QtGui.QColor("#8C8C8C"))
pen = QtCore.Qt.NoPen
painter.save()
painter.setPen(pen)
painter.setBrush(bg_brush)
painter.drawRoundedRect(self.rect(), 12, 12)
painter.restore()
def setData(self, role, value):
if role == QtCore.Qt.DisplayRole:
self.label.setText(value)
class CustomItem(QtGui.QStandardItem):
def __init__(self):
super(CustomItem, self).__init__()
self.number = None
self.item_widget = CustomItemWidget()
self.setSelectable(True)
def type(self):
return _USER_ROLE
def data(self, role):
if role == QtCore.Qt.DisplayRole:
value = "DATA %s" % str(self.number)
self.item_widget.setData(role, value)
return value
if role == QtCore.Qt.SizeHintRole:
return _DEFAULT_ITEM_SIZE
return QtGui.QStandardItem.data(self, role)
class CustomItemDelegate(QtGui.QStyledItemDelegate):
def __init__(self, parent=None):
super(CustomItemDelegate, self).__init__(parent=parent)
class CustomItemModel(QtGui.QStandardItemModel):
def __init__(self, parent=None):
super(CustomItemModel, self).__init__(parent)
def flags(self, index):
return QtCore.Qt.ItemIsEnabled | \
QtCore.Qt.ItemIsSelectable | \
QtCore.Qt.ItemIsDragEnabled | \
QtCore.Qt.ItemIsDropEnabled
class CustomItemFilterProxyModel(QtGui.QSortFilterProxyModel):
def __init__(self, parent=None):
super(CustomItemFilterProxyModel, self).__init__(parent)
self.setDynamicSortFilter(True)
self.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
self.setFilterKeyColumn(0)
class CustomView(QtGui.QListView):
def __init__(self, parent=None):
super(CustomView, self).__init__(parent=parent)
self.setIconSize(_DEFAULT_ITEM_SIZE)
self.setMovement(QtGui.QListView.Static)
self.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.setSelectionBehavior(QtGui.QAbstractItemView.SelectItems)
self.setViewMode(QtGui.QListView.IconMode)
self.setUniformItemSizes(True)
self.setFlow(QtGui.QListView.LeftToRight)
self.setResizeMode(QtGui.QListView.Adjust)
self.data_model = CustomItemModel(self)
self.proxy_model = CustomItemFilterProxyModel(self)
self.proxy_model.setSourceModel(self.data_model)
self.setModel(self.proxy_model)
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_1:
self.proxy_model.setFilterWildcard("*1*")
print self.proxy_model.filterRegExp()
if event.key() == QtCore.Qt.Key_2:
self.proxy_model.setFilterWildcard("*2*")
print self.proxy_model.filterRegExp()
if event.key() == QtCore.Qt.Key_3:
self.proxy_model.setFilterWildcard("*3*")
print self.proxy_model.filterRegExp()
if event.key() == QtCore.Qt.Key_Backspace:
self.proxy_model.setFilterFixedString("")
print self.proxy_model.filterRegExp()
if event.key() == QtCore.Qt.Key_Plus:
self.addNewItem()
QtGui.QListView.keyPressEvent(self, event)
def addNewItem(self):
item = CustomItem()
item.number = self.data_model.rowCount()
self.addItem(item)
def addItem(self, item):
self.data_model.appendRow(item)
proxy_index = self.proxy_model.mapFromSource(item.index())
self.setIndexWidget(proxy_index, item.item_widget)
if __name__ == '__main__':
import sys
qapplication = QtGui.QApplication(sys.argv)
layout = QtGui.QVBoxLayout()
window = QtGui.QDialog()
window.setLayout(layout)
view = CustomView(window)
view.resize(800, 600)
layout.addWidget(view)
for i in range(0, 10):
item = CustomItem()
item.number = i
view.addItem(item)
window.show()
sys.exit(qapplication.exec_())
or sample code here:
https://gist.github.com/66e29df303d1f1825a53
Can someone please help me on this? is this a known bug ? or I'm doing it completely wrong :P
Thanks in advance for your help.
This is an old question, but as I struggled with a similar problem for quite a while, here the solution I found and a possible explanation:
Instead of caching the custom widget on the model item, I cached the data needed to create the widget. In my case, I wanted to use a custom label with html in order to be able to format parts of text in different colour. Hence, I cached the html string on the item.
Then, in the initStyleOption method of the item delegate, I recreated the widget if it didn't yet exist or had disappeared after filtering:
label = self.parent().indexWidget(modelIndex)
if not label:
label = CustomLabel(item.html)
self.parent().setIndexWidget(modelIndex, label)
The reason why filtering deletes the widget cached on the item is as follows, I believe: the widget can "exist" only in one place. When it is put as indexWidget, it "exists" on a row in the view, not in an item of the model any more. As filtering removes rows from view, widgets on those rows get deleted. - A poor explanation, but I've often got similar surprises when manipulating html elements with JavaScript if I've forgotten to clone the element.

Dynamic SQL Query based on values selected in dropdown/radiobutton

I have a controller in a simple grails application which looks like the following:
The where condition here is a static one. I want the filters in where condition(Hourly,Afiiliates)to be dyanamic based on selection made using radio buttons(for timeperiod) and using dropdown (for mv)
class Tablev1classController {
def dataSource
def listJson = {
def sql = new Sql(dataSource)
def rows = sql.rows("select date_hour, total_revenue as sales, visits, marketing, organic,single_page_visits,total_units,orders,total_revenue_ly as sales_ly, visits_ly,marketing_ly, organic_ly,total_units_ly,orders_ly,single_page_visits_ly from xyz.mu_ewacs_marketing_vehicle_tylylw where time_period = 'HOURLY' && mv = 'AFFILIATES'")
sql.close()
render rows as JSON
}
Any help would be appreciated.
What you're probably looking for are named or ordinal parameters. See Sql javadoc, chapters "Avoiding SQL injection" and "Named and named ordinal parameters".
Using ordinal parameters it could be done like this:
def rows = sql.rows("select ... from xyz.whatever
where time_period = ? && mv = ?", [timePeriod, mv])