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())
Related
Hard to explain this in a simple subject. This is with a GET, no POSTs.
I have a function defined in a different file, we'll say list_gen.py that returns list 'gen_list'. 'gen_list' is a global variable here.
In the views.py, I call this function and assign the output as 'output_list' and send it with the return as such: " return render(request, 'detail.html', {'output_list':output_list}.
Then, in detail.html I simply place the results with {{output_list}}.
When I visit the page, I see the exact output I expected. But, if press refresh, the output duplicates and continues to do so each time I press refresh or if I visit another entry that uses that detail.html page.
Has anyone seen this before?
Below is the view in question. The suspect list is "ped"
#login_required(login_url='/accessdenied/')
def detail(request, did):
try:
dog = Dogs.objects.get(did__exact=did)
resp = pedigree(did, 3)
ped = ''.join(map(str, resp))
try:
dam = Dogs.objects.get(did__exact=dog.dam).registered_name
except:
dam = "Not Listed"
try:
sire = Dogs.objects.get(did__exact=dog.sire).registered_name
except:
sire = "Not Listed"
parents = {'dam': dam, 'sire': sire}
except Dogs.DoesNotExist:
raise Http404("Dog does not exist")
return render(request, 'main/detail.html', {'dog': dog, 'parents': parents, 'ped': ped})
Below am image of the first time visiting the details for a specific entry (a dog):
And refresh a few times, and then visit a different entry. Note that each recent entry appears at the bottom.
The pedigree function (which calls printTree). Yes... I am making an html file into a list which is probably dumb. BUT, I get the exact same duplication if I use the "dog" list you see commented out where used.
dog = []
ped = ['<table border=1>']
def pedigree(did, max):
'''
:param did: id of the child dog
:param max: max number of generations to display
:return: list of dogs
'''
con = sqlite3.connect(f)
con.row_factory
cursor = con.execute('SELECT "Registered Name", ROUND(((JulianDay("now") - JulianDay("Date of Birth"))/365.25),1), "Sex" FROM dogs where did IS %s ' % did).fetchall()
for row in cursor:
name, age, gender = row[0], row[1], row[2]
sql = "SELECT a.Sire, a.Dam, s.'Registered Name', d.'Registered Name' FROM dogs a INNER JOIN dogs s ON a.Sire = s.did INNER JOIN dogs d ON a.Dam = d.did WHERE a.did = "
printTree(sql, did, name, 0, max)
return ped
#return dog
def printTree(stmt, did, name, N, max):
'''
:param stmt: sql statement to query information
:param did: dog to obtain information on
'''
rspan = 2**(max-N)
if (rspan > 1):
ped.append('<td rowspan='+str(rspan)+'><a href=/dogs/'+str(did)+'>'+name+'</td>')
# dog.append({'name': name, 'rspan': rspan})
else:
ped.append('<td><a href=/dogs/'+str(did)+'>'+name+"</td>")
if (N == max):
ped.append("</tr><tr>")
if(N < max):
s = None
d = None
sn = None
dn = None
con = sqlite3.connect(f).execute(stmt+str(did)).fetchall()
for row in con:
s, d, sn, dn = row[0], row[1], row[2], row[3]
if (s and sn) != None:
printTree(stmt, s, sn, N+1, max)
if (d and dn) != None:
printTree(stmt, d, dn, N+1, max)
Thanks to the comments, I fixed by altering the pedigree function as such to clear the list.
ped = []
def pedigree(did, max):
'''
:param did: id of the child dog
:param max: max number of generations to display
:return: list of dogs
'''
ped.clear()
ped.append('<table border=1>')
con = sqlite3.connect(f)
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:
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()
I am writing a logger which records the level of the entries.
To make it simple, let's say it logs entries like <level> <message>.
I am now trying to write a log viewer which formats the logfile "nicely" as an indented tree grid.
For example is the raw log file contains:
0 entry1
0 entry2
1 entry3
2 entry4
3 entry5
2 entry6
0 entry7
It should output:
entry1
entry2
└entry3
├entry4
│└entry5
└entry6
entry7
My first steps were
Converting the list into a tree
Recursively print the tree
This worked with one single exception: I cannot figure out how I can pass the information that - referring to the example - before entry5 comes the │ sign to display that the previous level continues after the sub-levels.
So any hint, how to come from the list to the desired output is welcome.
Finally got it:
class LogViewer(LogFile):
"""
Formats raw log file contents nicely
and thus makes it human-readable
"""
__down = False
class EntryTreeNode():
"""
A minimal entry wrapper
"""
def __init__(self, string):
"""
Constructor
"""
lst = string.split(LogEntry.colsep())
if len(lst) != 6:
raise Exception('Invalid entry: ' + string)
else:
self.DATE = datetime.strptime(lst[0], LogEntry.timeformat())
self.ERRLVL = ErrLvlType(lst[1])
self.USER = lst[2]
self.CALLER = lst[3]
self.OFFSET = int(lst[4])
self.MSG = lst[5]
self.tag = self.OFFSET
self.children = []
self.pre = '[' + datetime.strftime(self.DATE, LogEntry.timeformat()) + ']\t' \
+ str(self.ERRLVL) + '\t' \
+ str(self.USER) + '\t'
self.post = str(self.CALLER) + ' \t' + str(self.MSG)
def __repr__(self):
return str(self.tag)
def __init__(self, path):
"""
Constructor
"""
super().__init__(path)
#property
def __sym_last(self):
"""
Returns the symbol for a last entry
"""
return '┌' if self.__down else '└'
#property
def __sym_mid(self):
"""
Returns the symbol for a middle entry
"""
return '├'
#property
def __sym_follow(self):
"""
Returns the symbol for a following entry
"""
return '│'
def __mktree(self, lst):
"""
Converts a log entry list into a tree
"""
roots = []
def children(root, lst):
result = []
while lst:
curr = lst.pop()
if curr.tag == root.tag + 1:
curr.children = children(curr, lst)
result.append(curr)
else:
lst.append(curr)
break
return result
while lst:
curr = lst.pop()
if curr.tag == 0:
curr.children = children(curr, lst)
roots.append(curr)
return roots
def __print_tree(self, root, offset='', prefix='', last=True):
"""
Prints a log entry tree
"""
print(root.pre + offset + prefix + root.post)
if last:
offset += ' '
else:
offset += self.__sym_follow
for i in range(0, len(root.children)):
if i == len(root.children)-1:
prefix = self.__sym_last
last = True
else:
prefix = self.__sym_mid
last = False
self.__print_tree(root.children[i], offset, prefix, last)
def display(self, reverse=False):
"""
Displays the log file nicely
"""
self.__down = reverse
entries = reversed(self.dump()) if reverse else self.dump()
entries = [self.EntryTreeNode(e) for e in entries]
tree = self.__mktree(entries)
for root in tree:
self.__print_tree(root)
Why are these two functions different?
def other_entry1(self, selection, row, el, var):
if selection == "Other":
var = StringVar()
el = Entry(self.frame1, textvariable=var)
el.grid(row=row, column=6)
#Calling it as part of an optionMenu
self.e33 = OptionMenu(self.frame1, self.ea_tf, *fixtures, command= lambda selection:self.other_entry1(selection,15, self.e33, self.ea_tf))
The other one:
def other_entry2(self, selection):
if selection == "Other":
self.ea_tf = StringVar()
self.e33 = Entry(self.frame1, textvariable=self.ea_tf)
self.e33.grid(row=15, column=6)
#Calling it in an optionMenu
self.e33 = OptionMenu(self.frame1, self.ea_tf, *fixtures, command=self.other_entry2)
I would like to be able to call the first function several times and just tell it what entry box to create instead of making several separate functions.
Edit: Isn't the second function just skipping the step of substituting in the arguments?
self.s33 is reference to OptionMenu
Second function overwrite self.e33 by reference to Entry and you can't use self.e33 to get access to OptionMenu.
First function copy reference from self.s33 to el then overwrites el by reference to Entry but you can still use self.s33 to get access to OptionMenu
See simple example using "Visual Execution" on PythonTutor.com:
Use link to open page with example, click "Visual Execution" and then you can use "Forward" button to see step by step how example works
function 1
function 2
You can use first function to create several Entry but you don't need to send self.e33 and self.ea_tf because function will not use it.
You get the same result as
def other_entry1(self, selection, row):
if selection == "Other":
var = StringVar()
Entry(self.frame1, textvariable=var).grid(row=row, column=6)
#Calling it as part of an optionMenu
self.e33 = OptionMenu(self.frame1, self.ea_tf, *fixtures, command=lambda selection:self.other_entry1(selection,15))
problem is with access to Entry or StringVar() to get value from Entry
self.all_vars = {} # dictionary
def other_entry1(self, selection, row):
if selection == "Other":
self.all_vars[row] = StringVar()
Entry(self.frame1, textvariable=self.all_vars[row]).grid(row=row, column=6)
#Calling it as part of an optionMenu
self.e33 = OptionMenu(self.frame1, self.ea_tf, *fixtures, command=lambda selection:self.other_entry1(selection,15))
# another place
row = 15
print "row:", row
print "value in Entry:", self.all_vars[row].get()
EDIT: working example
#!/usr/bin/env python
from Tkinter import *
class App():
def __init__(self, master):
self.master = master
self.all_entries = {} # empty dictionary
self.all_entries_vars = {} # empty dictionary
self.all_optionmenus = {} # empty dictionary
self.all_optionmenus_vars = {} # empty dictionary
fixtures = ["One", "Two", "Tree", "Other"]
# create 5 options menu
for i in range(5):
self.all_optionmenus_vars[i] = StringVar()
self.all_optionmenus_vars[i].set("One")
self.all_optionmenus[i] = OptionMenu(self.master, self.all_optionmenus_vars[i], *fixtures, command=lambda selection, col=i:self.addEntry(selection, col))
self.all_optionmenus[i].grid(row=1, column=i)
Button(master, text="Print OptionMenus Vars", command=self.printOptionMenus).grid(row=2, column=0, columnspan=5)
Button(master, text="Print Entries Vars", command=self.printEntries).grid(row=3, column=0, columnspan=5)
def run(self):
self.master.mainloop()
def addEntry(self, selection, col):
if selection == "Other":
# if Entry was created before
if col in self.all_entries:
# show existing Entry
self.all_entries[col].grid(row=0, column=col)
else:
# create new Entry
self.all_entries_vars[col] = StringVar()
self.all_entries[col] = Entry(self.master, textvariable=self.all_entries_vars[col])
self.all_entries[col].grid(row=0, column=col)
# if Entry was created before
elif col in self.all_entries:
# hide Entry
self.all_entries[col].grid_forget()
def printEntries(self):
print "-"*30
for key in self.all_entries_vars:
print "Entry #%d: %s" % ( key, self.all_entries_vars[key].get() )
def printOptionMenus(self):
print "-"*30
for key in self.all_optionmenus_vars:
print "OptionMenu #%d: %s" % ( key, self.all_optionmenus_vars[key].get() )
#----------------------------------------------------------------------
if __name__ == '__main__':
App(Tk()).run()