Browse Source

Cut out old code, bolt on k-means

pull/1/head
Taylor Bockman 5 years ago
parent
commit
adb1bfc0ce
  1. 26
      clusterview2.ui
  2. 9
      clusterview2/debug.py
  3. 5
      clusterview2/mode.py
  4. 2
      clusterview2/point_manager.py
  5. 20
      clusterview2/points.py
  6. 88
      clusterview2/ui/mode_handlers.py
  7. 1
      clusterview2/ui/opengl_widget.py
  8. 33
      clusterview2_ui.py
  9. 41
      main_window.py

26
clusterview2.ui

@ -103,12 +103,12 @@
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Centroids</string>
<string>Clusters</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="number_of_centroids">
<widget class="QSpinBox" name="number_of_clusters">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -130,42 +130,32 @@
</widget>
</item>
<item row="3" column="0">
<widget class="QPushButton" name="choose_centroids_button">
<widget class="QPushButton" name="unweighted_clustering_button">
<property name="enabled">
<bool>true</bool>
<bool>false</bool>
</property>
<property name="text">
<string>Choose Centroids</string>
<string>Unweighted Clustering</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QPushButton" name="unweighted_clustering_button">
<widget class="QPushButton" name="weighted_clustering_button">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Unweighted Clustering</string>
<string>Weighted Clustering</string>
</property>
</widget>
</item>
<item row="6" column="0">
<item row="5" column="0">
<widget class="QPushButton" name="reset_button">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QPushButton" name="weighted_clustering_button">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Weighted Clustering</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

9
clusterview2/debug.py

@ -0,0 +1,9 @@
def debug_trace():
"""
A wrapper for pdb that works with PyQt5.
"""
from PyQt5.QtCore import pyqtRemoveInputHook
from pdb import set_trace
pyqtRemoveInputHook()
set_trace()

5
clusterview2/mode.py

@ -13,6 +13,5 @@ class Mode(Enum):
MOVE = 3
DELETE = 4
LOADED = 5
CHOOSE_CENTROIDS = 6
UNWEIGHTED_CLUSTERING = 7
WEIGHTED_CLUSTERING = 8
UNWEIGHTED_CLUSTERING = 6
WEIGHTED_CLUSTERING = 7

2
clusterview2/point_manager.py

@ -11,7 +11,7 @@ class PointManager():
"""
point_set = None
centroids = []
clusters = []
@staticmethod
def load(location):

20
clusterview2/points.py

@ -14,7 +14,7 @@ class Point(BasePoint):
"""
def __init__(self, x, y, color, point_size,
viewport_width, viewport_height):
viewport_width, viewport_height, weight=1.0):
"""
Initializes a new point with a point_size bounding box, viewport
awareness, and a color.
@ -35,8 +35,14 @@ class Point(BasePoint):
"type Color.")
self._point_size = point_size
# Unfortunately, it appears decorated property methods are not
# inheirited and instead of redoing everything we will just repeat
# the properties here.
self._x = x
self._y = y
self._cluster = None
self._weight = weight
self._color = color
@ -60,6 +66,18 @@ class Point(BasePoint):
return self._y
@property
def weight(self):
return self._weight
@property
def cluster(self):
return self._cluster
@cluster.setter
def cluster(self, cluster):
self._cluster = cluster
@property
def point_size(self):
return self._point_size

88
clusterview2/ui/mode_handlers.py

@ -3,12 +3,14 @@ import random
from PyQt5.QtCore import QEvent, Qt
from PyQt5.QtGui import QCursor
from kmeans.algorithms import k_means
from clusterview2.colors import Color
from clusterview2.exceptions import ExceededWindowBoundsError
from clusterview2.mode import Mode
from .opengl_widget import (set_drawing_event, set_move_bb_top_left,
set_move_bb_bottom_right, reset_move_bbs,
viewport_height, viewport_width)
from clusterview2.ui.opengl_widget import (set_drawing_event, set_move_bb_top_left,
set_move_bb_bottom_right, reset_move_bbs,
viewport_height, viewport_width)
from clusterview2.point_manager import PointManager
@ -293,64 +295,9 @@ def _handle_info_updates(ctx, event):
ctx.mouse_position_label.setText(f"{event.x(), event.y()}")
def _handle_choose_centroids(ctx, event):
"""
Similar to move in terms of selecting points, however this
function assigns a random color up to the maximum number
of centroids, and after the maximum number has been selected it will
enable the group button.
"""
global _centroid_count
def reset_colors():
global _remaining_colors
_handle_info_updates(ctx, event)
if _centroid_count == ctx.number_of_centroids.value():
# We have specified the number of centroids required
return
if (event.button() == Qt.LeftButton and
event.type() == QEvent.MouseButtonPress):
point = None
for test_point in PointManager.point_set.points:
if test_point.hit(event.x(), event.y()):
point = test_point
if point is None:
# No point was found on the click, do nothing
return
if point in PointManager.centroids:
# Centroids must be unique
return
_centroid_count += 1
color = random.choice(_remaining_colors)
_remaining_colors.remove(color)
point.color = color
# Recolor the point and restash the point in centroids
PointManager.centroids.append(point)
if _centroid_count == ctx.number_of_centroids.value():
# Prevent the user from changing the centroids
ctx.number_of_centroids.setEnabled(False)
ctx.choose_centroids_button.setEnabled(False)
ctx.unweighted_clustering_button.setEnabled(True)
ctx.weighted_clustering_button.setEnabled(True)
ctx.opengl_widget.update()
def reset_centroid_count_and_colors():
global _centroid_count
global _remaining_colors
_centroid_count = 0
_remaining_colors = [c for c in Color if c not in [Color.BLUE, Color.GREY]]
for point in PointManager.point_set.points:
@ -389,6 +336,27 @@ def generate_random_points(point_count, x_bound, y_bound):
PointManager.point_set.add_point(point[0], point[1], Color.GREY)
def _handle_clustering(ctx, _):
points = list(PointManager.point_set.points)
if not ctx.clustering_solved:
clusters = k_means(points, ctx.number_of_clusters.value(), 0.001)
# We can leverage the paint function by first assigning every cluster
# a color (for completeness) and then assigning every point in that
# cluster the cluster's color.
for i, cluster in enumerate(clusters):
cluster.color = _remaining_colors[i]
for point in cluster.points:
point.color = cluster.color
PointManager.clusters = clusters
ctx.opengl_widget.update()
ctx.clustering_solved = True
# Simple dispatcher to make it easy to dispatch the right mode
# function when the OpenGL window is acted on.
MODE_HANDLER_MAP = {
@ -398,5 +366,5 @@ MODE_HANDLER_MAP = {
Mode.EDIT: _handle_edit_point,
Mode.MOVE: _handle_move_points,
Mode.DELETE: _handle_delete_point,
Mode.CHOOSE_CENTROIDS: _handle_choose_centroids
Mode.UNWEIGHTED_CLUSTERING: _handle_clustering
}

1
clusterview2/ui/opengl_widget.py

@ -183,7 +183,6 @@ def paint_gl():
if (__current_context.mode is Mode.ADD or
__current_context.mode is Mode.DELETE or
__current_context.mode is Mode.LOADED or
__current_context.mode is Mode.CHOOSE_CENTROIDS or
__current_context.mode is Mode.UNWEIGHTED_CLUSTERING or
__current_context.mode is Mode.WEIGHTED_CLUSTERING):

33
clusterview2_ui.py

@ -64,31 +64,27 @@ class Ui_MainWindow(object):
self.label_2 = QtWidgets.QLabel(self.groupBox_3)
self.label_2.setObjectName("label_2")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_2)
self.number_of_centroids = QtWidgets.QSpinBox(self.groupBox_3)
self.number_of_clusters = QtWidgets.QSpinBox(self.groupBox_3)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.number_of_centroids.sizePolicy().hasHeightForWidth())
self.number_of_centroids.setSizePolicy(sizePolicy)
self.number_of_centroids.setMinimumSize(QtCore.QSize(50, 26))
self.number_of_centroids.setMaximumSize(QtCore.QSize(50, 16777215))
self.number_of_centroids.setObjectName("number_of_centroids")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.number_of_centroids)
self.choose_centroids_button = QtWidgets.QPushButton(self.groupBox_3)
self.choose_centroids_button.setEnabled(True)
self.choose_centroids_button.setObjectName("choose_centroids_button")
self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.choose_centroids_button)
sizePolicy.setHeightForWidth(self.number_of_clusters.sizePolicy().hasHeightForWidth())
self.number_of_clusters.setSizePolicy(sizePolicy)
self.number_of_clusters.setMinimumSize(QtCore.QSize(50, 26))
self.number_of_clusters.setMaximumSize(QtCore.QSize(50, 16777215))
self.number_of_clusters.setObjectName("number_of_clusters")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.number_of_clusters)
self.unweighted_clustering_button = QtWidgets.QPushButton(self.groupBox_3)
self.unweighted_clustering_button.setEnabled(False)
self.unweighted_clustering_button.setObjectName("unweighted_clustering_button")
self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.unweighted_clustering_button)
self.reset_button = QtWidgets.QPushButton(self.groupBox_3)
self.reset_button.setObjectName("reset_button")
self.formLayout.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.reset_button)
self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.unweighted_clustering_button)
self.weighted_clustering_button = QtWidgets.QPushButton(self.groupBox_3)
self.weighted_clustering_button.setEnabled(False)
self.weighted_clustering_button.setObjectName("weighted_clustering_button")
self.formLayout.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.weighted_clustering_button)
self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.weighted_clustering_button)
self.reset_button = QtWidgets.QPushButton(self.groupBox_3)
self.reset_button.setObjectName("reset_button")
self.formLayout.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.reset_button)
self.verticalLayout.addWidget(self.groupBox_3)
spacerItem = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
self.verticalLayout.addItem(spacerItem)
@ -170,11 +166,10 @@ class Ui_MainWindow(object):
MainWindow.setWindowTitle(_translate("MainWindow", "ClusterView2"))
self.groupBox.setTitle(_translate("MainWindow", "Point List"))
self.groupBox_3.setTitle(_translate("MainWindow", "Solver"))
self.label_2.setText(_translate("MainWindow", "Centroids"))
self.choose_centroids_button.setText(_translate("MainWindow", "Choose Centroids"))
self.label_2.setText(_translate("MainWindow", "Clusters"))
self.unweighted_clustering_button.setText(_translate("MainWindow", "Unweighted Clustering"))
self.reset_button.setText(_translate("MainWindow", "Reset"))
self.weighted_clustering_button.setText(_translate("MainWindow", "Weighted Clustering"))
self.reset_button.setText(_translate("MainWindow", "Reset"))
self.groupBox_2.setTitle(_translate("MainWindow", "Canvas Information"))
self.label.setText(_translate("MainWindow", "Mouse Position:"))
self.menu_file.setTitle(_translate("MainWindow", "File"))

41
main_window.py

@ -10,7 +10,7 @@ from clusterview2.mode import Mode
from clusterview2.ui.mode_handlers import (MODE_HANDLER_MAP,
ogl_keypress_handler,
refresh_point_list,
reset_centroid_count_and_colors,
reset_colors,
generate_random_points)
from clusterview2.ui.opengl_widget import (clear_selection, initialize_gl,
mouse_leave, paint_gl, resize_gl,
@ -50,12 +50,12 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self._viewport_width,
self._viewport_height)
# Spin box should only allow the number of centroids to be no
# Spin box should only allow the number of clusters to be no
# greater than the number of supported colors minus 2 to exclude
# the color for selection (Color.BLUE) and the default color for points
# (Color.GREY).
self.number_of_centroids.setMinimum(0)
self.number_of_centroids.setMaximum(Color.count() - 2)
self.number_of_clusters.setMinimum(0)
self.number_of_clusters.setMaximum(Color.count() - 2)
# We only need to set the context in our OpenGL state machine
# wrapper once here since the window is fixed size.
@ -83,9 +83,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.point_list_widget.itemClicked.connect(partial(item_click_handler,
self))
self.choose_centroids_button.clicked.connect(self._choose_centroids)
self.unweighted_clustering_button.clicked.connect(self._unweighted_clustering)
self.number_of_clusters.valueChanged.connect(self._clustering_enabled)
self.reset_button.clicked.connect(self._reset)
@ -123,6 +122,9 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.opengl_widget.mouseMoveEvent = self._ogl_click_dispatcher
self.opengl_widget.mouseReleaseEvent = self._ogl_click_dispatcher
# Clustering flag so it does not continue to run
self.clustering_solved = False
# -----------------------------------------------------------------
# Mode changers - these will be used to signal the action in the
# OpenGL Widget.
@ -164,33 +166,22 @@ class MainWindow(QMainWindow, Ui_MainWindow):
clear_selection()
self.opengl_widget.update()
def _choose_centroids(self):
self._mode = Mode.CHOOSE_CENTROIDS
self.opengl_widget.setCursor(QCursor(Qt.CursorShape.CrossCursor))
self.status_bar.showMessage('CHOOSE CENTROIDS')
clear_selection()
self.opengl_widget.update()
def _unweighted_clustering(self):
clear_selection()
self._mode = Mode.UNWEIGHTED_CLUSTERING
self.opengl_widget.setCursor(QCursor(Qt.CursorShape.ArrowCursor))
self.status_bar.showMessage('UNWEIGHTED CLUSTERING')
clear_selection()
# unweighted_clustering(self)
self._off_mode()
self.opengl_widget.update()
def _reset(self):
self._off_mode()
self.number_of_centroids.setEnabled(True)
self.number_of_centroids.setValue(0)
self.choose_centroids_button.setEnabled(True)
self.number_of_clusters.setEnabled(True)
self.number_of_clusters.setValue(0)
self.unweighted_clustering_button.setEnabled(False)
self.weighted_clustering_button.setEnabled(False)
PointManager.centroids = []
reset_centroid_count_and_colors()
self.clustering_solved = False
PointManager.clusters = []
reset_colors()
def _generate_random_points(self):
value, ok = QInputDialog.getInt(self, 'Number of Points',
@ -208,6 +199,10 @@ class MainWindow(QMainWindow, Ui_MainWindow):
refresh_point_list(self)
def _clustering_enabled(self):
self.unweighted_clustering_button.setEnabled(self.number_of_clusters.value() > 0)
self.weighted_clustering_button.setEnabled(self.number_of_clusters.value() > 0)
@property
def mode(self):
""""

Loading…
Cancel
Save