Skip to content
Snippets Groups Projects
Commit bba5d3b7 authored by Lucas Philippe's avatar Lucas Philippe
Browse files

Ajout TP2 fonctionnel

parent ef888311
Branches
No related tags found
No related merge requests found
Showing
with 379 additions and 64 deletions
...@@ -31,4 +31,3 @@ class BubbleCursor: ...@@ -31,4 +31,3 @@ class BubbleCursor:
if previous_closest: if previous_closest:
previous_closest.highlighted = False previous_closest.highlighted = False
self.closest.highlighted = True self.closest.highlighted = True
import csv, random, time import csv
import random
import time
from PyQt5.QtGui import QPainter from PyQt5.QtGui import QPainter
from PyQt5.QtWidgets import QWidget from PyQt5.QtWidgets import QWidget
from Target import Target
from BubbleCursor import BubbleCursor from BubbleCursor import BubbleCursor
from Target import Target
class BubbleWidget(QWidget): class BubbleWidget(QWidget):
def __init__(self): def __init__(self, file_name, exp_setup, number_of_targets):
super().__init__() super().__init__()
self.exp_setup = exp_setup
self.targets = [] self.targets = []
self.loadTargets()
self.cursor = BubbleCursor(self.targets)
self.setMouseTracking(True) self.setMouseTracking(True)
self.start_time = None self.start_time = None
self.selectRandomTarget() self.errors = 0
def loadTargets(self): # Chargement du fichier csv
with open('src_tp_bubble.csv', newline='') as csvfile: with open(file_name) as csvfile:
reader = csv.reader(csvfile) reader = csv.reader(csvfile)
for row in reader: for row in reader:
if len(row) >= 3: x = int(row[0])
x, y, size = map(int, row[:3]) y = int(row[1])
self.targets.append(Target(x,y,size)) size = int(row[2])
target = Target(x, y, size)
self.targets.append(target)
self.target_to_select = self.targets[:number_of_targets]
self.cursor = BubbleCursor(self.targets)
self.selectRandomTarget()
def paintEvent(self, event): def paintEvent(self, event):
painter = QPainter(self) painter = QPainter(self)
...@@ -35,15 +45,23 @@ class BubbleWidget(QWidget): ...@@ -35,15 +45,23 @@ class BubbleWidget(QWidget):
def selectRandomTarget(self): def selectRandomTarget(self):
if self.start_time == None: if self.start_time == None:
self.start_time = time.time() self.start_time = time.time()
target = random.choice(self.targets) target = self.target_to_select.pop()
target.toSelect = True target.toSelect = True
self.update() self.update()
def printTime(self): def printLog(self):
print((time.time() - self.start_time)*1000) current_time = (time.time() - self.start_time)*1000
self.exp_setup.add_line_to_response(current_time, self.errors)
self.errors = 0
self.start_time = None self.start_time = None
def mousePressEvent(self, event): def mousePressEvent(self, event):
if self.cursor.closest is not None:
if self.cursor.closest.click_cible(): if self.cursor.closest.click_cible():
self.printTime() self.printLog()
if len(self.target_to_select) > 0:
self.selectRandomTarget() self.selectRandomTarget()
else:
self.exp_setup.next_experience()
else:
self.errors += 1
...@@ -3,14 +3,18 @@ import sys ...@@ -3,14 +3,18 @@ import sys
from PyQt5.QtGui import QPainter from PyQt5.QtGui import QPainter
from PyQt5.QtWidgets import QApplication, QMainWindow from PyQt5.QtWidgets import QApplication, QMainWindow
from BubbleWidget import BubbleWidget from BubbleWidget import BubbleWidget
file_name = "data/src_tp_bubble.csv"
def main(): def main():
app = QApplication(sys.argv) app = QApplication(sys.argv)
main_window = QMainWindow() main_window = QMainWindow()
main_window.resize(1024, 800) main_window.resize(1024, 800)
bubble_widget = BubbleWidget() bubble_widget = BubbleWidget(file_name)
main_window.setCentralWidget(bubble_widget) main_window.setCentralWidget(bubble_widget)
main_window.show() main_window.show()
sys.exit(app.exec_()) sys.exit(app.exec_())
if __name__ == "__main__": if __name__ == "__main__":
if len(sys.argv) == 2:
file_name = sys.argv[1]
main() main()
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow from PyQt5.QtWidgets import QApplication, QMainWindow
from NormalWidget import NormalWidget from NormalWidget import NormalWidget
file_name = "data/src_tp_bubble.csv"
def main(): def main():
app = QApplication([]) app = QApplication([])
window = QMainWindow() window = QMainWindow()
window.resize(1024, 800) window.resize(1024, 800)
normalWidget = NormalWidget() normalWidget = NormalWidget(file_name)
window.setCentralWidget(normalWidget) window.setCentralWidget(normalWidget)
window.show() window.show()
app.exec_() app.exec_()
if __name__ == "__main__": if __name__ == "__main__":
if len(sys.argv) == 2:
file_name = sys.argv[1]
main() main()
from PyQt5.QtWidgets import QApplication, QMainWindow from PyQt5.QtWidgets import QApplication, QMainWindow
from RopeWidget import RopeWidget from RopeWidget import RopeWidget
file_name = "data/src_tp_bubble.csv"
def main(): def main():
app = QApplication([]) app = QApplication([])
window = QMainWindow() window = QMainWindow()
window.resize(1024, 800) window.resize(1024, 800)
bubble_widget = RopeWidget() bubble_widget = RopeWidget(file_name)
window.setCentralWidget(bubble_widget) window.setCentralWidget(bubble_widget)
window.show() window.show()
app.exec_() app.exec_()
......
...@@ -11,16 +11,19 @@ class NormalCursor: ...@@ -11,16 +11,19 @@ class NormalCursor:
def move(self, x, y): def move(self, x, y):
self.x = x self.x = x
self.y = y self.y = y
previous_closest = self.closest closest_target = None
min_distance = float('inf')
for target in self.targets: for target in self.targets:
distance = ((self.x - target.x) ** 2 + (self.y - target.y) ** 2) ** 0.5 # cf. Theoreme de Pythagore distance = ((self.x - target.x) ** 2 + (self.y - target.y) ** 2) ** 0.5
if distance < min_distance:
min_distance = distance
closest_target = target
if distance < target.size/2: if closest_target and min_distance < closest_target.size / 2:
self.closest = target closest_target.highlighted = True
self.closest.highlighted = True else:
if self.closest and self.closest != closest_target:
self.closest.highlighted = False
if previous_closest is not None: self.closest = closest_target
distance = ((self.x - previous_closest.x) ** 2 + (self.y - previous_closest.y) ** 2) ** 0.5 # cf. Theoreme de Pythagore \ No newline at end of file
if distance > previous_closest.size / 2:
previous_closest.highlighted = False
\ No newline at end of file
import csv, random, time import csv
import random
import time
from PyQt5.QtGui import QPainter from PyQt5.QtGui import QPainter
from PyQt5.QtWidgets import QWidget from PyQt5.QtWidgets import QWidget
from NormalCursor import NormalCursor
from Target import Target from Target import Target
from TP1.NormalCursor import NormalCursor
class NormalWidget(QWidget): class NormalWidget(QWidget):
def __init__(self): def __init__(self, file_name, exp_setup, number_of_targets):
super().__init__() super().__init__()
self.targets = [] self.targets = []
self.loadTargets()
self.cursor = NormalCursor(self.targets)
self.setMouseTracking(True) self.setMouseTracking(True)
self.start_time = None self.start_time = None
self.selectRandomTarget() self.exp_setup = exp_setup
self.errors = 0
def loadTargets(self): # Chargement du fichier csv
with open('src_tp_bubble.csv', newline='') as csvfile: with open(file_name) as csvfile:
reader = csv.reader(csvfile) reader = csv.reader(csvfile)
for row in reader: for row in reader:
if len(row) >= 3: x = int(row[0])
x, y, size = map(int, row[:3]) y = int(row[1])
self.targets.append(Target(x,y,size)) size = int(row[2])
target = Target(x, y, size)
self.targets.append(target)
self.target_to_select = self.targets[:number_of_targets]
self.cursor = NormalCursor(self.targets)
self.selectRandomTarget()
def paintEvent(self, event): def paintEvent(self, event):
painter = QPainter(self) painter = QPainter(self)
...@@ -32,17 +41,30 @@ class NormalWidget(QWidget): ...@@ -32,17 +41,30 @@ class NormalWidget(QWidget):
self.update() self.update()
def selectRandomTarget(self): def selectRandomTarget(self):
if self.start_time == None: if self.start_time is None:
self.start_time = time.time() self.start_time = time.time()
target = random.choice(self.targets)
target = self.target_to_select.pop()
target.toSelect = True target.toSelect = True
self.update() self.update()
def printTime(self): def printLog(self):
print((time.time() - self.start_time)*1000) current_time = (time.time() - self.start_time)*1000
self.exp_setup.add_line_to_response(current_time, self.errors)
self.errors = 0
self.start_time = None self.start_time = None
def mousePressEvent(self, event): def mousePressEvent(self, event):
if self.cursor.closest is not None and self.cursor.closest.toSelect:
distance = ((event.x() - self.cursor.closest.x) ** 2 + (event.y() - self.cursor.closest.y) ** 2) ** 0.5
if distance <= self.cursor.closest.size / 2:
if self.cursor.closest.click_cible(): if self.cursor.closest.click_cible():
self.printTime() self.printLog()
if len(self.target_to_select) > 0:
self.selectRandomTarget() self.selectRandomTarget()
else:
self.exp_setup.next_experience()
else:
self.errors += 1
else:
self.errors += 1
\ No newline at end of file
# Programmation d'une technique d'interaction : le Bubble Cursor
...@@ -5,22 +5,27 @@ from Target import Target ...@@ -5,22 +5,27 @@ from Target import Target
from RopeCursor import RopeCursor from RopeCursor import RopeCursor
class RopeWidget(QWidget): class RopeWidget(QWidget):
def __init__(self): def __init__(self, file_name, exp_setup, number_of_targets):
super().__init__() super().__init__()
self.exp_setup = exp_setup
self.targets = [] self.targets = []
self.loadTargets()
self.cursor = RopeCursor(self.targets)
self.setMouseTracking(True) self.setMouseTracking(True)
self.start_time = None self.start_time = None
self.selectRandomTarget() self.errors = 0
def loadTargets(self): # Chargement du fichier csv
with open('src_tp_bubble.csv', newline='') as csvfile: with open(file_name) as csvfile:
reader = csv.reader(csvfile) reader = csv.reader(csvfile)
for row in reader: for row in reader:
if len(row) >= 3: x = int(row[0])
x, y, size = map(int, row[:3]) y = int(row[1])
self.targets.append(Target(x,y,size)) size = int(row[2])
target = Target(x, y, size)
self.targets.append(target)
self.target_to_select = self.targets[:number_of_targets]
self.cursor = RopeCursor(self.targets)
self.selectRandomTarget()
def paintEvent(self, event): def paintEvent(self, event):
painter = QPainter(self) painter = QPainter(self)
...@@ -35,15 +40,23 @@ class RopeWidget(QWidget): ...@@ -35,15 +40,23 @@ class RopeWidget(QWidget):
def selectRandomTarget(self): def selectRandomTarget(self):
if self.start_time == None: if self.start_time == None:
self.start_time = time.time() self.start_time = time.time()
target = random.choice(self.targets) target = self.target_to_select.pop()
target.toSelect = True target.toSelect = True
self.update() self.update()
def printTime(self): def printLog(self):
print((time.time() - self.start_time)*1000) current_time = (time.time() - self.start_time)*1000
self.exp_setup.add_line_to_response(current_time, self.errors)
self.errors = 0
self.start_time = None self.start_time = None
def mousePressEvent(self, event): def mousePressEvent(self, event):
if self.cursor.closest is not None:
if self.cursor.closest.click_cible(): if self.cursor.closest.click_cible():
self.printTime() self.printLog()
if len(self.target_to_select) > 0:
self.selectRandomTarget() self.selectRandomTarget()
else:
self.exp_setup.next_experience()
else:
self.errors += 1
No preview for this file type
No preview for this file type
No preview for this file type
import os
import sys
from PyQt5.QtGui import QIntValidator
from PyQt5.QtWidgets import QDialog, QLabel, QLineEdit, QComboBox, QGridLayout, QPushButton, QHBoxLayout, QVBoxLayout
from Experience import Experience
from FileDisplayWidget import FileDisplayWidget
DIR_PATH = "experience/generated_exp/"
TARGETS_PER_EXPERIENCE = 15
class ExpSetup(QDialog):
def __init__(self, window):
super().__init__()
self.file_name = 'experience/data/response.csv'
self.current_ordonnance = None
self.ordonnance = None
self.window = window
self.user_number = None
self.technique = None
self.repeats = None
self.current_repeat_series = 0
self.current_target_number = 1
user_label = QLabel("Numéro d'utilisateur :")
self.user_edit = QLineEdit()
self.user_edit.setValidator(QIntValidator())
tech_label = QLabel("Technique :")
self.tech_combo = QComboBox()
self.tech_combo.addItems(["Bubble", "Rope", "Normal"])
repeats_label = QLabel("Nombre de répétitions :")
self.repeats_edit = QLineEdit()
self.repeats_edit.setValidator(QIntValidator())
layout = QGridLayout()
layout.addWidget(user_label, 0, 0)
layout.addWidget(self.user_edit, 0, 1)
layout.addWidget(tech_label, 1, 0)
layout.addWidget(self.tech_combo, 1, 1)
layout.addWidget(repeats_label, 4, 0)
layout.addWidget(self.repeats_edit, 4, 1)
self.validate_button = QPushButton("Valider")
self.validate_button.clicked.connect(self.verify_entry)
h_layout = QHBoxLayout()
h_layout.addStretch()
h_layout.addWidget(self.validate_button)
h_layout.addStretch()
v_layout = QVBoxLayout()
v_layout.addLayout(layout)
v_layout.addLayout(h_layout)
self.setLayout(v_layout)
def verify_entry(self):
self.user_number = self.user_edit.text()
self.technique = self.tech_combo.currentIndex()
self.repeats = int(self.repeats_edit.text())
if self.repeats <= 1:
print('Répétition doit être strictement supérieur à 1')
else:
self.init_ordonnance()
self.next_experience()
def create_experience(self, experience, density, target_size):
if experience not in Experience:
# Error
print('Aucune expérience selectionné')
return None
density = str(density)
target_size = str(target_size)
file_name = "src_d_" + density + "_s_" + target_size + ".csv"
file_path = DIR_PATH + file_name
return experience.create_widget(file_path, self, TARGETS_PER_EXPERIENCE)
def start_experience(self, widget):
if widget is not None:
self.window.resize(1024, 800)
self.window.setCentralWidget(widget)
self.window.show()
def init_ordonnance(self):
self.ordonnance = []
self.current_ordonnance = -1
experience_list = [Experience.BUBBLE, Experience.ROPE, Experience.NORMAL]
experience_ordered = experience_list[self.technique:] + experience_list[:self.technique]
density_list = [30, 60, 90]
size_list = [9, 12, 18]
# Répéter la création des widgets pour chaque expérience en fonction du nombre de répétitions
for _ in range(self.repeats): # Répéter pour le nombre total de répétitions
for experience in experience_ordered:
for density in density_list:
for size in size_list:
widget = self.create_experience(experience, density, size)
self.ordonnance.append((widget, experience, density, size))
def next_experience(self):
self.current_target_number = 1
self.current_ordonnance += 1
if self.current_ordonnance >= len(self.ordonnance):
self.display_data()
return
if self.current_ordonnance % (len(self.ordonnance) / self.repeats) == 0:
self.current_repeat_series += 1
widget = self.ordonnance[self.current_ordonnance][0]
self.start_experience(widget)
def add_line_to_response(self, time, error):
line = f"{self.user_number}, {self.current_repeat_series}, {self.current_target_number}, {self.ordonnance[self.current_ordonnance][2]}, {self.ordonnance[self.current_ordonnance][3]}, {self.ordonnance[self.current_ordonnance][1].name}, {time}, {error}"
with open(self.file_name, "a") as file:
file.write(line + '\n')
self.current_target_number += 1
def display_data(self):
widget = FileDisplayWidget(self.file_name)
self.start_experience(widget)
from enum import Enum
from BubbleWidget import BubbleWidget
from RopeWidget import RopeWidget
from NormalWidget import NormalWidget
class Experience(Enum):
BUBBLE = 1
ROPE = 2
NORMAL = 3
def create_widget(self, file_path, exp_setup, number_of_targets):
if self.value == Experience.BUBBLE.value:
widget = BubbleWidget(file_path, exp_setup, number_of_targets)
elif self.value == Experience.ROPE.value:
widget = RopeWidget(file_path, exp_setup, number_of_targets)
elif self.value == Experience.NORMAL.value:
widget = NormalWidget(file_path, exp_setup, number_of_targets)
else:
print('Aucune expérience selectionnée')
widget = None
return widget
\ No newline at end of file
from PyQt5.QtWidgets import QTextEdit, QWidget, QApplication, QMainWindow
class FileDisplayWidget(QWidget):
def __init__(self, filename):
super().__init__()
self.filename = filename
self.textEdit = QTextEdit(self)
self.textEdit.setReadOnly(True)
with open(self.filename, 'r') as f:
self.textEdit.setPlainText(f.read())
def resizeEvent(self, event):
self.textEdit.setGeometry(0, 0, self.width(), self.height())
import random
import csv
SIZE_X_MAX = 1000
SIZE_Y_MAX = 800
DIR_NAME = 'generated_exp/'
SPACING = 5
all_density = (30, 60, 90)
all_size = (9, 12, 18)
max_density = max(all_density)
max_size = max(all_size)
def generate_targets(num_targets, max_target_size, min_spacing):
all_targets = []
for _ in range(num_targets):
x = random.randint(0, SIZE_X_MAX)
y = random.randint(0, SIZE_Y_MAX)
target = (x, y, max_target_size)
if not all_targets:
all_targets.append(target)
else:
# cf. Theoreme de Pythagore
# Prendre la distance avec le cercle et pas le centre
min_distance = min([(((x - t[0]) ** 2 + (y - t[1]) ** 2) ** 0.5) - max_target_size/2 for t in all_targets])
if min_distance >= min_spacing:
all_targets.append(target)
return all_targets
def save_to_csv(filename, all_targets, density, change_size):
with open(filename, "w", newline='\n') as f:
writer = csv.writer(f)
for target in all_targets[:density]:
as_list = list(target)
as_list[2] = change_size
writer.writerow(as_list)
def create_csv(filename, all_targets, density, change_size):
save_to_csv(filename, all_targets, density, change_size)
def create_all_files(min_spacing):
all_targets = generate_targets(max_density, max_size, min_spacing)
for density in all_density:
for size in all_size:
filename = "src_d_" + str(density) + '_s_' + str(size) + '.csv'
create_csv(DIR_NAME + filename, all_targets, density, size)
if __name__ == '__main__':
create_all_files(SPACING)
from PyQt5.QtWidgets import QApplication, QMainWindow
from experience.ExpSetup import ExpSetup
def main():
app = QApplication([])
window = QMainWindow()
exp_widget = ExpSetup(window)
window.setCentralWidget(exp_widget)
window.show()
app.exec_()
if __name__ == "__main__":
main()
1, 1, 1, 30, 9, BUBBLE, 6032.421827316284, 0
1, 1, 2, 30, 9, BUBBLE, 966.8619632720947, 0
1, 1, 3, 30, 9, BUBBLE, 1031.6030979156494, 0
1, 1, 4, 30, 9, BUBBLE, 964.9369716644287, 0
1, 1, 5, 30, 9, BUBBLE, 1550.239086151123, 0
1, 1, 6, 30, 9, BUBBLE, 683.8860511779785, 0
1, 1, 7, 30, 9, BUBBLE, 1350.4488468170166, 0
1, 1, 8, 30, 9, BUBBLE, 1297.9211807250977, 0
1, 1, 9, 30, 9, BUBBLE, 1032.7260494232178, 0
1, 1, 10, 30, 9, BUBBLE, 1032.9539775848389, 0
1, 1, 11, 30, 9, BUBBLE, 896.4080810546875, 0
1, 1, 12, 30, 9, BUBBLE, 969.0001010894775, 1
1, 1, 13, 30, 9, BUBBLE, 866.1530017852783, 0
1, 1, 14, 30, 9, BUBBLE, 614.9580478668213, 0
1, 1, 15, 30, 9, BUBBLE, 815.1013851165771, 0
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment