Related
I am trying to import student data from an Excel workbook. I have to select column_name of the class StudentMasterResource dynamically which is present in the file. I got all column name in constants module has one dictionary which name column_name. When I do it for the first time, it works, then it fails
constants.py
column_name = dict()
resource.py
from common_account import constants
from import_export import widgets, fields, resources
def getClassName(key):
if key in constants.column_name:
return constants.column_name[key]
return key
class StudentMasterResource(resources.ModelResource):
organisation_id = fields.Field(
column_name=getClassName('organisation_id'),
attribute='organisation_id',
widget=widgets.ForeignKeyWidget(OrganisationMaster, 'organisation_name'),
saves_null_values=True
)
name = fields.Field(
column_name=getClassName('Name'),
attribute='name',
saves_null_values=True,
widget=widgets.CharWidget()
)
date_of_birth = fields.Field(
column_name=getClassName('date'),
attribute='date_of_birth',
saves_null_values=True,
widget=widgets.DateWidget()
)
views.py
from common_account import constants
from tablib import Dataset
#api_view(['POST'])
#permission_classes([IsAuthenticated])
def student_import(request):
if request.method == 'POST':
context_data = dict()
data_set = Dataset()
file = request.FILES['myfile']
extension = file.name.split(".")[-1].lower()
column_data = request.data
is_import = column_name['is_import']
constants.valid_data.clear()
constants.invalid_data.clear()
if extension == 'csv':
data = data_set.load(file.read().decode('utf-8'), format=extension)
else:
data = data_set.load(file.read(), format=extension)
constants.column_name = {
'date' : column_data.get('birth'),
'name' : column_data.get('name'),
}
if is_import == 'No':
result = student_resource.import_data(data_set, organisation_id = request.user.organisation_id,
offering_id = offering_id,all_invalid_data = False, dry_run=True, raise_errors=True)
context_data['valid_data'] = constants.valid_data
context_data['invalid_data'] = constants.invalid_data
context_data[constants.RESPONSE_RESULT] = {"Total records":student_resource.total_cnt,
"skip records":len(constants.invalid_data),
"Records imported": len(constants.valid_data),
}
return JsonResponse(context_data)
elif is_import == 'Yes':
result = student_resource.import_data(data_set, organisation_id = request.user.organisation_id,
offering_id = offering_id,all_invalid_data = False, dry_run=False, raise_errors=False)
context_data[constants.RESPONSE_ERROR] = False
context_data[constants.RESPONSE_MESSAGE] = 'Data Imported !!!'
context_data[constants.RESPONSE_RESULT] = {"Total records":student_resource.total_cnt,
"skip records":len(constants.invalid_data),
"Records imported": len(constants.valid_data),
}
return JsonResponse(context_data)
I'm reading and processing a file (using the same bit of code) which runs at two very different speeds: 1. scripted (50K+ iterations per second) and 2. wrapped in function (~300 iterations per second). I really can't figure out why there are such massive differences in reading time consumption.
The module structure (unused and irrelevant files omitted. The code is at the end.):
| experiments/
|--| experiment_runner.py
|
| module/
|--| shared/
|--|--| dataloaders.py
|--|--| data.py
In data.py we have the method (load, the class wrapping the method inherits from torch.utils.data.Dataset) actually loading the file. In dataloaders.py I prepare the arguments to pass to load, wrapped in a function for each dataset that I'm using. This is then passed to a loader function, which handles splitting the dataset and so on.
In experiment_runner then is where the differences in speed happen. If I use the dataset functions in dataloaders.py the loading happens at around 300 iterations/second. If I copy the code from the function and throw it directly into the experiment_runner, still using the loader function from dataloaders.py (so, not wrapped in a function for each dataset), the loading happens at roughly 50000 iterations / second. I am at a complete loss as to why wrapping code in a function would alter the speed of it that drastically.
Now the actual code:
data.py:
def load(self, dataset: str = 'train', skip_header = True, **kwargs) -> None:
fp = open(self.data_files[dataset])
if skip_header:
next(fp)
data = []
for line in tqdm(self.reader(fp), desc = f'loading {self.name} ({dataset})'):
data_line, datapoint = {}, base.Datapoint()
for field in self.train_fields:
idx = field.index if self.ftype in ['CSV', 'TSV'] else field.cname
data_line[field.name] = self.process_doc(line[idx].rstrip())
data_line['original'] = line[idx].rstrip()
for field in self.label_fields:
idx = field.index if self.ftype in ['CSV', 'TSV'] else field.cname
if self.label_preprocessor:
data_line[field.name] = self.label_preprocessor(line[idx].rstrip())
else:
data_line[field.name] = line[idx].rstrip()
for key, val in data_line.items():
setattr(datapoint, key, val)
data.append(datapoint)
fp.close()
if self.length is None:
# Get the max length
lens = []
for doc in data:
for f in self.train_fields:
lens.append(len([tok for tok in getattr(doc, getattr(f, 'name'))]))
self.length = max(lens)
if dataset == 'train':
self.data = data
elif dataset == 'dev':
self.dev = data
elif dataset == 'test':
self.test = data
dataloaders.py:
def loader(args: dict, **kwargs):
"""Loads the dataset.
:args (dict): Dict containing arguments to load dataaset.
:returns: Loaded and splitted dataset.
"""
dataset = GeneralDataset(**args)
dataset.load('train', **kwargs)
if (args['dev'], args['test']) == (None, None): # Only train set is given.
dataset.split(dataset.data, [0.8, 0.1, 0.1], **kwargs)
elif args['dev'] is not None and args['test'] is None: # Dev set is given, test it not.
dataset.load('dev', **kwargs)
dataset.split(dataset.data, [0.8], **kwargs)
elif args['dev'] is None and args['test'] is not None: # Test is given, dev is not.
dataset.split(dataset.data, [0.8], **kwargs)
dataset.dev_set = dataset.test
dataset.load('test', **kwargs)
else: # Both dev and test sets are given.
dataset.load('dev', **kwargs)
dataset.load('test', **kwargs)
return dataset
def binarize(label: str) -> str:
if label in ['0', '1']:
return 'pos'
else:
return 'neg'
def datal(path: str, cleaners: base.Callable, preprocessor: base.Callable = None):
args = {'data_dir': path,
'ftype': 'csv',
'fields': None,
'train': 'dataset.csv', 'dev': None, 'test': None,
'train_labels': None, 'dev_labels': None, 'test_labels': None,
'sep': ',',
'tokenizer': lambda x: x.split(),
'preprocessor': preprocessor,
'transformations': None,
'length': None,
'label_preprocessor': binarize,
'name': 'First dataset.'
}
ignore = base.Field('ignore', train = False, label = False, ignore = True)
d_text = base.Field('text', train = True, label = False, ignore = False, ix = 6, cname = 'text')
d_label = base.Field('label', train = False, label = True, cname = 'label', ignore = False, ix = 5)
args['fields'] = [ignore, ignore, ignore, ignore, ignore, d_label, d_text]
return loader(args)
And for the purposes:
experiment_runner.py
from module.dataloaders import datal, loader
dataset = datal() # Slow: 300-ish iterations/second
# Fast version: 50000 iter/second
def binarize(label: str) -> str:
if label in ['0', '1']:
return 'pos'
else:
return 'neg'
args = {'data_dir': path,
'ftype': 'csv',
'fields': None,
'train': 'dataset.csv', 'dev': None, 'test': None,
'train_labels': None, 'dev_labels': None, 'test_labels': None,
'sep': ',',
'tokenizer': lambda x: x.split(),
'preprocessor': preprocessor,
'transformations': None,
'length': None,
'label_preprocessor': binarize,
'name': 'First dataset.'
}
ignore = base.Field('ignore', train = False, label = False, ignore = True)
d_text = base.Field('text', train = True, label = False, ignore = False, ix = 6, cname = 'text')
d_label = base.Field('label', train = False, label = True, cname = 'label', ignore = False, ix = 5)
args['fields'] = [ignore, ignore, ignore, ignore, ignore, d_label, d_text]
dataset = loader(args)
I would ideally prefer to keep the dataset functions (e.g. datal) wrapped to keep the logic separate but with this speed decrease, that's not feasible.
I am extending the Virus on Network example from Mesa. The current network graph is like this. However, I want to remove the edges from the dead hosts (black).
My attempt is within the try_check_death():
model_agent.py
import random
import pysnooper
import sys
from random import randrange
from mesa import Agent
from .model_state import State
class HostAgent(Agent):
def __init__(self, unique_id, model, initial_state, virus_check_frequency, chance_spread_virus,
chance_recovery, chance_gain_resistance, chance_virus_kill_host, chance_severe_condition):
super().__init__(unique_id, model)
self.age = randrange(101) # Later to be reassign by another module (AB, Edmonton, Calgary specific)
self.sex = random.choice(['M', 'F']) # Later to be reassign by another module (AB, Edmonton, Calgary specific)
self.urban = random.choice(['Urban', 'Rural']) # Later to be reassign by another module (AB, Edmonton, Calgary specific); later to be interact with agent location and link assignment
self.state = initial_state
self.virus_check_frequency = virus_check_frequency
self.chance_spread_virus = chance_spread_virus
self.chance_recovery = chance_recovery
self.chance_gain_resistance = chance_gain_resistance
self.chance_virus_kill_host = chance_virus_kill_host
self.chance_severe_condition = chance_severe_condition
self.days_of_infection = None
self.days_in_hospital_for_infection = None
self.infection_severity = None
self.clinical_outcomes_from_infection = None
self.clinical_outcomes_after_recovery = None
def modify_chance(self): # a generic function that modify chance attributes
pass
def try_infect_neighbors(self):
neighbors_nodes = self.model.grid.get_neighbors(self.pos, include_center=False)
susceptible_neighbors = [agent for agent in self.model.grid.get_cell_list_contents(neighbors_nodes) if
agent.state is State.SUSCEPTIBLE]
for neighbor_agent in susceptible_neighbors:
if self.random.random() < self.chance_spread_virus:
neighbor_agent.state = State.INFECTED
def try_gain_resistance(self):
if self.random.random() < self.chance_gain_resistance:
self.state = State.RESISTANT
def try_remove_infection(self):
# Try to remove
if self.random.random() < self.chance_recovery:
# Success
self.state = State.SUSCEPTIBLE
self.try_gain_resistance()
else:
# Failed
self.state = State.INFECTED
def try_kill_host(self):
if self.random.random() < self.chance_virus_kill_host:
self.state = State.DEATH
def try_check_infection(self):
if self.random.random() < self.virus_check_frequency:
# Checking...
if self.state is State.INFECTED:
self.try_remove_infection()
def try_check_death(self):
if self.state is State.INFECTED:
self.try_kill_host()
if self.state is State.DEATH:
neighbors_nodes = self.model.grid.get_neighbors(self.pos, include_center=False)
neighbor_agents = [neighbor for neighbor in self.model.grid.get_cell_list_contents(neighbors_nodes)]
agent_neighbor_pairs = [(self.unique_id, neighbor.unique_id) for neighbor in neighbor_agents]
self.model.G.remove_edges_from(agent_neighbor_pairs)
def step(self):
if self.state is State.INFECTED:
self.try_infect_neighbors()
self.try_check_death()
self.try_check_infection()
model_network.py
import math
import sys
import networkx as nx
from mesa import Model
from mesa.time import RandomActivation
from mesa.datacollection import DataCollector
from mesa.space import NetworkGrid
from .model_state import State, number_infected, number_susceptible, number_resistant, number_state, number_death
from .model_agent import HostAgent
class HostNetwork(Model):
"""A virus model with some number of agents"""
def __init__(self, num_nodes=0, avg_node_degree=0, initial_outbreak_size=1, chance_spread_virus=0.0,
virus_check_frequency=0.0, chance_recovery=0.0, chance_gain_resistance=0.0,
chance_virus_kill_host=0.1, chance_severe_condition=0.0):
# Some assert statement to make sure some chances together don't add up > 1.0
self.num_nodes = num_nodes
prob = avg_node_degree / self.num_nodes
self.G = nx.erdos_renyi_graph(n=self.num_nodes, p=prob)
self.grid = NetworkGrid(self.G)
self.schedule = RandomActivation(self)
self.initial_outbreak_size = initial_outbreak_size if initial_outbreak_size <= num_nodes else num_nodes
self.chance_spread_virus = chance_spread_virus
self.virus_check_frequency = virus_check_frequency
self.chance_recovery = chance_recovery
self.chance_gain_resistance = chance_gain_resistance
self.chance_virus_kill_host = chance_virus_kill_host
self.chance_severe_condition = chance_severe_condition
self.datacollector = DataCollector({"Infected": number_infected,
"Susceptible": number_susceptible,
"Resistant": number_resistant,
"Death": number_death,
})
# Create agents
for i, node in enumerate(self.G.nodes()):
agent = HostAgent(i, self, State.SUSCEPTIBLE, self.chance_spread_virus, self.virus_check_frequency,
self.chance_recovery, self.chance_gain_resistance, self.chance_virus_kill_host,
self.chance_severe_condition)
self.schedule.add(agent)
# Add the agent to the node
self.grid.place_agent(agent, node)
# Infect some nodes
infected_nodes = self.random.sample(self.G.nodes(), self.initial_outbreak_size)
for agent in self.grid.get_cell_list_contents(infected_nodes):
agent.state = State.INFECTED
self.running = True
self.datacollector.collect(self)
def resistant_susceptible_ratio(self):
try:
return number_state(self, State.RESISTANT) / number_state(self, State.SUSCEPTIBLE)
except ZeroDivisionError:
return math.inf
def step(self):
self.schedule.step()
# collect data
self.datacollector.collect(self)
def run_model(self, n):
for i in range(n):
self.step()
server.py
import sys
import math
from mesa.visualization.ModularVisualization import ModularServer
from mesa.visualization.UserParam import UserSettableParameter
from mesa.visualization.modules import ChartModule
from mesa.visualization.modules import NetworkModule
from mesa.visualization.modules import TextElement
from .model_network import HostNetwork
from .model_state import State, number_infected, number_susceptible, number_resistant, number_death
def network_portrayal(G):
# The model ensures there is always 1 agent per node
def node_color(agent):
return {
State.INFECTED: '#FF0000',
State.SUSCEPTIBLE: '#008000',
State.DEATH: '#000000',
}.get(agent.state, '#00C5CD')
def edge_color(agent1, agent2):
if State.RESISTANT in (agent1.state, agent2.state):
return '#000000'
return '#e8e8e8'
def edge_width(agent1, agent2):
if State.RESISTANT in (agent1.state, agent2.state):
return 1
else:
return 3
def get_agents(source, target):
return G.nodes[source]['agent'][0], G.nodes[target]['agent'][0]
portrayal = dict()
portrayal['nodes'] = [{'size': 6,
'color': node_color(agents[0]),
'tooltip': "id: {}<br>state: {}".format(agents[0].unique_id, agents[0].state.name),
}
for (_, agents) in G.nodes.data('agent')]
portrayal['edges'] = [{'source': source,
'target': target,
'color': edge_color(*get_agents(source, target)),
'width': edge_width(*get_agents(source, target)),
}
for (source, target) in G.edges]
return portrayal
network = NetworkModule(network_portrayal, 500, 500, library='d3')
chart = ChartModule([{'Label': 'Infected', 'Color': '#FF0000'},
{'Label': 'Susceptible', 'Color': '#008000'},
{'Label': 'Resistant', 'Color': '#00C5CD'},
{'Label': 'Death', 'Color': '#000000'},
])
class MyTextElement(TextElement):
def render(self, model):
ratio = model.resistant_susceptible_ratio()
resistance_susceptible_ratio_text = '∞' if ratio is math.inf else '{0:.2f}'.format(ratio)
infected_text = str(number_infected(model))
susceptible_text = str(number_susceptible(model))
resistant_text = str(number_resistant(model))
death_text = str(number_death(model))
return "Resistant/Susceptible Ratio: {}\
<br>Infected Number: {}\
<br>Susceptible Number: {}\
<br>Resistant Number: {}\
<br>Death Number: {}\
".format(resistance_susceptible_ratio_text, infected_text, susceptible_text,
resistant_text, death_text)
model_params = {
'num_nodes': UserSettableParameter('slider', 'Number of agents', 10, 10, 300, 1,
description='Choose how many agents to include in the model'),
'avg_node_degree': UserSettableParameter('slider', 'Avg Node Degree', 2, 1, 8, 1,
description='Avg Node Degree'),
'initial_outbreak_size': UserSettableParameter('slider', 'Initial Outbreak Size', 1, 1, 100, 1,
description='Initial Outbreak Size'),
'chance_spread_virus': UserSettableParameter('slider', 'Chance to spread virus', 0.4, 0.0, 1.0, 0.1,
description='Probability that susceptible neighbor will be infected'),
'virus_check_frequency': UserSettableParameter('slider', 'Virus Check Frequency', 0.4, 0.0, 1.0, 0.1,
description='Frequency the nodes check whether they are infected by '
'a virus'),
'chance_recovery': UserSettableParameter('slider', 'Chance to recover', 0.3, 0.0, 1.0, 0.1,
description='Probability that the virus will be removed'),
'chance_gain_resistance': UserSettableParameter('slider', 'Chance to gain resistance', 0.5, 0.0, 1.0, 0.1,
description='Probability that a recovered agent will become '
'resistant to this virus in the future'),
}
server = ModularServer(HostNetwork, [network, MyTextElement(), chart], 'Covid-19 Model', model_params)
server.port = 8521
self.model.G.remove_edges_from(agent_neighbor_pairs) seems to give me troubles. What I am seeing is some duplicated nodes and edges similar (but not exactly the same) as the original graph.
One example:
Another example:
I'm creating a game where i have the data imported from a database, but i have a little problem...
Currently i get a copy of the data as a dictionary, which i need to pass as argument to my GUI, however i also need to process some data, like in this example:
I get the data as a dict (I've created the UseDatabase context manager and is working):
def get_user(name: str, passwd: str):
user = {}
user['name'] = name
user['passwd'] = passwd
with UseDatabase() as cursor:
_SQL = "SELECT id, cash, ruby FROM user WHERE name='Admin' AND password='adminpass'"
cursor.execute(_SQL)
res = cursor.fetchall()
if res:
user['id'] = res[0][0]
user['cash'] = res[0][1]
user['ruby'] = res[0][2]
return user
return res
.
.
.
def get_activities():
with UseDatabase() as cursor:
_SQL = "SELECT * FROM activities WHERE user_id='2'"
cursor.execute(_SQL)
res = cursor.fetchall()
if res:
ids = [i[0] for i in res]
activities = {}
for i in res:
activities[i[0]] = {'title':i[1],'unlock':i[2],'usr_progress':i[3]}
return (ids, activities)
return res
Need it as a dict in my GUI ("content" argument):
class SideBar:
def __init__(self, screen: 'pygame.display.set_mode()', box_width: int, box_height: int, content: dict, font: 'font = pygame.font.Font()'):
#content dict: {id: {'title':'','unlock':'','usr_progress':''},...}
self.box_width = box_width
self.box_height = box_height
self.box_per_screen = screen.get_height() // box_height
self.content = content
self.current_box = 1
self.screen = screen
self.font = font
self.generate_bar()
def generate_bar (self):
active = [i for i in self.content.keys() if i in range(self.current_box, self.current_box+self.box_per_screen)]
for i in range(self.box_per_screen):
gfxdraw.box(self.screen,pygame.Rect((0,i*self.box_height),(self.screen.get_width()/3,self.screen.get_height()/3)),(249,0,0,170))
self.screen.blit(self.font.render(str(active[i]) + ' - ' + self.content[active[i]]['title'], True, (255,255,255)),(10,i*self.box_height+4))
for i in range(self.box_per_screen):
pygame.draw.rect(self.screen,(50,0,0),pygame.Rect((0,i*self.box_height),(self.screen.get_width()/3,self.screen.get_height()/3)),2)
But still need to make some changes in the data:
def unlock_act(act_id):
if user['cash'] >= activities[act_id]['unlock'] and activities[act_id]['usr_progress'] == 0:
user['cash'] -= activities[act_id]['unlock']
activities[act_id]['usr_progress'] = 1
So the question is: in this situation should i keep a copy of the data as dict, and create a class with it plus the methods i need or use functions to edit the data inside the dict?
I want to have certain rows selected color be red instead of the standard color (blue on windows) so that I can indicate status. Anyone know if this is possible in wxPython?
In your class that you derive from wx.ListCtrl, take a look at overriding
def OnGetItemAttr(self, item):
return self.normalAttr[item % 2]
#
Where the item attributes are initialized ahead of time using:
self.normalAttr = []
self.normalAttr.append(wx.ListItemAttr())
grayAttr = wx.ListItemAttr()
grayAttr.SetBackgroundColour(lightGray)
self.normalAttr.append(grayAttr)
So in this case, I'm alternating background colors between the default, and a light Gray attribute.
This function is called for each row as its painted, so you can use it to indicate all sorts of status. If row is selected should be an easy case.
In order to do what you want, i.e. have a different selection color when certain items are selected, you will need to drop into win32. Fortunately, it is not too hard to do that in python. It does however make your code platform dependent. I tried it out today in a small program. If the Genre is not "Rock" I make the selection orange. Here are some screenshots.
Rock Items Selected
Mixed Items Selected. Notice the RnB and Blues are selected with Orange
alt text http://img258.imageshack.us/img258/1307/soshot2.jpg
Here is the code. It looks scary at first but if you know any win32, its not that bad. I make use of the pywin32 package and the std ctypes libraries. I had to define some of the SDK constants as they were not available in the win32con module.
import sys
import wx
import wx.lib.mixins.listctrl as listmix
import win32api
import win32gui
import win32con
import win32gui_struct
import commctrl
import ctypes
from ctypes.wintypes import BOOL, HWND, RECT, UINT, DWORD, HDC, DWORD, LPARAM, COLORREF
LVM_FIRST = 0x1000
LVM_GETSUBITEMRECT=(LVM_FIRST + 56)
LVIR_BOUNDS =0
LVIR_ICON =1
LVIR_LABEL =2
LVIR_SELECTBOUNDS =3
DEFAULT_GUI_FONT =17
#LPNMHDR
class NMHDR(ctypes.Structure):
pass
INT = ctypes.c_int
NMHDR._fields_ = [('hwndFrom', HWND), ('idFrom', UINT), ('code', INT)]
LPNMHDR = ctypes.POINTER(NMHDR)
#LPNMCUSTOMDRAW
class NMCUSTOMDRAW(ctypes.Structure):
pass
NMCUSTOMDRAW._fields_ = [('hdr', NMHDR), ('dwDrawStage', DWORD), ('hdc', ctypes.c_int),
('rc', RECT), ('dwItemSpec', DWORD), ('uItemState', UINT),
('lItemlParam', LPARAM)]
LPNMCUSTOMDRAW = ctypes.POINTER(NMCUSTOMDRAW)
#LPNMLVCUSTOMDRAW
class NMLVCUSTOMDRAW(ctypes.Structure):
pass
NMLVCUSTOMDRAW._fields_ = [('nmcd', NMCUSTOMDRAW),
('clrText', COLORREF),
('clrTextBk', COLORREF),
('iSubItem', ctypes.c_int),
('dwItemType', DWORD),
('clrFace', COLORREF),
('iIconEffect', ctypes.c_int),
('iIconPhase', ctypes.c_int),
('iPartId', ctypes.c_int),
('iStateId', ctypes.c_int),
('rcText', RECT),
('uAlign', UINT)
]
LPNMLVCUSTOMDRAW = ctypes.POINTER(NMLVCUSTOMDRAW)
musicdata = {
1 : ("Bad English", "The Price Of Love", "Rock"),
2 : ("DNA featuring Suzanne Vega", "Tom's Diner", "Rock"),
3 : ("George Michael", "Praying For Time", "Rock"),
4 : ("Gloria Estefan", "Here We Are", "Rock"),
5 : ("Linda Ronstadt", "Don't Know Much", "Rock"),
6 : ("Michael Bolton", "How Am I Supposed To Live Without You", "Blues"),
7 : ("Paul Young", "Oh Girl", "Rock"),
8 : ("Paula Abdul", "Opposites Attract", "Rock"),
9 : ("Richard Marx", "Should've Known Better", "Rock"),
10 : ("Bobby Brown", "My Prerogative", "RnB"),
}
class MyListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin):
def __init__(self, parent, ID, pos=wx.DefaultPosition,
size=wx.DefaultSize, style=0):
wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
listmix.ListCtrlAutoWidthMixin.__init__(self)
def ShouldCustomDraw(self, row):
if self.IsSelected(row):
listitem = self.GetItem(row, 2)
genre = listitem.GetText()
return genre != "Rock"
def CustomDraw(self, lpcd):
if lpcd.contents.nmcd.dwDrawStage == commctrl.CDDS_PREPAINT:
return (True, commctrl.CDRF_NOTIFYITEMDRAW)
if lpcd.contents.nmcd.dwDrawStage == commctrl.CDDS_ITEMPREPAINT:
if self.ShouldCustomDraw(lpcd.contents.nmcd.dwItemSpec):
#do custom drawing for non Rock selected rows
#paint the selection background
color = win32api.RGB(255, 127, 0) #orange
brush = win32gui.CreateSolidBrush(color)
r = lpcd.contents.nmcd.rc
win32gui.FillRect(int(lpcd.contents.nmcd.hdc), (r.left+4, r.top, r.right, r.bottom), brush)
win32gui.DeleteObject(brush)
return (True, commctrl.CDRF_NOTIFYSUBITEMDRAW)
if lpcd.contents.nmcd.dwDrawStage == commctrl.CDDS_ITEMPREPAINT|commctrl.CDDS_SUBITEM:
row = lpcd.contents.nmcd.dwItemSpec
col = lpcd.contents.iSubItem
item = self.GetItem(row, col)
text = item.GetText()
#paint the text
rc = RECT()
rc.top = col
if col > 0:
rc.left = LVIR_BOUNDS
else:
rc.left = LVIR_LABEL
success = win32api.SendMessage(self.Handle, LVM_GETSUBITEMRECT, row, ctypes.addressof(rc))
if col > 0:
rc.left += 5
else:
rc.left += 2
rc.top += 2
if success:
oldColor = win32gui.SetTextColor(lpcd.contents.nmcd.hdc, win32gui.GetSysColor(win32con.COLOR_HIGHLIGHTTEXT))
win32gui.DrawText(lpcd.contents.nmcd.hdc, text, len(text), (rc.left, rc.top, rc.right, rc.bottom), win32con.DT_LEFT|win32con.DT_VCENTER)
win32gui.SetTextColor(lpcd.contents.nmcd.hdc, oldColor)
return (True, commctrl.CDRF_SKIPDEFAULT)
# don't need custom drawing
return (True, commctrl.CDRF_DODEFAULT)
class MyFrame(wx.Frame):
def __init__(self, *args, **kwds):
wx.Frame.__init__(self, *args, **kwds)
self._sizer = wx.BoxSizer(wx.VERTICAL)
tID = wx.NewId()
self._ctl = MyListCtrl(self, tID,
style=wx.LC_REPORT
#| wx.BORDER_SUNKEN
| wx.BORDER_NONE
| wx.LC_EDIT_LABELS
| wx.LC_SORT_ASCENDING
#| wx.LC_NO_HEADER
#| wx.LC_VRULES
#| wx.LC_HRULES
#| wx.LC_SINGLE_SEL
)
self._sizer.Add(self._ctl, 1, wx.EXPAND, 3)
self.PopulateList()
self.oldWndProc = win32gui.SetWindowLong(self.GetHandle(), win32con.GWL_WNDPROC, self.MyWndProc)
def MyWndProc(self, hWnd, msg, wParam, lParam):
if msg == win32con.WM_NOTIFY:
hwndFrom, idFrom, code = win32gui_struct.UnpackWMNOTIFY(lParam)
if code == commctrl.NM_CUSTOMDRAW and hwndFrom == self._ctl.Handle:
lpcd = ctypes.cast(lParam, LPNMLVCUSTOMDRAW)
retProc, retCode = self._ctl.CustomDraw(lpcd)
if retProc:
return retCode
# Restore the old WndProc. Notice the use of wxin32api
# instead of win32gui here. This is to avoid an error due to
# not passing a callable object.
if msg == win32con.WM_DESTROY:
win32api.SetWindowLong(self.GetHandle(),
win32con.GWL_WNDPROC,
self.oldWndProc)
# Pass all messages (in this case, yours may be different) on
# to the original WndProc
return win32gui.CallWindowProc(self.oldWndProc,
hWnd, msg, wParam, lParam)
def PopulateList(self):
self._ctl.InsertColumn(0, "Artist")
self._ctl.InsertColumn(1, "Title")
self._ctl.InsertColumn(2, "Genre")
items = musicdata.items()
for key, data in items:
index = self._ctl.InsertStringItem(sys.maxint, data[0])
self._ctl.SetStringItem(index, 1, data[1])
self._ctl.SetStringItem(index, 2, data[2])
self._ctl.SetItemData(index, key)
self._ctl.SetColumnWidth(0, wx.LIST_AUTOSIZE)
self._ctl.SetColumnWidth(1, wx.LIST_AUTOSIZE)
self._ctl.SetColumnWidth(2, 100)
self.currentItem = 0
class MyApp(wx.App):
def OnInit(self):
frame = MyFrame(None, -1, 'wxListCtrl StackOverflow')
frame.Show()
self.SetTopWindow(frame)
return 1
if __name__ == "__main__":
app = MyApp(0)
app.MainLoop()