Is it possible to have multi label widget ? for example like tabular column ? in Tkinter Python? - python-2.7

Is it possible to create a multi label widget like a tabular column ? For, example as shown in the snapshot attached ?
Kindly let me know or provide some comments if there is any option to create widgets with multi label option ?
Thanks !

There is no built-in way to do that, but it's relatively easy to write your own using a canvas. For example, put one label in the upper-right corner and the other in the lower-left. Then, draw a line from upper-left to lower-right.
Example:
import tkinter as tk
class CustomLabel(tk.Frame):
def __init__(self, parent, label1, label2, **kwargs):
tk.Frame.__init__(self, parent, **kwargs)
self.canvas = tk.Canvas(self, borderwidth=0, highlightthickness=0, background=self.cget("background"))
self.canvas.pack(fill="both", expand=True)
l1 = tk.Label(self.canvas, text=label1, background=self.cget("background"))
l2 = tk.Label(self.canvas, text=label2, background=self.cget("background"))
l1.place(relx=.75, rely=.25, anchor="c")
l2.place(relx=.25, rely=.75, anchor="c")
# arrange for the line to be redrawn whenever the canvas
# changes size
self.canvas.bind("<Configure>", self.handle_configure)
# set the default size to be relative to the requested size
# of the labels plus some margin
width = l1.winfo_reqwidth() + l2.winfo_reqwidth() + 4
height = l1.winfo_reqheight() + l2.winfo_reqheight() + 4
self.canvas.configure(width=width, height=height)
def handle_configure(self, event):
self.canvas.delete("line")
self.canvas.create_line(0,0,event.width, event.height, tags=("line",))
Example usage:
root = tk.Tk()
colors = ("SteelBlue4", "SteelBlue3", "SkyBlue1")
for row in range(3):
for column in range(4):
if row == 0 and column == 0:
widget = CustomLabel(root, "Place", "Name", background=colors[row])
else:
widget = tk.Label(root, text="", background=colors[row])
widget.grid(row=row, column=column, sticky="nsew", padx=1, pady=1)
for row in range(3):
root.grid_rowconfigure(row, uniform="row")
for column in range(4):
root.grid_columnconfigure(column, uniform="column")
Screenshot:

Related

Iterate Pandas Series to create a new chart legend

After grouping etc. I get a Series like in the example below. I would like to show the average numbers for each bar. The code below shows only one entry (of course, as I have only one "legend"). Could anyone one suggest a smart way of showing these numbers?
%matplotlib inline
import matplotlib.pyplot as plt
import matplotlib
matplotlib.style.use('ggplot')
import pandas
# create Series
dict_ = {"Business" : 104.04,"Economy":67.04, "Markets":58.56, "Companies":38.48}
s = pandas.Series(data=dict_)
# plot it
ax = s.plot(kind='bar', color='#43C6DB', stacked=True, figsize=(20, 10), legend=False)
plt.tick_params(axis='both', which='major', labelsize=14)
plt.xticks(rotation=30) #rotate labels
# Shrink current axis by 20%
box = ax.get_position()
ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])
#create new legend
legend = ['%s (%.1f a day)' %(i, row/7) for i, row in s.iteritems()]
# Put the legend to the right of the current axis
L = ax.legend(legend, loc='center left', bbox_to_anchor=(1, 0.5), fontsize=18)
plt.show()
The legend only has a single entry. This is a handle of a blue bar. Therefore even if you set the labels to a longer list, only the first element of that list is used as label for the existing handle.
The idea can be to duplicate the legend handle to have the same size as the labels
legend = ['%s (%.1f a day)' %(i, row/7) for i, row in s.iteritems()]
h,l = ax.get_legend_handles_labels()
L = ax.legend(handles = h*len(legend), labels=legend, loc='center left',
bbox_to_anchor=(1, 0.5), fontsize=18)

How to populate wxpython LIstCtrl with OnClick event

I want to design a wxpython ListCtrl. So when the Search button is clicked i am getting list like this
[(7, u'GLUOCOSE', u'C6H1206'), (8, u'SUCROSE', u'C12H22O11')]
I want to populate the listctrl with the above output. I am InsertStringItem method, which will return me the index of current row and rest of the columns are filled by SetStringItem() method.But that gives me TypeError: String or Unicode type required.How do i accomplish this?
def OnSearch(self, event):
placeholder = '?'
placeholders = ','.join(placeholder for unused in self.molecule_list)
query = 'SELECT * FROM MOLECULE WHERE MOL_NUMBER IN (%s)' % placeholders
cursor = self.conn.execute(query, self.molecule_list)
final = cursor.fetchall()
print final
for j in final:
index = self.list.InsertStringItem(sys.maxint, j[0])
self.list.SetStringItem(index, 1, j[1])
self.list.SetStringItem(index, 2, j[2])
You are iterating over the cursor, rather than the data which was returned in the variable final. Swap over to iterate using final
index = 0
for j in final:
index = self.list.InsertStringItem(index, str(j[0]))
self.list.SetStringItem(index, 1, j[1])
self.list.SetStringItem(index, 2, j[2])
index +=1
The easiest way, is to ensure that the ListCtrl has a style=wx.LC_REPORT and then use Append.
for j in final:
self.list.Append((j[0],j[1],j[2],j[3]))
Where j[n] is each item you require from the data for the number of items in the ListCtrl. I hope that makes sense.
Here is an example showing both methods
import wx
class MyForm(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Molecules")
panel = wx.Panel(self, wx.ID_ANY)
self.index = 0
self.list_ctrl = wx.ListCtrl(panel, size=(-1,100),
style=wx.LC_REPORT
)
self.list_ctrl.InsertColumn(0, 'Number')
self.list_ctrl.InsertColumn(1, 'Element')
self.list_ctrl.InsertColumn(2, 'Make up')
btn = wx.Button(panel, label="Add data")
btn.Bind(wx.EVT_BUTTON, self.add_lines)
btn2 = wx.Button(panel, label="Append data")
btn2.Bind(wx.EVT_BUTTON, self.app_lines)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.list_ctrl, 0, wx.ALL|wx.EXPAND, 5)
sizer.Add(btn, 0, wx.ALL|wx.CENTER, 5)
sizer.Add(btn2, 0, wx.ALL|wx.CENTER, 5)
panel.SetSizer(sizer)
def add_lines(self, event):
data = [[7, 'GLUCOSE', 'C6H1206'],[8,'SUCROSE', 'C12H22O11']]
index = 0
for j in data:
self.list_ctrl.InsertStringItem(index, str(j[0]))
self.list_ctrl.SetStringItem(index, 1, j[1])
self.list_ctrl.SetStringItem(index, 2, j[2])
index += 1
def app_lines(self, event):
data = [[7, 'GLUCOSE', 'C6H1206'],[8,'SUCROSE', 'C12H22O11']]
for j in data:
self.list_ctrl.Append((j[0],j[1],j[2]))
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm()
frame.Show()
app.MainLoop()

Matplotlib animation scatter plot python. Gradually change color of points

I am new to matplotlib animation and am trying to animate a scatter plot where points moving towards the right will turn red gradually while points moving towards the left will turn blue gradually. The code doesn't work perfectly as it doesn't change the color of the points gradually. When I pause the animation and maximize it, the gradual change in color suddenly appears, when I play it, it is again the same. Here is the animation link. The final image should be something like this:
But the animation doesn't show gradual change of colors as you can see in the video.
Here is the code, I'd really appreciate your help. Thanks
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
import pandas as pd
class AnimatedScatter(object):
"""An animated scatter plot using matplotlib.animations.FuncAnimation."""
def __init__(self, numpoints=5):
self.numpoints = numpoints
self.stream = self.data_stream()
# Setup the figure and axes...
self.fig, self.ax = plt.subplots()
# Then setup FuncAnimation.
self.ani = animation.FuncAnimation(self.fig, self.update, interval=500,
init_func=self.setup_plot, blit=True,repeat=False)
self.fig.canvas.mpl_connect('button_press_event',self.onClick)
#self.ani.save("animation.mp4")
def setup_plot(self):
"""Initial drawing of the scatter plot."""
t=next(self.stream)
x, y, c = t[:,0],t[:,1],t[:,2]
self.scat = self.ax.scatter(x, y, c=c, s=50, animated=True)
self.ax.axis([-15, 15, -10, 10])
# For FuncAnimation's sake, we need to return the artist we'll be using
# Note that it expects a sequence of artists, thus the trailing comma.
return self.scat,
def data_stream(self):
#f=pd.read_csv("crc_viz.csv")
columns = ['TbyN','CbyS']
#f=f[['TbyN','CbyS']]
index=range(1,self.numpoints+1)
x=10*(np.ones((self.numpoints,1))-2*np.random.random((self.numpoints,1)))
y = 5*(np.ones((self.numpoints,1))-2*np.random.random((self.numpoints,1)))
f=np.column_stack((x,y))
f=pd.DataFrame(f,columns=columns)
print f
f['new_cbys'] = f['CbyS']
f['new_cbys'][f['new_cbys']<0] = -1
f['new_cbys'][f['new_cbys']>0] = 1
f=f[:self.numpoints]
cbys=np.array(list(f['CbyS']))
sign = np.array(list(f['new_cbys']))
x = np.array([0]*self.numpoints)
y = np.array(f['TbyN'])
c = np.array([0.5]*self.numpoints)
t = [(255,0,0) for i in range(self.numpoints)]
data=np.column_stack((x,y,c))
x = data[:, 0]
c = data[:,2]
while True:
#print xy
#print cbys
if not pause:
for i in range(len(x)):
if sign[i]==1:
if x[i]<cbys[i]-0.1:
x[i]+=0.1
c[i]+=0.05
else:
x[i]=cbys[i]
elif sign[i]==-1:
if x[i]>cbys[i]+0.1:
x[i]-=0.1
c[i]-=0.05
else:
x[i]=cbys[i]
print c
#print data
#print c
yield data
def onClick(self,event):
global pause
pause ^=True
def update(self, i):
"""Update the scatter plot."""
data = next(self.stream)
print data[:,2]
# Set x and y data...
self.scat.set_offsets(data[:, :2])
# Set colors..
self.scat.set_array(data[:,2])
return self.scat,
def save(self):
plt.rcParams['animation.ffmpeg_path'] = 'C:\\ffmpeg\\bin\\ffmpeg.exe'
self.mywriter = animation.FFMpegWriter()
self.ani.save("myMovie.mp4",writer=self.mywriter)
self.show()
def show(self):
#mng = plt.get_current_fig_manager()
#mng.window.state('zoomed')
plt.show()
pause = False
if __name__ == '__main__':
a = AnimatedScatter(10)
a.show()
#a.save()
The problem you have is that the scatter plot is redrawn in every iteration, renormalizing the colors to the minimal and maximal value of c. So even at the start there will be a dot coresponding to the minmal and maximal color in the colormap already.
The solution would be to use a color normalization which is absolute from the start. The easiest way to do this is using the vmin and vmax keyword arguments.
ax.scatter(x, y, c=c, vmin=-1.5, vmax=2)
(This means that a value of c=-1.5 is the lowest color in the colormap and c=2 corresponds to the highest.)
Now it may be a bit hard to find the appropriate values, as the values are constantly changing in an infinite loop, so you need to find out appropriate values yourself depending on the use case.

How should I link specific rows to columns in a Qtablewidget

I have a Qtablewidget with 3 columns. The first column contain user names, the second and third are columns are check boxes. I would like these check boxes to be attributes for the row of users. For example If you look at the image below, User: bruno is marked(has attributes) delete and delete home. I would like to have an output like: User Bruno marked for delete, marked for delete home. To do that, I would need to link the users to both these columns which I haven't the slightest idea. How should I approach this problem. Below is the code that I already came up with. It populates the users column from a file.
from PyQt4 import QtGui, QtCore
class Window(QtGui.QWidget):
def __init__(self, rows, columns):
QtGui.QWidget.__init__(self)
self.table = QtGui.QTableWidget(rows, columns, self)
self.table.setHorizontalHeaderItem(0, QtGui.QTableWidgetItem("Users"))
self.table.setHorizontalHeaderItem(1, QtGui.QTableWidgetItem("Delete User"))
self.table.setHorizontalHeaderItem(2, QtGui.QTableWidgetItem("Delete User and Home"))
self.table.verticalHeader().hide()
header = self.table.horizontalHeader()
header.setStretchLastSection(True)
rowf = 0
with open("/home/test1/data/users") as in_file:
if in_file is not None:
users = in_file.readlines()
for line in users:
users = QtGui.QTableWidgetItem()
self.table.setItem(rowf, 0, users)
users.setText(line)
rowf+=1
in_file.close()
for column in range(columns):
for row in range(rows):
if column % 3:
item = QtGui.QTableWidgetItem(column)
item.setFlags(QtCore.Qt.ItemIsUserCheckable |
QtCore.Qt.ItemIsEnabled)
item.setCheckState(QtCore.Qt.Unchecked)
self.table.setItem(row, column, item)
self.table.itemClicked.connect(self.cell_was_clicked)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.table)
self._list = []
def cell_was_clicked(self, item):
#row = self.table.currentItem().row()
#col = self.table.currentItem().column()
#print "item:", item
#print "row=", row
if item.checkState() == QtCore.Qt.Checked:
print('"%s" Checked' % item.text())
#self._list.append(item.row())
else:
#print "(", row , ",", col, ")"
print('%s' % item.text())
print (item.row(),item.column())
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window(200, 3)
window.resize(400, 400)
window.show()
sys.exit(app.exec_())
fixed it like this:
def cell_was_clicked(self, item):
if item.checkState() == QtCore.Qt.Checked:
user = self.table
delete = user.horizontalHeaderItem(item.column()).text()
print ('%s' % user.item(item.row(), 0).text())
print ('is marked for %s' % delete)
print('%s Checked' % item.text())
#self._list.append(item.row())
else:
print('%s' % item.text())
print (item.row(),item.column())

Generate a bunch of tkinter checkbuttons and read the status of all those radio buttons at once

I have a tkinter class which reads some data into a couple of lists. From this now i have created a dictionary for creating checkbuttons.
I'm trying to create those checkbuttons in a new window() with a button to submit and read the stutus of those. I want this data to process.
def get_data(self):
self.flags = ["one","two","three", "four"]
self.tests = ["Jack","Queen","King","Ace"]
self.value = [11,12,13,1]
self.dict1 = {k:v for k,v in enumerate(self.flags,1)}
def get_status(self):
self.selectWindow = Toplevel(root)
self.selectWindow.title("Select Test Cases")
Submit_btn = Button(selectWindow, text="Submit", command=read_status )
for testcase in self.dict1:
self.dict1[testcase] = Variable()
l = Checkbutton(self.selectWindow,text=self.dict1[testcase], variable=self.dict1[testcase])
l.pack()
def read_status(self):
pass
From here I'm not able go ahead and read the status of checkbuttons and get those are checked. I need this data for further processing on tests(not actual lists given here I have few more). How to solve? Please let me know.
Checkbutton has a built in command function that can solve this problem. Every time you press a button that function is called, and you can print out the values of the buttons (0,1)
def get_data(self):
self.flags = ["one","two","three", "four"]
self.tests = ["Jack","Queen","King","Ace"]
self.value = [11,12,13,1]
self.dict1 = {k:v for k,v in enumerate(self.flags,1)}
def get_status(self):
self.selectWindow = Toplevel(self)
self.selectWindow.title("Select Test Cases")
self.get_data()
Submit_btn = Button(self.selectWindow, text="Submit", command=read_status ) # This button should be packed
Submit_btn.pack()
for testcase in self.dict1:
self.dict1[testcase] = Variable()
l = Checkbutton(self.selectWindow,text=self.dict1[testcase], variable=self.dict1[testcase], command=self.read_status) # Note the command
l.pack()
self.selectWindow.mainloop()
# Here comes the interesting part
def read_status(self):
for i,j in self.dict1.iteritems():
print j.get()
You forgot to use self and pack method:
Submit_btn = Button(self.selectWindow, text="Submit", command=self.read_status )
Submit_btn.pack()
Checkbutton's states are (0, 1) so use IntVar() to inspect the state:
...
self.dict1[testcase] = IntVar()
...
Then use IntVar get method:
def read_status(self):
for v in self.dict1:
print self.dict1[v].get()