I'm using psychopy to build a cognitive task.
I have 5 circles on the screen and the participant needs to pressed on the good circle.
My code :
if mouse.isPressedIn(cercle_1):
continueRoutine = False
# save data if you like:
thisExp.addData('correct', 1)
thisExp.addData('RT', t)
elif mouse.isPressedIn(cercle_2):
# save data if you like:
thisExp.addData('correct', 0)
thisExp.addData('RT', t)
continueRoutine = True
elif mouse.isPressedIn(cercle_3):
# save data if you like:
thisExp.addData('correct', 0)
thisExp.addData('RT', t)
continueRoutine = True
elif mouse.isPressedIn(cercle_4):
# save data if you like:
thisExp.addData('correct', 0)
thisExp.addData('RT', t)
continueRoutine = True
elif mouse.isPressedIn(cercle_5):
# save data if you like:
thisExp.addData('correct', 0)
thisExp.addData('RT', t)
continueRoutine = True
The problem is that my datafile only contains the response time (RT) and the info of the circle_1. I would have no idea if the participant tried other circle before pressing on circle_1.
Question : How can I have in my csv file infos about all the times a participant pressed the mouse bouton.Maybe before pressing cercle_1, he pressed the cercle_3. Right now, I only have how long it took to get the correct answer.
It sounds like you want to record a sequence of events in the trial. This can be hard to find an appropriate data structure for, but in your case, I can think of two solutions.
Record the number of wrong responses
Have a column (e.g. n_wrong) and count the number of non-circle_1 responses. In Begin routine add
n_wrong = 0
Then in each frame add:
if mouse.isPressedIn(cercle_1):
thisExp.addData('correct', 1)
thisExp.addData('RT', t)
continueRoutine = False
elif mouse.isPressedIn(cercle_2) or mouse.isPressedIn(cercle_3) or mouse.isPressedIn(cercle_4) or mouse.isPressedIn(cercle_5):
thisExp.addData('correct', 0)
thisExp.addData('RT', t)
n_wrong += 1 # One more error recorded!
# Now wait until the mouse release to prevent recording 60 wrong clicks per second!
while any(mouse.getPressed()):
pass
Then under end routine add:
thisExp.addData('n_wrong', n_wrong)
Record which circles were pressed
The other is to have a column for each circle and shift those from "unpressed" to "pressed" when they are clicked. Then the column cercle1 would correspond to what you currently call the correct column. So under begin routine:
# Mark all non-target cirlces as unpressed
thisExp.addData('cercle1', 0)
thisExp.addData('cercle2', 0)
thisExp.addData('cercle3', 0)
thisExp.addData('cercle4', 0)
thisExp.addData('cercle5', 0)
Then under each frame I would do this:
if mouse.isPressedIn(cercle_1):
thisExp.addData('cercle1', 1)
continueRoutine = False
if mouse.isPressedIn(cercle_2):
thisExp.addData('cercle2', 1)
if mouse.isPressedIn(cercle_3):
thisExp.addData('cercle3', 1)
if mouse.isPressedIn(cercle_4):
thisExp.addData('cercle4', 1)
if mouse.isPressedIn(cercle_5):
thisExp.addData('cercle5', 1)
The latter approach could be extended with reaction times by adding columns called cercle1_rt etc. but then you'd also need to do the while any(mouse.getPressed()): pass trick to record the onset and not just the release.
Related
I have an Excel (.xlsx) file that I'm trying to parse, row by row. I have a header (first row) that has a bunch of column titles like School, First Name, Last Name, Email, etc.
When I loop through each row, I want to be able to say something like:
row['School']
and get back the value of the cell in the current row and the column with 'School' as its title.
I've looked through the OpenPyXL docs but can't seem to find anything terribly helpful.
Any suggestions?
I'm not incredibly familiar with OpenPyXL, but as far as I can tell it doesn't have any kind of dict reader/iterator helper. However, it's fairly easy to iterate over the worksheet rows, as well as to create a dict from two lists of values.
def iter_worksheet(worksheet):
# It's necessary to get a reference to the generator, as
# `worksheet.rows` returns a new iterator on each access.
rows = worksheet.rows
# Get the header values as keys and move the iterator to the next item
keys = [c.value for c in next(rows)]
for row in rows:
values = [c.value for c in row]
yield dict(zip(keys, values))
Excel sheets are far more flexible than CSV files so it makes little sense to have something like DictReader.
Just create an auxiliary dictionary from the relevant column titles.
If you have columns like "School", "First Name", "Last Name", "EMail" you can create the dictionary like this.
keys = dict((value, idx) for (idx, value) in enumerate(values))
for row in ws.rows[1:]:
school = row[keys['School'].value
I wrote DictReader based on openpyxl. Save the second listing to file 'excel.py' and use it as csv.DictReader. See usage example in the first listing.
with open('example01.xlsx', 'rb') as source_data:
from excel import DictReader
for row in DictReader(source_data, sheet_index=0):
print(row)
excel.py:
__all__ = ['DictReader']
from openpyxl import load_workbook
from openpyxl.cell import Cell
Cell.__init__.__defaults__ = (None, None, '', None) # Change the default value for the Cell from None to `` the same way as in csv.DictReader
class DictReader(object):
def __init__(self, f, sheet_index,
fieldnames=None, restkey=None, restval=None):
self._fieldnames = fieldnames # list of keys for the dict
self.restkey = restkey # key to catch long rows
self.restval = restval # default value for short rows
self.reader = load_workbook(f, data_only=True).worksheets[sheet_index].iter_rows(values_only=True)
self.line_num = 0
def __iter__(self):
return self
#property
def fieldnames(self):
if self._fieldnames is None:
try:
self._fieldnames = next(self.reader)
self.line_num += 1
except StopIteration:
pass
return self._fieldnames
#fieldnames.setter
def fieldnames(self, value):
self._fieldnames = value
def __next__(self):
if self.line_num == 0:
# Used only for its side effect.
self.fieldnames
row = next(self.reader)
self.line_num += 1
# unlike the basic reader, we prefer not to return blanks,
# because we will typically wind up with a dict full of None
# values
while row == ():
row = next(self.reader)
d = dict(zip(self.fieldnames, row))
lf = len(self.fieldnames)
lr = len(row)
if lf < lr:
d[self.restkey] = row[lf:]
elif lf > lr:
for key in self.fieldnames[lr:]:
d[key] = self.restval
return d
The following seems to work for me.
header = True
headings = []
for row in ws.rows:
if header:
for cell in row:
headings.append(cell.value)
header = False
continue
rowData = dict(zip(headings, row))
wantedValue = rowData['myHeading'].value
I was running into the same issue as described above. Therefore I created a simple extension called openpyxl-dictreader that can be installed through pip. It is very similar to the suggestion made by #viktor earlier in this thread.
The package is largely based on source code of Python's native csv.DictReader class. It allows you to select items based on column names using openpyxl. For example:
import openpyxl_dictreader
reader = openpyxl_dictreader.DictReader("names.xlsx", "Sheet1")
for row in reader:
print(row["First Name"], row["Last Name"])
Putting this here for reference.
I have an application which uses a QTableView/QAbstractTableModel combination. For the view, I've defined a Delegate which displays an image (a QPixmap, loaded from an image file) in one column of the table view.
Basically, the problem is that when a cell in the column with the Delegate is selected, sometimes the background shows and sometimes it doesn't.
Here is what I've discovered by experimentation so far, and I can't make much sense of it:
I have this relatively short test program:
from PyQt5 import QtCore, QtWidgets, QtGui
import sys
# ------------------------------------------------------------------------------
class TableModel(QtCore.QAbstractTableModel):
def __init__(self, data = [[]], headers = None, parent = None):
QtCore.QAbstractTableModel.__init__(self, parent)
self.__data = data
def rowCount(self, parent):
return len(self.__data)
def columnCount(self, parent):
return len(self.__data[0])
def data(self, index, role):
row = index.row()
column = index.column()
if role == QtCore.Qt.DisplayRole:
value = self.__data[row][column]
return value
def flags(self, index):
return QtCore.Qt.ItemIsEnabled|QtCore.Qt.ItemIsEditable|QtCore.Qt.ItemIsSelectable
# ------------------------------------------------------------------------------
class Delegate(QtWidgets.QStyledItemDelegate):
def paint(self, painter, option, index):
if (index.column() == 0):
image = QtGui.QImage('open.png')
pixmap = QtGui.QPixmap.fromImage(image)
x = option.rect.center().x() - pixmap.rect().width() / 2
y = option.rect.center().y() - pixmap.rect().height() / 2
painter.drawPixmap(x, y, pixmap)
# ------------------------------------------------------------------------------
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
app.setStyle('fusion')
tableView = QtWidgets.QTableView()
tableView.setItemDelegateForColumn(0, Delegate())
tableView.resize(550, 160)
tableView.show()
rowCount = 3
columnCount = 4
data = [
[i for i in range(columnCount)]
for j in range(rowCount)
]
model = TableModel(data)
tableView.setModel(model)
sys.exit(app.exec_())
When I specify app.setStyle('fusion') in __main__, I get what I would expect: When a cell in the column with the Delegate is selected, the cell background is blue and the image appears in front of it:
However, if I change to app.setStyle('windows'), even though in general it uses the same blue background for selected cells, when I move to a cell in the first column, the background disappears:
(You can't obviously see it, but the same cell is selected as in the first example).
That's just a piece of test code, which I don't completely understand.
In the actual application I'm writing, I am using Qt Designer to create the UI. Even though I specify app.setStyle('fusion'), the table has entirely different styling, with a different appearance to the background of a selected cell:
I can't for the life of me figure out where it is picking up the different style. It must come from Qt Designer somehow, but I've looked at the .py file Qt Designer creates, and I can't find it.
This style (wherever it comes from) seems to suffer from the same problem as the windows style. In the image above, there is no Delegate in use. The cell in row 2/column 2 is selected, and the background shows.
But if I add a Delegate to display a QPixmap in column 2, then the background does not show when the cell is selected:
(It's selected; take my word for it).
I thought maybe it was the case that once you use a Delegate to display an image, you could no longer get a background in the selected cell. But you obviously can. It works in one case, just not the others.
If anyone can shed light on this, I'd appreciate it. (I realize this is long; thanks for sticking with me).
I've been fiddling around with this issue more, and I've learned some things about my original question. In retrospect, I think it was not as clear as it could have been (or maybe I just understand it all a bit better).
For starters, I never should have referred to cells as being "selected". In fact, I don't even have the Qt.ItemIsSelectable flag set for any of the cells in the view. What I really have been trying to do is control the background of a cell when it is active (for lack of a better word) -- meaning it is the cell where the cursor is currently positioned.
This can be done by overriding initStyleOption() in the Delegate. My original test code is modified as shown below:
from PyQt5 import QtCore, QtWidgets, QtGui
import sys
# ------------------------------------------------------------------------------
class TableModel(QtCore.QAbstractTableModel):
def __init__(self, data = [[]], headers = None, parent = None):
QtCore.QAbstractTableModel.__init__(self, parent)
self.__data = data
def rowCount(self, parent):
return len(self.__data)
def columnCount(self, parent):
return len(self.__data[0])
def data(self, index, role):
row = index.row()
column = index.column()
if role == QtCore.Qt.DisplayRole:
value = self.__data[row][column]
return value
if role == QtCore.Qt.BackgroundRole:
return QtGui.QBrush(QtGui.QColor(255, 255, 255))
def flags(self, index):
return QtCore.Qt.ItemIsEnabled|QtCore.Qt.ItemIsEditable
# ------------------------------------------------------------------------------
class TableView(QtWidgets.QTableView):
def __init__(self, parent=None):
super().__init__(parent)
# ------------------------------------------------------------------------------
class Delegate(QtWidgets.QStyledItemDelegate):
# <Modification>
def initStyleOption(self, option, index):
super().initStyleOption(option, index)
if (
index.row() == tableView.currentIndex().row() and
index.column() == tableView.currentIndex().column()
):
option.backgroundBrush = QtGui.QBrush(QtGui.QColor(232, 244, 252))
def paint(self, painter, option, index):
if (index.column() == 0):
# <Modification>
if (
index.row() == tableView.currentIndex().row() and
index.column() == tableView.currentIndex().column()
):
self.initStyleOption(option, index)
painter.setPen(QtCore.Qt.NoPen)
painter.setBrush(option.backgroundBrush)
painter.drawRect(option.rect)
image = QtGui.QImage('open.png')
pixmap = QtGui.QPixmap.fromImage(image)
x = option.rect.center().x() - pixmap.rect().width() / 2
y = option.rect.center().y() - pixmap.rect().height() / 2
painter.drawPixmap(x, y, pixmap)
else:
super().paint(painter, option, index)
# ------------------------------------------------------------------------------
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
app.setStyle('fusion')
tableView = TableView()
tableView.resize(550, 160)
tableView.setItemDelegate(Delegate())
tableView.show()
rowCount = 3
columnCount = 4
data = [
[i for i in range(columnCount)]
for j in range(rowCount)
]
model = TableModel(data)
tableView.setModel(model)
sys.exit(app.exec_())
initStyleOption() sets the background brush for a cell when it is active (current). But as I bemoaned before, this doesn't occur in the first column, which has a Delegate with a custom paint() method that displays a pixmap. So paint() must also take responsibility for setting the background for cells in that column when they are active. It uses the same backgroundBrush that initStyleOption() set.
The result is very nearly what I'm shooting for. The only fly in the ointment is that there is still clearly additional styling going on, that affects all the cells in the view except those in column 1 with the custom Delegate. So they don't look quite exactly alike when active:
(It's subtle, but there's a bit of a gradient to the background of the cell in column 2, which is absent in column 1).
I know now that there are style 'factories' that apply a widget-wide style. Since I'm using Fusion, that is evidently where the extra styling is coming from.
So now my question is -- where is that styling defined, and do I have any control over it? If I could see it, I could make my custom background style match it. Better yet, if I could modify it, I could make it match mine.
I had the same problem with my own tool today. I think your issue is the same as this other question. In short, you just need to call super in paint before doing any of your extra work. When I added super to my own code, selections worked again as expected in the delegate.
class Delegate(QtWidgets.QStyledItemDelegate):
def paint(self, painter, option, index):
super().paint(painter, option, index)
if (index.column() == 0):
image = QtGui.QImage('open.png')
pixmap = QtGui.QPixmap.fromImage(image)
x = option.rect.center().x() - pixmap.rect().width() / 2
y = option.rect.center().y() - pixmap.rect().height() / 2
painter.drawPixmap(x, y, pixmap)
(FWIW I haven't tested the code above. But it should work).
I have a set of points and I want to create line / road network from those points. Firstly, I need to determine the closest point from each of the points. For that, I used the KD Tree and developed a code like this:
def closestPoint(source, X = None, Y = None):
df = pd.DataFrame(source).copy(deep = True) #Ensure source is a dataframe, working on a copy to keep the datasource
if(X is None and Y is None):
raise ValueError ("Please specify coordinate")
elif(not X in df.keys() and not Y in df.keys()):
raise ValueError ("X and/or Y is/are not in column names")
else:
df["coord"] = tuple(zip(df[X],df[Y])) #create a coordinate
if (df["coord"].duplicated):
uniq = df.drop_duplicates("coord")["coord"]
uniqval = list(uniq.get_values())
dupl = df[df["coord"].duplicated()]["coord"]
duplval = list(dupl.get_values())
for kq,vq in uniq.items():
clstu = spatial.KDTree(uniqval).query(vq, k = 3)[1]
df.at[kq,"coord"] = [vq,uniqval[clstu[1]]]
if([uniqval[clstu[1]],vq] in list(df["coord"]) ):
df.at[kq,"coord"] = [vq,uniqval[clstu[2]]]
for kd,vd in dupl.items():
clstd = spatial.KDTree(duplval).query(vd,k = 1)[1]
df.at[kd,"coord"] = [vd,duplval[clstd]]
else:
val = df["coord"].get_values()
for k,v in df["coord"].items():
clst = spatial.KDTree(val).query(vd, k = 3)[1]
df.at[k,"coord"] = [v,val[clst[1]]]
if([val[clst[1]],v] in list (df["coord"])):
df.at[k,"coord"] = [v,val[clst[2]]]
return df["coord"]
The code can return the the closest points around. However, I need to ensure that no double lines are created (e.g (x,y) to (x1,y1) and (x1,y1) to (x,y)) and also I need to ensure that each point can only be used as a starting point of a line and an end point of a line despite the point being the closest one to the other points.
Below is the visualization of the result:
Result of the code
What I want:
What I want
I've also tried to separate the origin and target coordinate and do it like this:
df["coord"] = tuple(zip(df[X],df[Y])) #create a coordinate
df["target"] = "" #create a column for target points
count = 2 # create a count iteration
if (df["coord"].duplicated):
uniq = df.drop_duplicates("coord")["coord"]
uniqval = list(uniq.get_values())
for kq,vq in uniq.items():
clstu = spatial.KDTree(uniqval).query(vq, k = count)[1]
while not vq in (list(df["target"]) and list(df["coord"])):
clstu = spatial.KDTree(uniqval).query(vq, k = count)[1]
df.set_value(kq, "target", uniqval[clstu[count-1]])
else:
count += 1
clstu = spatial.KDTree(uniqval).query(vq, k = count)[1]
df.set_value(kq, "target", uniqval[clstu[count-1]])
but this return an error
IndexError: list index out of range
Can anyone help me with this? Many thanks!
Answering now about the global strategy, here is what I would do (rough pseudo-algorithm):
current_point = one starting point in uniqval
while (uniqval not empty)
construct KDTree from uniqval and use it for next line
next_point = point in uniqval closest to current_point
record next_point as target for current_point
remove current_point from uniqval
current_point = next_point
What you will obtain is a linear graph joining all your points, using closest neighbors "in some way". I don't know if it will fit your needs. You would also obtain a linear graph by taking next_point at random...
It is hard to comment on your global strategy without further detail about the kind of road network your want to obtain. So let me just comment your specific code and explain why the "out of range" error happens. I hope this can help.
First, are you aware that (list_a and list_b) will return list_a if it is empty, else list_b? Second, isn't the condition (vq in list(df["coord"]) always True? If yes, then your while loop is just always executing the else statement, and at the last iteration of the for loop, (count-1) will be greater than the total number of (unique) points. Hence your KDTree query does not return enough points and clstu[count-1] is out of range.
I am creating some code for a school project, and for a module I use later on, I need to know what the intensity to end on(end_intensity) is. When the code is run, the end_intensity still comes out as unassigned, this means that the
if client_intensity == "High":
line is never being run.Can someone please explain why it won't assign .
correct = False
end_intensity = "Unassigned"
while correct != True:
id_search = input("please enter the Client ID of the client you wish to record results for:")
# open file, with will automatically close it for you
with open("text_documents/clientIntensity.txt") as f:
user_found = False
# loop over every line
for line in f:
client,intensity = line.split(",")
if id_search == client:
correct = True
user_found = True
intensity = str (intensity)
client_intensity = intensity
#assigns which one is the end intensity
if intensity == 'High':
end_intensity = 'Moderate'
elif intensity == 'Moderate':
end_intensity = 'High'
if user_found == False:
print("I'm sorry no results we're found for that ID, please try again\n")
print(end_intensity)
The text document is in this format:
NeQua,High
ImKol,Moderate
YoTri,Moderate
(I apologize for the numbers for the text document formatting,stack overflow would only let me show it like that)
Any help would be appreciated,Thanks
Ieuan
I was trying to solve Challenge 2 at the end of the classes chapter (Chapter 8) in "Python Programming for the Absolute Beginner" which is stated as:
Write a program that simulates a television by creating it as an object. The user should be able to enter a channel number and raise or lower the volume. Make sure that the channel number and the volume level stay within valid ranges.
I keep getting: TypeError: 'int' object is not callable, which at this stage just isn't very helpful.
I'm a beginner but I've seen something really similar working (see at the bottom right below my code) and nearly went as far as nearly copying that code. Could somebody maybe explain what's wrong with this and how I can get it to work?
Here's the complete error:
Traceback (most recent call last):
File "Untitled 3.py", line 59, in <module>
main()
File "Untitled 3.py", line 50, in main
tv.channel(newchannel = int(input("What channel would you like to set the TV to?")))
TypeError: 'int' object is not callable
My code is below,
Thanks
class Television(object):
"""a TV"""
def __init__(self, channel = 0, volume = 0):
self.channel = channel
self.volume = volume
def channel(self, newchannel = 0):
if newchannel <= 0 or newchannel >9:
print("No negative numbers or numbers higher than 9. Start again from the menu")
else:
self.channel = newchannel
print("You set the TV on channel", self.channel)
def volume(self, newvolume = 0):
if newvolume <= 0 or newvolume >9:
print("No negative numbers or numbers higher than 9. Start again from the menu")
else:
self.volume = newvolume
print("You set the TV on volume", self.volume)
def watch(self):
print("You are watching channel", self.channel, "at volume", self.volume)
def main():
tv = Television()
choice = None
while choice != "0":
print \
("""
TV
0 - Quit
1 - Watch the TV
2 - Change channel
3 - Set the volume
""")
choice = input("Choice: ")
print()
# exit
if choice == "0":
print("Good-bye.")
elif choice == "1":
tv.watching()
elif choice == "2":
tv.channel(newchannel = int(input("What channel would you like to set the TV to?")))
elif choice == "3":
tv.volume(newvolume = int(input("What channel would you like to set the TV to?")))
# some unknown choice
else:
print("\nSorry, but", choice, "isn't a valid choice.")
main()
("\n\nPress the enter key to exit.")
Why does the following work instead? To me, it looks pretty similar to what I've done.
class Critter(object):
"""A virtual pet"""
def __init__(self, hunger = 0, boredom = 0):
self.hunger = hunger
self.boredom = boredom
def eat(self, food = 4):
print("Brruppp. Thank you.")
self.hunger += food
if self.hunger < 0:
self.hunger = 0
crit = Critter()
print(crit.hunger)
crit.eat(food = int(input("how much do you want to feed him?")))
print(crit.hunger)
The problem is you are defining a method with the same name as a property. That is, you're saying Television.channel is an int, but later you are binding a method to that name.