wxPython ListCtrl : write Colored text - python

Trying to write string to a ListCtrl , I don't understand the logic completely. Is this the proper way?
self.rightPanel = wx.ListCtrl(spliter, -1, style=wx.LC_REPORT)
self.rightPanel.InsertColumn(0, 'LineNumber')
self.rightPanel.InsertColumn(1, 'log')
self.rightPanel.SetColumnWidth(0, 8)
self.rightPanel.SetColumnWidth(1, 80)
def writeConsole(self,str):
item = wx.ListItem()
item.SetText(str)
item.SetTextColour(wx.RED)
item.SetBackgroundColour(wx.BLACK)
index = self.rightPanel.GetItemCount()
self.rightPanel.InsertItem(item)
self.rightPanel.SetStringItem(index, 0, str(index))
self.rightPanel.SetStringItem(index, 1, item.GetText())
1-Why text is not displayed in color ?
2-Why there are 2 different methods for display text in ListCtrl?
ListCtrl.InsertItem()
ListCtrl.SetStringItem()
I think InsertItem just loads the item to list.SetString but displays the item content.(Not Sure)

SetTextColour() and SetBackgroundColour() are methods of the entire listctrl, not of items.
For items you should use (valid only for report mode):
GetItemTextColour(idx_item)
SetItemTextColour(idx_item, col)
InsertItem(index, item) (item here is an instance of wx.ListItem) is one of the InsertItem() methods to add a new row on a ListCtrl.
SetStringItem(index, col, label, imageId=-1) (where index and col parameters are the row and column indexes for a cell) allows setting strings in any selected column. Other insert methods work only for the first column.
Reference: wxPython in Action, Noel Rappin and Robin Dunn.

Related

Repetitive PyQT references

I have a PyQT UI designed in QT Designer, with 40 figures (on different tabs), and a bunch of repetitive references to elements in the UI. For example, the following unique text fields, which are used to set the title of each figure.
# Tab 1
self.tab1_tl1.canvas.axes.set_title(self.tab1_tl1_title.text())
self.tab1_tl2.canvas.axes.set_title(self.tab1_tl2_title.text())
self.tab1_tl3.canvas.axes.set_title(self.tab1_tl3_title.text())
self.tab1_tl4.canvas.axes.set_title(self.tab1_tl4_title.text())
self.tab1_tl1.canvas.axes.set_ylabel(self.tab1_tl1_ylabel.text())
self.tab1_tl2.canvas.axes.set_ylabel(self.tab1_tl2_ylabel.text())
self.tab1_tl3.canvas.axes.set_ylabel(self.tab1_tl3_ylabel.text())
self.tab1_tl4.canvas.axes.set_ylabel(self.tab1_tl4_ylabel.text())
# Tab 2
self.tab2_tl1.canvas.axes.set_title(self.tab2_tl1_title.text())
self.tab2_tl2.canvas.axes.set_title(self.tab2_tl2_title.text())
self.tab2_tl3.canvas.axes.set_title(self.tab2_tl3_title.text())
self.tab2_tl4.canvas.axes.set_title(self.tab2_tl4_title.text())
self.tab2_tl1.canvas.axes.set_ylabel(self.tab2_tl1_ylabel.text())
self.tab2_tl2.canvas.axes.set_ylabel(self.tab2_tl2_ylabel.text())
self.tab2_tl3.canvas.axes.set_ylabel(self.tab2_tl3_ylabel.text())
self.tab2_tl4.canvas.axes.set_ylabel(self.tab2_tl4_ylabel.text())
As you can imagine, the code gets very wordy. Is there a more efficient way to loop through all these references and fields?
As I said in the comments, you could use getattr, but without further context, this is the best I can suggest:
for i in range(1, 41):
for j in range(1, 5):
ax = getattr(self, f'tab{i}_tl{j}').canvas.axes
title = getattr(self, f'tab{i}_tl{j}_title').text()
ylabel = getattr(self, f'tab{i}_tl{j}_ylabel').text()
ax.set_title(title)
ax.set_ylabel(ylabel)
You should put the corresponding tl, title and ylabel in a dictionary and arange them in a nested list instead of putting names as tab1_tl1
Create them like this:
tabs = []
for i in range(tabCount):
tab = []
tabs.append(tab)
for j in range(tlCount):
tab.append(dict(
obj = create_tl(i,j)),
title = create_title(i,j),
ylabel = create_ylabel(i,j) )
Now tabs[i][j] correspond to tabi_tlj and it is a dict containing the acutal tl object, the title and the ylabel.
So you can update the properties like this:
for tab in tabs:
for tl in tab:
tl['obj'].canvas.axes.set_title(tl['title'])
tl['obj'].canvas.axes.set_ylabel(tl['ylabel'])
In general create numbered variables is not good. Lists are there for that. Also always gather related repeated objects in dicts or objects.

'TypeError: can only concatenate str (not "int") to str' when accessing nested lists

I am trying to build a Tkinter program that displays vertically stacked text fields with labels. Next to each of these fields there are two other text fields with labels. I want to be able to add vertically and horizontally add more fields with labels.
In a Data class I have store a list assets that is build like this:
assets = [[[vertical_label, vertical_input],[[horizontal_label_1, horizontal_input_1],[horizontal_label_2, horizontal_input_2]]]]
so that
assets[i] is an asset
assets[i][0] is the vertical part of which [0] is the label and [1] is the input
asserts[i][1] is the horizontal part of which [j][0] is the label of the jth horizontal item
In my code i have called each horizontal item ticker.
Now, I need to actually add the asset to the window for which I coded a function Data.update()
def update():
asset_index = 0
asset = Data.assets[0]
asset[0][0].grid(row=asset_index, column=0)
asset[0][1].grid(row=asset_index, column=1)
ticker_index = 2
ticker = asset[1][0]
ticker[0][0].grid(row=asset_index, column=ticker_index)
ticker[0][1].grid(row=asset_index, column=ticker_index + 1)
ticker[1][0].grid(row=asset_index + 1, column=ticker_index)
ticker[1][1].grid(row=asset_index + 1, column=ticker_index + 1)
But for the line ticker[0][0].grid(row=asset_index, column=ticker_index) it throws the error.
How is that even possible?
looks like you go one level too deep with your subscripts:
ticker = asset[1][0]
gives you
ticker = [horizontal_label_1, horizontal_input_1]
so I think with ticker[0][0] you get the first letter of your horizontal label not the label itself.
I guess what you want is
ticker = asset[1] before the line that throws the error and then it should be fine.

index a column of dataframe regarding other columns and

I have provided this data frame,
as you see I have 3 index chapter, ParaIndex, (paragraph index) and Sentindex (sententcesindex), I have 70 chapters, 1699 Paragraph, and 6999 sentences
so each of them starts from the beginning (0 or 1 ), the problem is that I want to make a widget to call a "specific sentence" which placed in a specific paragraph of a chapter. something like this
https://towardsdatascience.com/interactive-controls-for-jupyter-notebooks-f5c94829aee6
but for extracting specific sentences in the specific paragraph of the specific chapter
I think I should have another index (like ChapParaSent ABBREVIATION for all) or even multidimensions index which show that this sentence where exactly placed
any idea how can I provide that using ipywidget
https://ipywidgets.readthedocs.io/en/latest/examples/Using%20Interact.html
#interact
def showDetail( Chapter=(1,70),ParaIndex=(0,1699),SentIndex=(0,6999)):
return df.loc[(df.Chapter == Chapter) & (df.ParaIndex==ParaIndex)&(df.SentIndex==SentIndex)]
the problem with this is since we do not know each chapter has how many paragraphs has as well as and we do not know in each paragraph SentIndex the index to start from which number most of the time we have no result.
the aim is to adopt this (or define a new index) in a way that with changing the bar buttons we have always one unique sentence
for example, here I have the result:
but when I changed to this :
I do not have any result, the REASON is obvious because we do not have any index as 1-2-1 since, in chapter 1, Paragraph index 2: Sentindex starts from 2!
One solution I saw that it was a complete definition of a multidimensional data frame but I need something easier that I can use by ipywidget...
many many thanks
Im sure there is a easier solution out there but that works I guess.
import pandas as pd
data = [
dict(Chapter=0, ParaIndex=0, SentIndex=0, content="0"),
dict(Chapter=1, ParaIndex=1, SentIndex=1, content="a"),
dict(Chapter=1, ParaIndex=1, SentIndex=2, content="b"),
dict(Chapter=2, ParaIndex=2, SentIndex=3, content="c"),
dict(Chapter=2, ParaIndex=2, SentIndex=4, content="d"),
dict(Chapter=2, ParaIndex=3, SentIndex=5, content="e"),
dict(Chapter=3, ParaIndex=4, SentIndex=6, content="f"),
]
df = pd.DataFrame(data)
def showbyindex(target_chapter, target_paragraph, target_sentence):
df_chapter = df.loc[df.Chapter==target_chapter]
unique_paragraphs = df_chapter.ParaIndex.unique()
paragraph_idx = unique_paragraphs[target_paragraph]
df_paragraph = df_chapter.loc[df.ParaIndex==paragraph_idx]
return df_paragraph.iloc[target_sentence]
showbyindex(target_chapter=2, target_paragraph=0, target_sentence=1)
Edit:
If you want the sliders only to be within a valid range you can define IntSliders for your interact decorator:
chapter_slider = widgets.IntSlider(min=0, max=max(df.Chapter.unique()), step=1, value=0)
paragraph_slider = widgets.IntSlider(min=0, max=1, step=1, value=0)
sentence_slider = widgets.IntSlider(min=0, max=1, step=1, value=0)
#interact(target_chapter=chapter_slider, target_paragraph=paragraph_slider, target_sentence=sentence_slider)
Now you have to check the valid number of paragraphs/sentences within your showbyindex function and set the sliders value/max accordingly.
if(...):
paragraph_slider.max = ...
...

tkinter table:adding data into new row and specify index

Has anyone worked with tkinter table?
I have problem with adding data into new row. I have an entry field in which user enter object. Then in def onButtonSents() I check if this word is contained in objects array and enter this object into the first column of the table naming Object, in the second column I add positive sentiments of the object and so on. I want by clicking the button add new row and put in different columns of one row values of dictionary. Here what I have for now:
file sentiment_analysis.py:
objects [] #an array of objects which user enters into entry field
positiveSentiments = dict() #dictionary with key - object and positive words which correspond to it
negativeSentiments = dict() #dictionary with key - object and negative words which correspond to it
array_cols = ['Object', 'Positive sentiments', 'Negative sentiments']
var = tktable.ArrayVar()
#below is initialization of my tkinter table
import tktable
array_cols = ['Object', 'Positive sentiments', 'Negative sentiments']
var = tktable.ArrayVar()
table = tktable.Table(frame,
rows = 1,
cols = 3,
roworigin=0,
colorigin=0,
variable=var,)
table.pack(side='bottom', fill='both')
var.set(index='0,0', value = 'Object')
table.tag_configure('active', )
var.set(index='0,1', value = 'Positive Sentiments')
var.set(index='0,2', value = 'Negative Sentiments')
#The method which puts data into the rows of table
def onButtonSents():
table.insert_rows(1)
for i in range (1, 5):
index1 = '%i,%i' % (i, 0)
table.activate(index1)
for key in sentiment_analysis.positiveSentiments.keys():
if key == entry.get():
var.set(index='active', value=entry.get())
for i in range (1, 5):
index2 = '%i,%i' % (i, 1)
table.activate(index2)
for key in sentiment_analysis.positiveSentiments.keys():
if key == entry.get():
var.set(index='active', value=sentiment_analysis.positiveSentiments[key])
for i in range (1, 5):
index3 = '%i,%i' % (i, 2)
table.activate(index3)
for key in sentiment_analysis.negativeSentiments.keys():
if key == entry.get():
var.set(index='active', value=sentiment_analysis.negativeSentiments[key])
But the cells of table aren't correctly filled. First object if filled correctly but then all objects become the same like first I enter 'ubs' and when I enter the second 'wheat' the first also becomes 'wheat' and their sentiments are also changed.
Unfortunately I haven't found any suggestions for this problem in the Internet. I would be very grateful for any advice!
Below I have included my rewrite of your code to work the way you intended.
This runs properly for me.
Let me know if it works for you.
Please Note: I am using Python 3.6, tkinter and tktable.
Before I show the code, I will give a brief explanation of the tktable syntax for setting data into a row.
Your code was on the right track, but there were some errors preventing it from working.
Your first error was to use "var.set" to set data to the tktable array variable.
Instead, you should have been using "table.set" to set data to the tktable "table" pathname variable.
Secondly, because you are setting data to a row, you can use the set 'row' option.
Thirdly, you do Not need to specify "index=" and "value=".
You can simply enter the index and value, separated by commas.
If you have created variables for the index and value, you can use the variable names.
To set multiple values to multiple indexes on the same row,
you simply add more values, separated by commas.
You only need to specify the index of the first cell in this row.
For example:
You wrote:
var.set(index='active', value=entry.get())
This should be:
table.set('row', '1,0', entry.get(), 'Positive Sentiments', 'Negative Sentiments')
The above line presumes you want this 'row' to begin at cell index '1,0'.
The code you provided did not run for me.
Therefore, to get the code to run as intended, I have added a class structure.
If you have questions about classes and how to use them, most of these questions have already been answered in detail on Stack Overflow by Bryan Oakley and others.
The class structure I have added to your code is based on what I have learnt from Bryan Oakley via Stack Overflow.
Also, I made other changes as follows:
Amended your onButtonSents method, by changing the conditional statements, and correcting the "insert_rows" syntax,
so that the correct data is added to the next new line as you specified.
If you have any questions about this, please let me know.
Added some test sample data to the "objects" list and for the dictionaries: "positiveSentiments" and "negativeSentiments',
to demonstrate how the code works.
Since you have Not specified how you would like the code to behave when the user inputs an object that is Not on the list,
I have left this for you to do yourself.
Here is the revised code:
import tkinter as tk
import tktable
from tkinter import Tk, Label, Button
from tkinter import *
class Control(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.controller = self
self.shared_data = {
"objects": tk.Variable(),
"positiveSentiments": tk.Variable(),
"negativeSentiments": tk.Variable(),
"x": tk.IntVar(),
}
self.title('Title of Control Class')
self.entry_frame = tk.Frame()
self.entry_frame.pack(side=TOP)
self.user_input = tk.StringVar()
self.test_entry = tk.Entry(self.entry_frame, textvariable=self.user_input)
self.test_entry.pack(side=LEFT)
self.test_button = tk.Button(self.entry_frame, text='Button', command=self.onButtonSents)
self.test_button.pack(side=RIGHT)
self.table_frame = tk.Frame()
self.table_frame.pack(side=BOTTOM)
self.array_cols = ['Object', 'Positive sentiments', 'Negative sentiments']
self.var = tktable.ArrayVar()
self.table = tktable.Table(self.table_frame,
rows = 1,
cols = 3,
roworigin=0,
colorigin=0,
colwidth=20,
variable=self.var,)
self.table.pack(side='bottom', fill='both', expand=True)
self.table.set('row', '0,0', 'Object','Positive Sentiments', 'Negative Sentiments')
self.table.tag_configure('active', )
self.shared_data["objects"] = ['object1', 'object2', 'object3', 'object4', 'object5', 'object6', 'object7'] #an array of objects which user enters into entry field
self.shared_data["positiveSentiments"] = {'object1' : 'positive1', 'object2' : 'positive2', 'object3' : 'positive3', 'object4' : 'positive4', 'object5' : 'positive5', 'object6' : 'positive6', 'object7' : 'positive7' } #dictionary with key - object and positive words which correspond to it
self.shared_data["negativeSentiments"] = {'object1' : 'negative1', 'object2' : 'negative2', 'object3' : 'negative3', 'object4' : 'negative4', 'object5' : 'negative5', 'object6' : 'negative6', 'object7' : 'negative7'} #dictionary with key - object and negative words which correspond to it
#The method which puts data into the rows of table
def onButtonSents(self, _event=None):
#self.table.insert_rows(1)
for o in self.shared_data["objects"]:
if o == self.user_input.get():
if o in self.shared_data["positiveSentiments"]:
if o in self.shared_data["negativeSentiments"]:
if self.shared_data["x"].get() is not None:
self.shared_data["x"].set(self.shared_data["x"].get() + 1)
self.index1 = '%i,%i' % (self.shared_data["x"].get(), 0)
self.table.insert_rows(self.shared_data["x"].get() - 1, 1)
self.table.set('row', self.index1, self.user_input.get(), self.shared_data["positiveSentiments"].get(o), self.shared_data["negativeSentiments"].get(o))
else:
self.shared_data["x"].set(1)
self.index1 = '%i,%i' % (self.shared_data["x"].get(), 0)
self.table.set('row', self.index1, self.user_input.get(), self.shared_data["positiveSentiments"].get(o), self.shared_data["negativeSentiments"].get(o))
Control1 = Control()
Control1.mainloop()

higher level Python GUI toolkit, e.g. pass dict for TreeView/Grid

Started my first Python pet project using PyGTK. Though it is a really powerful GUI toolkit and looks excellent, I have some pet peeves. So I thought about transitioning to something else, as it's not yet too extensive. Had a look around on SO and python documentation, but didn't get a good overview.
What's nice about PyGTK:
Glade files
self.signal_autoconnect({...})
self.get_widget() as __getattr__
This is bugging me however:
manual gobject.idle_add(lambda: ... and False)
no standard functionality to save application/window states
TreeView needs array building
widget.get_selection().get_selected(), model.get_value(iter, liststore_index)
TreeView: Because this is the main interface element, it's the most distracting. Basically my application builds a list of dictionaries to be displayed name=column+row=>value. To display it using GTK there needs to be a manual conversion process, ordering, typecasts. This seems a lot of overhead, and I wished for something more object-oriented here. PyGtk has many abstractions atop gtk+ but still seems rather low-levelish. I'd prefer to pass my dict as-is and have columns pre-defined somehow. (GtkBuilder can predefine TreeView columns, but this doesn't solve the data representation overhead.)
When I get a mousclick on my TreeView list, I also have to convert everything back into my application data structures. And it's also irksome that PyGTK doesn't wrap gtk+ calls with gobject.idle itself, if run from a non-main thread. Right now there is a lot of GUI code that I believe shouldn't be necessary, or could be rationalized away.
? So, are there maybe additional wrappers on top of PyGTK. Or which other toolkit supports simpler interfaces for displaying a Grid / TreeView. I've read a lot about wxPython being everyones favourite, but it's less mature on Linux. And PyQT seems to be mostly the same abstraction level as PyGTK. Haven't used TkInter much so don't know about if it has simpler interfaces, but it anyway looks unattractive. As does PyFLTK. PyJamas sounds fascinating, but is already too far out (Desktop application).
.
So, GUI toolkit with dict -> Grid display. Which would you pick?
.
Just as exhibit, this is my current TreeView mapping function. Sort of works, but I would rather have something standard:
#-- fill a treeview
#
# Adds treeviewcolumns/cellrenderers and liststore from a data dictionary.
# Its datamap and the table contents can be supplied in one or two steps.
# When new data gets applied, the columns aren't recreated.
#
# The columns are created according to the datamap, which describes cell
# mapping and layout. Columns can have multiple cellrenderers, but usually
# there is a direct mapping to a data source key from entries.
#
# datamap = [ # title width dict-key type, renderer, attrs
# ["Name", 150, ["titlerow", str, "text", {} ] ],
# [False, 0, ["interndat", int, None, {} ] ],
# ["Desc", 200, ["descriptn", str, "text", {} ], ["icon",str,"pixbuf",{}] ],
#
# An according entries list then would contain a dictionary for each row:
# entries = [ {"titlerow":"first", "interndat":123}, {"titlerow":"..."}, ]
# Keys not mentioned in the datamap get ignored, and defaults are applied
# for missing cols. All values must already be in the correct type however.
#
#staticmethod
def columns(widget, datamap=[], entries=[], pix_entry=False):
# create treeviewcolumns?
if (not widget.get_column(0)):
# loop through titles
datapos = 0
for n_col,desc in enumerate(datamap):
# check for title
if (type(desc[0]) != str):
datapos += 1 # if there is none, this is just an undisplayed data column
continue
# new tvcolumn
col = gtk.TreeViewColumn(desc[0]) # title
col.set_resizable(True)
# width
if (desc[1] > 0):
col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
col.set_fixed_width(desc[1])
# loop through cells
for var in xrange(2, len(desc)):
cell = desc[var]
# cell renderer
if (cell[2] == "pixbuf"):
rend = gtk.CellRendererPixbuf() # img cell
if (cell[1] == str):
cell[3]["stock_id"] = datapos # for stock icons
expand = False
else:
pix_entry = datapos
cell[3]["pixbuf"] = datapos
else:
rend = gtk.CellRendererText() # text cell
cell[3]["text"] = datapos
col.set_sort_column_id(datapos) # only on textual cells
# attach cell to column
col.pack_end(rend, expand=cell[3].get("expand",True))
# apply attributes
for attr,val in cell[3].iteritems():
col.add_attribute(rend, attr, val)
# next
datapos += 1
# add column to treeview
widget.append_column(col)
# finalize widget
widget.set_search_column(2) #??
widget.set_reorderable(True)
# add data?
if (entries):
#- expand datamap
vartypes = [] #(str, str, bool, str, int, int, gtk.gdk.Pixbuf, str, int)
rowmap = [] #["title", "desc", "bookmarked", "name", "count", "max", "img", ...]
if (not rowmap):
for desc in datamap:
for var in xrange(2, len(desc)):
vartypes.append(desc[var][3]) # content types
rowmap.append(desc[var][0]) # dict{} column keys in entries[] list
# create gtk array storage
ls = gtk.ListStore(*vartypes) # could be a TreeStore, too
# prepare for missing values, and special variable types
defaults = {
str: "",
unicode: u"",
bool: False,
int: 0,
gtk.gdk.Pixbuf: gtk.gdk.pixbuf_new_from_data("\0\0\0\0",gtk.gdk.COLORSPACE_RGB,True,8,1,1,4)
}
if gtk.gdk.Pixbuf in vartypes:
pix_entry = vartypes.index(gtk.gdk.Pixbuf)
# sort data into gtk liststore array
for row in entries:
# generate ordered list from dictionary, using rowmap association
row = [ row.get( skey , defaults[vartypes[i]] ) for i,skey in enumerate(rowmap) ]
# autotransform string -> gtk image object
if (pix_entry and type(row[pix_entry]) == str):
row[pix_entry] = gtk.gdk.pixbuf_new_from_file(row[pix_entry])
# add
ls.append(row) # had to be adapted for real TreeStore (would require additional input for grouping/level/parents)
# apply array to widget
widget.set_model(ls)
return ls
pass
Try Kiwi, maybe? Especially with its ObjectList.
Update: I think Kiwi development has moved to PyGTKHelpers.
I hadn't come across Kiwi before. Thanks, Johannes Sasongko.
Here are some more tooklits that I keep bookmarked. Some of these are wrappers around other toolkits (GTK, wxWidgets) while others stand alone:
AVC
Dabo
pyFLTK
pyglet
PyGTK
PyGUI
uxPython
Wax
wxPython
wxpita
WxWrappers
(I've included a few that were already mentioned for the sake of others who come across this post. I would have posted this as a comment, but it's a bit too long.)
See also pygtkhelpers' ObjectList
I would suggest taking a look at wxPython. I found it really easy to pick up and very powerful too although I'd have to admit I've not done a lot with Treeviews myself.
wxWidgets calls the equivalent control a wxTreeCtrl
[Edit] The wxDataViewTreeCtrl might actually be of more use in your case.

Categories