├── .gitattributes ├── .gitignore ├── .gitmodules ├── .idea ├── Visualization.iml ├── deployment.xml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── misc.xml ├── modules.xml ├── remote-mappings.xml ├── vcs.xml └── webServers.xml ├── Conda_linux64_spec_file.txt ├── Conda_package_list.txt ├── Conda_win64_spec_file.txt ├── FullRequirements.txt ├── FullSetup.bat ├── FullSetup.sh ├── README.md ├── UpdateApplication.bat ├── UpdateApplication.sh ├── Vis_OrigColors.png ├── Visualizer ├── MouseInteraction.py ├── Scripts │ ├── GetData.bat │ └── Setup.bat ├── TaskList.txt ├── Vis.ico ├── Visualizer.py ├── Window.py ├── colors.py ├── data.py ├── events │ ├── __init__.py │ ├── event.py │ └── listener.py ├── exception_handler.py ├── frames │ ├── ListFrame.py │ ├── MatplotlibFrame.py │ ├── PMIPlot.py │ ├── QtMatplotlib.py │ ├── RelationTypeFrame.py │ ├── TimeSeriesFrame.py │ ├── TimeSeriesVisualizer.py │ ├── TopRelations.py │ ├── VisualizerFrame.py │ ├── VisualizerWidget.py │ ├── __init__.py │ ├── matplotlib_util.py │ └── preprocessor_controller.py ├── main.py ├── menu.py ├── menus │ ├── __init__.py │ └── pmi_menu.py ├── models │ ├── __init__.py │ └── list_model.py ├── pmi_corr_plot.png ├── qt_test.py ├── requirements.txt ├── setup.py ├── startup.py ├── templates │ └── template.tex ├── ui │ ├── GeneratePy.bat │ ├── GeneratePy.sh │ ├── __init__.py │ ├── main_window.py │ ├── main_window.ui │ ├── pmi_control_panel_horiz.py │ ├── pmi_control_panel_horiz.ui │ ├── pmi_control_panel_vert.py │ ├── pmi_control_panel_vert.ui │ ├── preprocessor_form.py │ ├── preprocessor_form.ui │ ├── preprocessor_run.py │ ├── preprocessor_run.ui │ ├── relation_types.py │ ├── relation_types.ui │ ├── relation_types_tabs.py │ ├── relation_types_tabs.ui │ ├── top_relations.py │ ├── top_relations.ui │ ├── topics_list.py │ ├── topics_list.ui │ ├── visualizer.py │ └── visualizer.ui └── widgets │ ├── ColoredBox.py │ ├── ListBoxColumn.py │ └── __init__.py └── examples ├── acl.jsonlist.gz ├── acl.jsonlist.short ├── acl.p ├── nips.jsonlist.gz └── nips_t.p /.gitattributes: -------------------------------------------------------------------------------- 1 | *.gz filter=lfs diff=lfs merge=lfs -text 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Nikko Rush 2 | # 6/16/2017 3 | # Created by https://www.gitignore.io/api/python 4 | 5 | ## Python 6 | # Byte-compiled / optimized / DLL files 7 | __pycache__/ 8 | *.py[cod] 9 | *$py.class 10 | 11 | # virtualenv 12 | .venv 13 | venv/ 14 | ENV/ 15 | 16 | ## Visual Studio 17 | # User-specific files 18 | *.suo 19 | *.user 20 | *.userosscache 21 | *.sln.docstates 22 | 23 | ## Custom 24 | 25 | output/ 26 | *.p 27 | 28 | ### PyCharm ### 29 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 30 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 31 | 32 | # User-specific stuff: 33 | .idea/**/workspace.xml 34 | .idea/**/tasks.xml 35 | .idea/dictionaries 36 | 37 | # Sensitive or high-churn files: 38 | .idea/**/dataSources/ 39 | .idea/**/dataSources.ids 40 | .idea/**/dataSources.xml 41 | .idea/**/dataSources.local.xml 42 | .idea/**/sqlDataSources.xml 43 | .idea/**/dynamic.xml 44 | .idea/**/uiDesigner.xml 45 | 46 | ## Custom files 47 | data/ 48 | *.whl 49 | Visualizer/acl_example/ 50 | idea_rel_dist/ 51 | *.log 52 | 53 | Application/ 54 | build/ 55 | 56 | *.tgz 57 | *.zip -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "idea_relations"] 2 | path = idea_relations 3 | url = git@github.com:nwrush/idea_relations.git 4 | branch = master 5 | -------------------------------------------------------------------------------- /.idea/Visualization.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 20 | 21 | 22 | 24 | -------------------------------------------------------------------------------- /.idea/deployment.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 17 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/remote-mappings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/webServers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /Conda_linux64_spec_file.txt: -------------------------------------------------------------------------------- 1 | # This file may be used to create an environment using: 2 | # $ conda create --name --file 3 | # platform: linux-64 4 | cycler=0.10.0=py35_0 5 | dbus=1.10.20=0 6 | expat=2.1.0=0 7 | fontconfig=2.12.1=3 8 | freetype=2.5.5=2 9 | glib=2.50.2=1 10 | gst-plugins-base=1.8.0=0 11 | gstreamer=1.8.0=0 12 | icu=54.1=0 13 | jpeg=9b=0 14 | libffi=3.2.1=1 15 | libgcc=5.2.0=0 16 | libgfortran=3.0.0=1 17 | libiconv=1.14=0 18 | libpng=1.6.27=0 19 | libxcb=1.12=1 20 | libxml2=2.9.4=0 21 | matplotlib=2.0.2=np113py35_0 22 | mkl=2017.0.3=0 23 | nltk=3.2.4=py35_0 24 | numpy=1.13.1=py35_0 25 | openssl=1.0.2l=0 26 | pandas=0.20.3=py35_0 27 | patsy=0.4.1=py35_0 28 | pcre=8.39=1 29 | pip=9.0.1=py35_1 30 | pyparsing=2.2.0=py35_0 31 | pyqt=5.6.0=py35_2 32 | python=3.5.4=0 33 | python-dateutil=2.6.1=py35_0 34 | pytz=2017.2=py35_0 35 | qt=5.6.2=5 36 | readline=6.2=2 37 | requests=2.14.2=py35_0 38 | scipy=0.19.1=np113py35_0 39 | seaborn=0.8=py35_0 40 | setuptools=27.2.0=py35_0 41 | sip=4.18=py35_0 42 | six=1.10.0=py35_0 43 | sqlite=3.13.0=0 44 | statsmodels=0.8.0=np113py35_0 45 | tk=8.5.18=0 46 | wheel=0.29.0=py35_0 47 | xz=5.2.2=1 48 | zlib=1.2.8=3 49 | -------------------------------------------------------------------------------- /Conda_package_list.txt: -------------------------------------------------------------------------------- 1 | matplotlib 2 | nltk 3 | numpy 4 | scipy 5 | pyqt # Version 5 6 | seaborn 7 | -------------------------------------------------------------------------------- /Conda_win64_spec_file.txt: -------------------------------------------------------------------------------- 1 | # This file may be used to create an environment using: 2 | # $ conda create --name --file 3 | # platform: win-64 4 | cycler=0.10.0=py36_0 5 | icu=57.1=vc14_0 6 | jpeg=9b=vc14_0 7 | libpng=1.6.27=vc14_0 8 | matplotlib=2.0.2=np113py36_0 9 | mkl=2017.0.3=0 10 | nltk=3.2.4=py36_0 11 | numpy=1.13.1=py36_0 12 | openssl=1.0.2l=vc14_0 13 | pandas=0.20.3=py36_0 14 | patsy=0.4.1=py36_0 15 | pip=9.0.1=py36_1 16 | pyparsing=2.2.0=py36_0 17 | pyqt=5.6.0=py36_2 18 | python=3.6.2=0 19 | python-dateutil=2.6.1=py36_0 20 | pytz=2017.2=py36_0 21 | qt=5.6.2=vc14_6 22 | requests=2.14.2=py36_0 23 | scipy=0.19.1=np113py36_0 24 | seaborn=0.8=py36_0 25 | setuptools=27.2.0=py36_1 26 | sip=4.18=py36_0 27 | six=1.10.0=py36_0 28 | statsmodels=0.8.0=np113py36_0 29 | tk=8.5.18=vc14_0 30 | vs2015_runtime=14.0.25420=0 31 | wheel=0.29.0=py36_0 32 | zlib=1.2.8=vc14_3 33 | -------------------------------------------------------------------------------- /FullRequirements.txt: -------------------------------------------------------------------------------- 1 | # Visualizaer 2 | cycler>=0.10.0 3 | matplotlib>=2.0.2 4 | numpy>=1.13.0 5 | pyparsing>=2.2.0 6 | python-dateutil>=2.6.0 7 | pytz>=2017.2 8 | six>=1.10.0 9 | PyQt5>=5.9 10 | 11 | # Idea Relations 12 | nltk>=3.2.1 13 | pandas>=0.18.1 14 | seaborn>=0.7.1 15 | -------------------------------------------------------------------------------- /FullSetup.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | setlocal 4 | 5 | Set PythonPath=PATH TO PYTHON HERE 6 | 7 | git submodule init 8 | git submodule update 9 | 10 | Set ROOT=%CD% 11 | 12 | Set AppPath=Application 13 | Set VisPath=Visualizer 14 | 15 | IF NOT EXIST %PythonPath% ( 16 | ECHO Python executable at %PythonPath% was not found 17 | EXIT /B 1 18 | ) 19 | 20 | :: This script should be executed from where you want to root the application 21 | 22 | mkdir "%AppPath%" 23 | 24 | :: Install the visualizer 25 | xcopy "%VisPath%\*.py" "%AppPath%\" 26 | xcopy "%VisPath%\frames\*.py" "%AppPath%\frames\" 27 | xcopy "%VisPath%\widgets\*.py" "%AppPath%\widgets\" 28 | xcopy "%VisPath%\menus\*.py" "%AppPath%\menus\" 29 | xcopy "%VisPath%\events\*.py" "%AppPath%\events\" 30 | xcopy "%VisPath%\ui\*.py" "%AppPath%\ui\" 31 | xcopy "%VisPath%\models\*.py" "%AppPath%\models\" 32 | 33 | :: Install the preprocessor 34 | mkdir "%AppPath%\idea_relations" 35 | 36 | xcopy "idea_relations\*.py" "%AppPath%\idea_relations\" 37 | xcopy "idea_relations\templates" "%AppPath%\idea_relations\templates\" 38 | xcopy "idea_relations\*.bat" "%AppPath%\idea_relations\" 39 | xcopy "idea_relations\*.sh" "%AppPath%\idea_relations\" 40 | 41 | 42 | :: Install the virtual environment 43 | copy FullRequirements.txt %AppPath%\FullRequirements.txt 44 | copy *.whl "%AppPath%\" 45 | 46 | endlocal & Set AppPath=%AppPath% & Set PythonPath=%PythonPath% 47 | cd %AppPath% 48 | 49 | virtualenv --python=%PythonPath% venv 50 | call .\venv\Scripts\activate.bat 51 | 52 | pip install "numpy-1.13.3+mkl-cp35-cp35m-win_amd64.whl" 53 | pip install "scipy-1.0.0-cp35-cp35m-win_amd64.whl" 54 | pip install -r FullRequirements.txt 55 | 56 | Set "AppPath=" 57 | Set "PythonPath=" 58 | -------------------------------------------------------------------------------- /FullSetup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PythonPath="$(which python3)" 4 | VenvPath="$(which virtualenv)" # Ideally the same one provided by the python installation you pointed to above 5 | 6 | git submodule init 7 | git submodule update 8 | 9 | ROOT=$(pwd) 10 | 11 | AppPath=Application 12 | VisPath=Visualizer 13 | 14 | # Check executables for existance 15 | if [[ ! -f "$PythonPath" ]] || [[ ! -x "$PythonPath" ]] 16 | then 17 | echo Couldn\'t locate python executable at $PythonPath 18 | exit 1 19 | else 20 | echo Using python executable at $PythonPath 21 | fi 22 | 23 | if [[ ! -f "$VenvPath" ]] || [[ ! -x "$VenvPath" ]] 24 | then 25 | echo Couldn\'t locate virtualenv at $VenvPath 26 | exit 1 27 | else 28 | echo Using virtualenv at $VenvPath 29 | fi 30 | 31 | mkdir $AppPath 32 | mkdir $AppPath/frames 33 | mkdir $AppPath/widgets 34 | mkdir $AppPath/menus 35 | mkdir $AppPath/events 36 | mkdir $AppPath/ui 37 | mkdir $AppPath/models 38 | 39 | 40 | cp $VisPath/*.py $AppPath 41 | cp $VisPath/frames/*.py $AppPath/frames/ 42 | cp $VisPath/widgets/*.py $AppPath/widgets/ 43 | cp $VisPath/menus/*.py $AppPath/menus/ 44 | cp $VisPath/events/*.py $AppPath/events/ 45 | cp $VisPath/ui/*.py $AppPath/ui 46 | cp $VisPath/models/*.py $AppPath/models 47 | 48 | mkdir $AppPath/idea_relations 49 | 50 | cp idea_relations/*.py $AppPath/idea_relations/ 51 | cp -r idea_relations/templates $AppPath/idea_relations/templates/ 52 | cp idea_relations/*.bat $AppPath/idea_relations/ 53 | cp idea_relations/*.sh $AppPath/idea_relations/ 54 | 55 | cp FullRequirements.txt $AppPath/FullRequirements.txt 56 | 57 | cd $AppPath 58 | 59 | $VenvPath --python=$PythonPath venv 60 | source venv/bin/activate 61 | pip install --upgrade pip 62 | 63 | pip install -r FullRequirements.txt 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Visualization 2 | 3 | This project is still in active development, but is mostly stable. Various features are subject to appear or disappear with little/no notice. 4 | 5 | ## Current Release 6 | 7 | v1.0 - [Link](https://github.com/nwrush/Visualization/releases) 8 | 9 | ## About 10 | 11 | This project builds on the work done by Chenhao Tan and Dallas Card in [this paper](https://chenhaot.com/pages/idea-relations.html). This tool provides a way to visualize text interactively using the relations outlined in the paper. 12 | 13 | # Installation 14 | Installing has been tested on Windows (7 and higher) (64-bit), Linux (Centos7), and some version of OSX. For convenience install scripts are provided for Windows and Linux. 15 | Binaries are also provided for Windows and Linux. 16 | 17 | After running any of the installation methods, if you've never used NLTK you will need to download the NLTK stopword corpus. Activate the virtualenv for the application and run `python -m nltk.downloader stopwords` 18 | 19 | ### Requirements 20 | - Python3 (only tested on 3.5 and higher) 21 | - A display 22 | - Running over a x-server is possibly but does not look good 23 | 24 | ## Installing from the repo 25 | 26 | ### Linux Installation 27 | 1. Download or clone the repo 28 | 2. Change the value of the variables $PYTHONPATH and $VenvPath to point to your system python installation and virtualenv 29 | 3. Run `./FullSetup.sh` 30 | - This will create the application in a new folder called Application 31 | 4. Activate the virtualenv (Application/venv) 32 | 5. Run the visualizer with: `python Visualizer.py` 33 | 34 | ### Windows Installation 35 | 1. Download or clone the repo 36 | 2. Change the value of the variable PythonPath to point to your system python installation 37 | 3. Run `FullSetup.bat` 38 | - This will create the application in a new folder called Application 39 | 4. Activate the virtualenv 40 | - If you called FullSetup.bat from the command line, the virtualenv may already be active 41 | 5. Run the visualizer with: `python Visualizer.py` 42 | 43 | ### Manual Installation 44 | 1. Download or clone the repo 45 | 2. Run: `git submodule init` and then `git submodule update` 46 | 3. Create a new folder where you want to install the application 47 | 4. From the folder Visualizer, copy all .py files and all folders to your application folder 48 | 6. Copy the top level idea_relations folder into the application folder 49 | 7. Create a virtual environment containing the packages listed in the FullRequirements.txt file 50 | 8. Activate the virtual environment and run the visualizer with `python Visualizer.py` 51 | 52 | ### Anaconda Installation 53 | 1. Follow the Manual Installation instructions up to step 6 54 | 2. Instead of creating a python virtualenv, you can create an anaconda environment containing the packages in Conda_package_list.txt 55 | - Linux and Windows users can try to use the appropriate spec file 56 | 3. Run the program the same as the manual installation instructions 57 | 58 | ## Packaged Installation 59 | 60 | The application is distributed in three forms. A standalone Windows binary, standalone Linux binary, and the plain python files. 61 | Both the Windows and Linux binary should run without any issue, just double click on the executable (Visualizer.exe) to run. 62 | The Linux binary should function on any Linux system (including OSX), but has only been tested on Centos 7. 63 | 64 | For the python files, you must have python version >3.5 available on your system. Installation is similar to the manual installation. Unpack the zip file and follow the manual installation instructions above from step 7. 65 | 66 | Because of the much larger size of the Windows and Linux binaries, it is probably better to use the python version, especially if you already have Python 3 on your system. 67 | 68 | As of right now, the prepacked installations cannot run the preprocessor due to bugs with scipy. A fix will potentially be available late December. Using a virtualenv with the raw python files works fine though. 69 | 70 | ## Note for Windows installation 71 | 72 | On Windows, it may be difficult/impossible to install scipy using pip. To get around that we recommend downloading a prebuilt wheel for numpy and scipy from [this](https://www.lfd.uci.edu/~gohlke/pythonlibs/) website. You will need wheels for both numpy and scipy. Download the appropriate wheel for your system (matching python version and architecture). 73 | If you're installing the program from the repo using the installation scripts, place them in the root directory for the repo, and edit lines 52 and 53 of FullSetup.py to have the correct file names. If you're installing from the zipped python files, copy them wherever you unpacked the code and install them using pip into your virtualenv. You must install them 74 | before you install packages from FullRequirements.txt otherwise pip will install numpy from the web and then be unable to install scipy. 75 | If you're installing from the standalone Windows binary, you don't need to do anything. 76 | 77 | # Usage 78 | 79 | The preprocessor can be run in two modes, Keywords and Topics. In keywords, the system will take two files a "input" file and a "background" file. The system calculates the prevelance of each word in both files, and creates the topic list from the top n words/phrases in the input file that aren't in the background file. While not necessary, the background file should be in the same domain as the input file for more accurate results. 80 | In Topic mode the system will run LDA on the input file using Mallet to "learn" the top 50 topics on its own. This doesn't require a background file, but does take significantly longer (~30 minutes on the demo dataset). 81 | Further details for how the topics are selected is available in [this paper](https://chenhaot.com/pages/idea-relations.html). 82 | 83 | After running the preprocessor to get the relationships a new tab will open with the plot of the relationships between topics. Each dot on the graph represents a single topic/topic pair, if there are more than 1000 pairs then 1000 items will be selected randomly to populate the graph. Selecting an item will display the frequency time series for each relation as well as populating the 'Top Relation' filters. 84 | 85 | On the left side of the window is the filter tab area, here you can filter down the visible topics. Selecting items in the ideas tab causes all relationship involving the selected ideas to populate the graph. In the types tab, relations are sorted by their type and strength, selecting a relationship here causes all relations involving either topic to appear on the graph. The other two tabs here 'Top Relation 1' and 'Top Relation 2' simply display a sorted list of the strength of all relations each selected topic has. 86 | 87 | You can save visualizations for later use from the File menu, this saves you from having to wait for the preprocessor to run again. Additionally, you can load multiple visualizations into multiple tabs. In the visualization menu you can save images of the current PMI/correlation plot and the time series plot, the images saved are exactly what appears on the screen when you hit save. 88 | 89 | ## Input file format 90 | 91 | Input files should be given as jsonlists, one document per line. The only two fields needed are date and text. 92 | The date will be parsed using the [python dateutil library](http://dateutil.readthedocs.io/en/stable/parser.html#dateutil.parser.parse) with a default date of datetime(1,1,1) and all other arguemnts at their defaults. It will be converted to a string first as well. The text should be just the full text of the document properly quoted. For examples see acl.jsonlist in the included [example data folder](./examples). 93 | The datafile can be compressed with gz before being passed in. 94 | 95 | Two example visualizations are provided for you, acl.p and nips_t.p. The first one is the acl dataset in acl.jsonlist.gz processed using keywords with the nips dataset as the background file, the second one is the nips dataset processed using topics. 96 | 97 | ## Preprocessor options 98 | 99 | You must pass the preprocessor 3 options, exploration name which determines what the visualization will be called (mainly setting the tab title), the number of ideas (which tells the preprocessor how many ideas to look for), and time grouping (year, month, day) to set the time scale the preprocessor uses when grouping articles. There are also 3 optional options, Tokenize, Lemmatize, and No Stop Words. These will perform the relevant preprocessing to the data before searching for topics. Additionally, you can use the advanced options to force the preprocessor to save it's intermediate output data somewhere else if you wanted. The intermediate data isn't terrible useful/readable though. 100 | 101 | ![Visualizer Screenshot](./Vis_OrigColors.png) 102 | -------------------------------------------------------------------------------- /UpdateApplication.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | git submodule update 4 | 5 | Set Loc=%CD% 6 | 7 | setlocal 8 | 9 | Set ROOT=%~dp0 10 | echo %ROOT% 11 | cd %ROOT% 12 | 13 | Set AppPath=Application 14 | Set VisPath=Visualizer 15 | 16 | :: This script re-copies all python files to the application folder 17 | 18 | :: Update the visualizer 19 | xcopy /Y "%VisPath%\*.py" "%AppPath%\" 20 | xcopy /Y "%VisPath%\frames\*.py" "%AppPath%\frames\" 21 | xcopy /Y "%VisPath%\widgets\*.py" "%AppPath%\widgets\" 22 | xcopy /Y "%VisPath%\menus\*.py" "%AppPath%\menus\" 23 | xcopy /Y "%VisPath%\events\*.py" "%AppPath%\events\" 24 | xcopy /Y "%VisPath%\ui\*.py" "%AppPath%\ui\" 25 | xcopy /Y "%VisPath%\models\*.py" "%AppPath%\models\" 26 | 27 | :: Update the preprocessor 28 | 29 | xcopy /Y "idea_relations\*.py" "%AppPath%\idea_relations\" 30 | xcopy /Y "idea_relations\templates" "%AppPath%\idea_relations\templates\" 31 | xcopy /Y "idea_relations\*.bat" "%AppPath%\idea_relations\" 32 | xcopy /Y "idea_relations\*.sh" "%AppPath%\idea_relations\" 33 | 34 | endlocal 35 | 36 | cd %Loc% 37 | 38 | set "Loc=" 39 | -------------------------------------------------------------------------------- /UpdateApplication.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | git submodule update 4 | 5 | ROOT=$(pwd) 6 | 7 | AppPath=Application 8 | VisPath=Visualizer 9 | 10 | # Update Visualizer 11 | cp -u $VisPath/*.py $AppPath 12 | cp -u $VisPath/frames/*.py $AppPath/frames/ 13 | cp -u $VisPath/widgets/*.py $AppPath/widgets/ 14 | cp -u $VisPath/menus/*.py $AppPath/menus/ 15 | cp -u $VisPath/events/*.py $AppPath/events/ 16 | cp -u $VisPath/ui/*.py $AppPath/ui 17 | cp -u $VisPath/models/*.py $AppPath/models 18 | 19 | # Update Idea Relations 20 | cp -u idea_relations/*.py $AppPath/idea_relations/ 21 | cp -u -r idea_relations/templates $AppPath/idea_relations/templates/ 22 | cp -u idea_relations/*.bat $AppPath/idea_relations/ 23 | cp -u idea_relations/*.sh $AppPath/idea_relations/ 24 | -------------------------------------------------------------------------------- /Vis_OrigColors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nwrush/Visualization/bc134aa02156956ea3644b34d491da9f0e9f63ae/Vis_OrigColors.png -------------------------------------------------------------------------------- /Visualizer/MouseInteraction.py: -------------------------------------------------------------------------------- 1 | class MouseInteract: 2 | def __init__(self, canvas, plot, select_callback=None): 3 | self.canvas = canvas 4 | self.plot = plot 5 | 6 | self.connect() 7 | 8 | self.press = None 9 | self.prev_selected_ind = None 10 | 11 | self._select_callback = select_callback 12 | 13 | def connect(self): 14 | self.canvas.mpl_connect('button_press_event', self.on_click) 15 | self.canvas.mpl_connect('button_release_event', self.on_release) 16 | self.canvas.mpl_connect('motion_notify_event', self.on_motion) 17 | self.canvas.mpl_connect('pick_event', self.on_select) 18 | 19 | def on_click(self, event): 20 | print("ouch") 21 | self.press = event.xdata, event.ydata 22 | 23 | def on_release(self, event): 24 | pass 25 | 26 | def on_motion(self, event): 27 | if self.press is None: 28 | return 29 | 30 | def on_select(self, event): 31 | print("Selected something") 32 | ind = event.ind[0] 33 | 34 | if self.prev_selected_ind is not None: 35 | self.plot._facecolors[self.prev_selected_ind] = (0, 0, 1, 1) 36 | self.plot._edgecolors[self.prev_selected_ind] = (0, 0, 1, 1) 37 | 38 | self.plot._facecolors[ind] = (1,0,0,1) 39 | self.plot._edgecolors[ind] = (1,0,0,1) 40 | 41 | if self._select_callback is not None: 42 | self._select_callback(event) 43 | 44 | self.canvas.draw() 45 | 46 | self.prev_selected_ind = ind 47 | -------------------------------------------------------------------------------- /Visualizer/Scripts/GetData.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | if not exist "..\processed_data" mkdir ..\processed_data 4 | 5 | pscp -P 1255 nikko@zin.cs.washington.edu:/home/nikko/visualization/Visualization/idea_relations/*.p ..\processed_data\. -------------------------------------------------------------------------------- /Visualizer/Scripts/Setup.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | cd .. 4 | 5 | virtualenv --python=C:\Users\Nikko\AppData\Local\Programs\Python\Python35\python.exe venv 6 | call .\venv\Scripts\activate.bat 7 | 8 | pip install -r requirements.txt -------------------------------------------------------------------------------- /Visualizer/TaskList.txt: -------------------------------------------------------------------------------- 1 | --Priority 2 | 3 | --TODO 4 | 7/2/17: Test multiple screen sizes 5 | 7/5/17: Don't assume with the datestamps and step sizes 6 | 7/6/17: Run in a profiler to see what's taking time on start 7 | 7/19/17: Make the settings for the file/directory dialog boxes more reasonable 8 | 7/28/17: Improve event handling system 9 | 8/10/17: Updated README with note about development status, install instructions, example data files 10 | 8/10/17: Document required file format 11 | 8/29/17: Provide way of filtering or sorting the top relation lists 12 | 13 | --Bugfix 14 | 7/28/17: Selecting a single relation and then a pair that contains the single causes strange behavior (behavior does not persist when rerun, could be VS lag bug) 15 | 16 | --Wishlist 17 | 7/5/17: More logical way to decide which element was picked in case of conflict 18 | 7/5/17: Drag box selection of points with zooming? 19 | 7/5/17: Parameter object that can be passed to frames to specify format (visual styles)| 7/17/17 Exists on LayoutConfiguration branch but is rather messy 20 | 8/8/17: Interactive date range (idea_relations.idea_relations:70) has arguments 21 | 8/10/17: Custom word list 22 | 8/10/17: Graph based layout on PMI or KL divergence between topics 23 | 24 | 25 | --Done (please mention the commit name here too) 26 | 7/2/17: Refactor code to make it easier to work with | 7/5/17 (abf2d55) 27 | 7/2/17: MouseInteraction should know what color to change points too | 7/5/17 (abf2d55) MouseInteraction is obsolete 28 | 7/2/17: Fix the correlation text overwriting when you select a new point (part of refactoring code into classes) | 7/5/17 (f855889) Sure, that "works" 29 | 7/7/17: Custom Multicolumn listboxes | 7/9/17 (dadf23b) 30 | 7/7/17: Refactor TimeSeriesFrame so it controls the graphing of plots instead of the PMI graph | 7/10/17 (329cfc4) 31 | 7/2/17: Layout all six frames in the tk full screen widget | 7/11/17 (4fbcc56) 32 | 7/10/17: Git commit hashes in the tasklist don't match up | 7/11/17 (3f36d95) 33 | 7/7/17: Verify correlation coefficients | 7/11/17 (NOCOMMIT) 34 | 7/13/17: Button names aren't correct, (tryst and friends are flipped) | 7/14/17 (f37289f) 35 | 7/8/17: Synchronize the scrolling of ListBoxColumn lists | 7/14/17 (4931ce0) 36 | 7/2/17: Find a better way to layout the time series plot correlation number | 7/17/17 (8bc42d5) TimeSeries control panel still could use work 37 | 7/19/17: Don't freeze the UI when you run the subprocess | 7/20/17 (2a3afb8) 38 | 7/7/17: Allow dynamically set (user defined) colors for plots | 7/27/17 (0389392) 39 | 7/27/17: Loading a data file when one is already loaded fails when trying to create new fonts | 7/28/17 (ac4bc09) 40 | 7/27/17: List highlighting | 7/28/17 (788591f) 41 | 7/27/17: Clear time series and and selected relations when new point is selected | 7/28/17 (788591f) 42 | 7/28/17: Move GUI root code into class | 8/7/17 (74645b5) 43 | 7/27/17: Column labels for the listboxes | 8/7/17 (74645b5) 44 | 7/2/17: Better looking frames | 8/7/17 (74645b5) 45 | 7/27/17: Mac scroll wheel bug | 8/7/17 (74645b5) Assumed fixed 46 | 7/27/17: Grid resizing | Obsolete with (d4409f1) 47 | 8/10/17: Clean up the preprocessor form to avoid showing extraneous fields | 8/10/17 (c32581e) 48 | 8/10/17: Validate the preprocessor form | 8/10/17 (c32581e) 49 | 8/10/17: Progress bar show incremental status | 8/11/17 (a4e7d98) 50 | 8/10/17: Anaconda support | 8/18/17 (fa473ec) 51 | 8/10/17: Save images of graphs to file | 8/18/17 (d4c1e58) -------------------------------------------------------------------------------- /Visualizer/Vis.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nwrush/Visualization/bc134aa02156956ea3644b34d491da9f0e9f63ae/Visualizer/Vis.ico -------------------------------------------------------------------------------- /Visualizer/Visualizer.py: -------------------------------------------------------------------------------- 1 | # Nikko Rush 2 | # 6/21/2017 3 | 4 | # region Imports 5 | import logging 6 | import platform 7 | import sys 8 | 9 | FORMAT = "%(asctime)s %(levelname)8s:[%(filename)s:%(lineno)s - %(funcName)20s()] %(message)s" 10 | logging.basicConfig(level=logging.DEBUG, 11 | format=FORMAT, 12 | datefmt='%m-%d %H:%M:%S', 13 | filename='./Visualizer.log', 14 | filemode='w') 15 | 16 | import PyQt5.QtGui as QtGui 17 | import PyQt5.QtWidgets as QtWidgets 18 | import matplotlib 19 | matplotlib.use("Qt5Agg") 20 | 21 | import exception_handler 22 | import data 23 | from frames.preprocessor_controller import PreprocessorController 24 | from frames.VisualizerWidget import VisualizerWidget 25 | from ui import main_window 26 | # endregion 27 | 28 | logging.info("Application Startup") 29 | logging.info(platform.uname()) 30 | logging.info(platform.platform()) 31 | logging.info(platform.python_version()) 32 | 33 | version = platform.win32_ver() 34 | if not version[0] == '': 35 | logging.info(version) 36 | 37 | version = platform.mac_ver() 38 | if not version[0] == '': 39 | logging.info(version) 40 | 41 | """ 42 | GUI creation 43 | """ 44 | 45 | 46 | class Application(QtWidgets.QMainWindow): 47 | 48 | def __init__(self, data_manager=None): 49 | super(Application, self).__init__() 50 | 51 | self.ui = main_window.Ui_mainWindow() 52 | self.ui.setupUi(self) 53 | 54 | self.ui.tabWidget.tabCloseRequested.connect(self._close_tab) 55 | 56 | self.main_widget = self.ui.main_widget 57 | self.main_widget.setFocus() 58 | self.setCentralWidget(self.main_widget) 59 | 60 | self._tabs = [] 61 | 62 | self._init_menu() 63 | 64 | self._preprocess_widget = None 65 | self._load_preprocessor() 66 | 67 | self._tabs.append(self._preprocess_widget) 68 | 69 | if data_manager is not None: 70 | self._add_tab(data_manager, 1) 71 | 72 | def _load_preprocessor(self): 73 | self._preprocess_widget = PreprocessorController(self.main_widget, self._preprocessor_callback) 74 | self.ui.tabWidget.insertTab(0, self._preprocess_widget, "Preprocessor") 75 | 76 | def _load_visualizer(self): 77 | self._visualizer_widget = VisualizerWidget(self.main_widget, self._data_manager) 78 | self.ui.tabWidget.insertTab(1, self._visualizer_widget, self._data_manager.name) 79 | 80 | def _init_menu(self): 81 | # File Menu 82 | self.ui.actionOpen.triggered.connect(self._open_visualization) 83 | self.ui.actionSave.triggered.connect(self._save_visualization) 84 | 85 | # Visualization Menu 86 | self.ui.tabWidget.currentChanged.connect(self._tab_changed) 87 | self.ui.actionSave_PMI.triggered.connect(self._save_pmi) 88 | self.ui.actionSave_TS.triggered.connect(self._save_ts) 89 | self.ui.actionSave_Both.triggered.connect(self._save_both) 90 | 91 | def _add_tab(self, tab_data, index): 92 | widget = VisualizerWidget(self.main_widget, tab_data) 93 | self.ui.tabWidget.insertTab(index, widget, tab_data.name) 94 | self._tabs = self._tabs[:index] + [widget] + self._tabs[index:] 95 | self.ui.tabWidget.setCurrentIndex(index) 96 | 97 | def _processed_file(self, fname): 98 | data_manager = data.load_data(fname) 99 | if data_manager is not None: 100 | self._add_tab(data_manager, len(self._tabs)) 101 | 102 | def _preprocessor_callback(self, return_code): 103 | if return_code == 0: 104 | self._processed_file(self._preprocess_widget.output_name) 105 | 106 | def _open_visualization(self): 107 | dialog = QtWidgets.QFileDialog(self) 108 | dialog.setFileMode(QtWidgets.QFileDialog.ExistingFiles) 109 | dialog.setNameFilters(["Processed data file (*.p)", "All Files (*)"]) 110 | dialog.exec() 111 | 112 | fnames = dialog.selectedFiles() 113 | if fnames: 114 | for fname in fnames: 115 | self._processed_file(fname) 116 | 117 | def _save_visualization(self): 118 | current_tab = self._tabs[self.ui.tabWidget.currentIndex()] 119 | if isinstance(current_tab, PreprocessorController): 120 | return 121 | 122 | dialog = QtWidgets.QFileDialog(self) 123 | dialog.setFileMode(QtWidgets.QFileDialog.AnyFile) 124 | dialog.setNameFilters(["Processed data file (*.p)", "All Files (*)"]) 125 | dialog.setAcceptMode(QtWidgets.QFileDialog.AcceptSave) 126 | dialog.setDefaultSuffix(".p") 127 | dialog.exec() 128 | 129 | fname = dialog.selectedFiles()[0] 130 | success = current_tab.save_data(fname) 131 | if not success: 132 | QtWidgets.QMessageBox.about(self, "Save Error", 133 | "An error occured saving the file\nCheck the log for more details") 134 | 135 | def _tab_changed(self, index): 136 | if index >= len(self._tabs): 137 | self.ui.visualizationMenu.setEnabled(False) 138 | else: 139 | self.ui.visualizationMenu.setEnabled(isinstance(self._tabs[index], VisualizerWidget)) 140 | 141 | def _save_pmi(self): 142 | self.ui.tabWidget.currentWidget().save_pmi() 143 | 144 | def _save_ts(self): 145 | self.ui.tabWidget.currentWidget().save_ts() 146 | 147 | def _save_both(self): 148 | self.ui.tabWidget.currentWidget().save_both() 149 | 150 | def _close_tab(self, index): 151 | if isinstance(self._tabs[index], PreprocessorController): 152 | return 153 | self.ui.tabWidget.removeTab(index) 154 | del self._tabs[index] 155 | 156 | def kill_preprocessor(self): 157 | self._preprocess_widget.kill() 158 | 159 | 160 | def is_square_matrix(a): 161 | return a.shape[0] == a.shape[1] 162 | 163 | 164 | def main(fname=None): 165 | data_manager = None 166 | if fname is not None: 167 | data_manager = data.load_data(fname) 168 | 169 | qApp = QtWidgets.QApplication(sys.argv) 170 | logging.info(qApp.primaryScreen().physicalSize()) 171 | window = Application(data_manager) 172 | window.show() 173 | qApp.exec() 174 | logging.info("Goodbye") 175 | 176 | 177 | if __name__ == "__main__": 178 | main() 179 | -------------------------------------------------------------------------------- /Visualizer/Window.py: -------------------------------------------------------------------------------- 1 | # Nikko Rush 2 | # 7/2/2017 3 | # The Tk window root 4 | 5 | import tkinter as tk 6 | from tkinter import ttk 7 | 8 | class Window(object): 9 | """description of class""" 10 | 11 | def __init__(self): 12 | self.root = tk.Tk() 13 | 14 | self.root.protocol("WM_DELETE_WINDOW", self.__close_window_callback) 15 | 16 | def start(self): 17 | self.root.mainloop() 18 | 19 | def __close_window_callback(self): 20 | self.root.quit() 21 | self._close() 22 | 23 | def _close(self): 24 | """Called after quiting tk""" 25 | pass 26 | 27 | 28 | if __name__ == "__main__": 29 | window = Window() 30 | window.start() -------------------------------------------------------------------------------- /Visualizer/colors.py: -------------------------------------------------------------------------------- 1 | # Nikko Rush 2 | # 7/25/2017 3 | 4 | import matplotlib.cm as cm 5 | import matplotlib.colors 6 | 7 | 8 | class PMIColormap(matplotlib.colors.Colormap): 9 | 10 | def __init__(self, name, color, N=256): 11 | super(PMIColormap, self).__init__(name=name, N=N) 12 | 13 | self._color_map = None 14 | self._base_color = None 15 | self._saturation_max = None 16 | 17 | if isinstance(color, matplotlib.colors.Colormap): 18 | self._color_map = color 19 | if isinstance(color, str) and color in cm.cmap_d: 20 | self._color_map = cm.get_cmap(color) 21 | else: 22 | self._base_color = color 23 | self._saturation_max = matplotlib.colors.rgb_to_hsv(color)[1] 24 | 25 | def _resample(self, lutsize): 26 | super(PMIColormap, self)._resample(lutsize) 27 | 28 | def _init(self): 29 | super(PMIColormap, self)._init() 30 | 31 | def __call__(self, dist, *args, **kwargs): 32 | if self._color_map is not None: 33 | return self._color_map.__call__(dist, *args, **kwargs) 34 | 35 | hsv_value = matplotlib.colors.rgb_to_hsv(self._base_color) 36 | 37 | hsv_value[1] = self._saturation_max * dist 38 | 39 | rgb = matplotlib.colors.hsv_to_rgb(hsv_value) 40 | r, g, b = tuple(rgb) 41 | r = int(r) 42 | g = int(g) 43 | b = int(b) 44 | assert 0 <= r <= 255 45 | assert 0 <= g <= 255 46 | assert 0 <= b <= 255 47 | if kwargs['bytes']: 48 | return r, g, b, 255.0 49 | else: 50 | return r / 255, g / 255, b / 255, 1.0 51 | -------------------------------------------------------------------------------- /Visualizer/data.py: -------------------------------------------------------------------------------- 1 | # Nikko Rush 2 | # 7/10/2017 3 | 4 | # Last update 9/4/17 5 | 6 | import logging 7 | import os 8 | import os.path 9 | import pickle 10 | 11 | import numpy as np 12 | 13 | 14 | def reverse_dict(input_dict): 15 | output = dict() 16 | for key, value in input_dict.items(): 17 | if value in output: 18 | logging.warn("Warning: Duplicate key will be overwritten in new dictionary") 19 | output[value] = key 20 | return output 21 | 22 | 23 | def is_square(a): 24 | row, col = a.shape 25 | return row == col 26 | 27 | 28 | def load_data(fname): 29 | if not os.path.isfile(fname): 30 | logging.info("Data file {0} doesn't exist".format(fname)) 31 | return None 32 | 33 | return pickle.load(open(fname, 'rb')) 34 | 35 | 36 | def save_data(fname, data): 37 | try: 38 | f = open(fname, 'wb') 39 | pickle.dump(data, f) 40 | f.flush() 41 | f.close() 42 | except Exception as ex: 43 | logging.error("Failure while saving data") 44 | logging.exception(ex) 45 | return False 46 | return True 47 | 48 | 49 | class Data: 50 | def __init__(self, args, pmi_matrix, ts_correlation_matrix, ts_matrix, idea_names, x_vals, 51 | name="Visualization"): 52 | 53 | self._args = args 54 | self._is_keywords = args.option.upper() == "KEYWORDS" 55 | self.pmi = pmi_matrix 56 | self.ts_correlation = ts_correlation_matrix 57 | self.ts_matrix = ts_matrix 58 | self.idea_names = idea_names # Goes from indexes to text 59 | self.x_values = x_vals 60 | 61 | self.idea_numbers = reverse_dict(self.idea_names) # Goes from text to indexes 62 | self.num_ideas = len(self.idea_names) 63 | 64 | self.strength_matrix = self._get_strength_matrix() 65 | 66 | self.relation_types = ("Friends", "Tryst", "Head-To-Head", "Arms-Race") # Layout the relation in quadrant order 67 | 68 | self.name = name if name is not None else "Visualization" 69 | 70 | def _get_strength_matrix(self): 71 | assert self.pmi.shape == self.ts_correlation.shape 72 | assert is_square(self.pmi) 73 | 74 | a = np.multiply(self.pmi, self.ts_correlation) 75 | 76 | lower_indexes = np.tril_indices(self.pmi.shape[0]) 77 | a[lower_indexes] = np.nan 78 | return a 79 | 80 | def get_idea_names(self, indexes): 81 | if isinstance(indexes, int): 82 | return self.idea_names[indexes] 83 | 84 | try: 85 | names = list() 86 | for index in indexes: 87 | names.append(self.idea_names[index]) 88 | return names 89 | except TypeError as err: 90 | logging.error(err) 91 | 92 | return None 93 | 94 | TOPIC_DISPLAY_WIDTH = 3 95 | 96 | def get_display_idea_names(self, indexes): 97 | if self._is_keywords: 98 | return self.get_idea_names(indexes) 99 | 100 | if isinstance(indexes, int): 101 | full_name = self.idea_names[indexes] 102 | return ','.join(full_name.split(',')[:self.TOPIC_DISPLAY_WIDTH]) + '...' 103 | 104 | try: 105 | names = list() 106 | first = True 107 | for index in indexes: 108 | full_name = self.idea_names[index] 109 | short_name = ','.join(full_name.split(',')[:self.TOPIC_DISPLAY_WIDTH]) + '...' 110 | names.append(short_name) 111 | return names 112 | except TypeError as err: 113 | logging.error(err) 114 | return None 115 | -------------------------------------------------------------------------------- /Visualizer/events/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nwrush/Visualization/bc134aa02156956ea3644b34d491da9f0e9f63ae/Visualizer/events/__init__.py -------------------------------------------------------------------------------- /Visualizer/events/event.py: -------------------------------------------------------------------------------- 1 | # Nikko Rush 2 | # 7/28/2017 3 | 4 | 5 | class Event(object): 6 | """description of class""" 7 | 8 | def __init__(self): 9 | pass 10 | -------------------------------------------------------------------------------- /Visualizer/events/listener.py: -------------------------------------------------------------------------------- 1 | # Nikko Rush 2 | # 7/28/2017 3 | 4 | 5 | class Listener(object): 6 | def __init__(self, *handlers): 7 | self._handlers = set(handlers) 8 | 9 | def add_handler(self, func): 10 | self._handlers.add(func) 11 | 12 | def has_handler(self, func): 13 | return func in self._handlers 14 | 15 | def remove_handler(self, func): 16 | self._handlers.discard(func) 17 | 18 | add = add_handler 19 | remove = remove_handler 20 | 21 | def invoke_empty(self): 22 | for func in self._handlers: 23 | func() 24 | 25 | def invoke(self, event): 26 | for func in self._handlers: 27 | code = func.__code__ 28 | if code.co_argcount == 0: 29 | func() 30 | elif code.co_argcount == 1 and code.co_varnames == ('self',): 31 | func() 32 | else: 33 | func(event) 34 | -------------------------------------------------------------------------------- /Visualizer/exception_handler.py: -------------------------------------------------------------------------------- 1 | # Nikko Rush 2 | # 8/3/2017 3 | 4 | import logging 5 | import sys 6 | import traceback 7 | 8 | 9 | def custom_handler(type, value, tback): 10 | logging.error(''.join(traceback.format_exception(type, value, tback))) 11 | 12 | sys.__excepthook__(type, value, traceback) 13 | 14 | sys.excepthook = custom_handler 15 | -------------------------------------------------------------------------------- /Visualizer/frames/ListFrame.py: -------------------------------------------------------------------------------- 1 | # Nikko Rush 2 | # 7/6/2017 3 | 4 | import PyQt5.QtCore as QtCore 5 | from PyQt5.QtCore import Qt 6 | 7 | from events import event, listener 8 | from frames.VisualizerFrame import VisualizerFrame 9 | from models.list_model import ListModel 10 | from ui import topics_list 11 | 12 | 13 | class ListFrame(VisualizerFrame): 14 | """description of class""" 15 | def __init__(self, parent, data=None): 16 | super(ListFrame, self).__init__(parent, data_manager=data) 17 | 18 | self.ui = topics_list.Ui_topicsList() 19 | self.ui.setupUi(self) 20 | 21 | self._filter = QtCore.QSortFilterProxyModel() 22 | self._model = ListModel(self.data.idea_names.values(), self) 23 | 24 | self._onselect_listener = listener.Listener() 25 | 26 | self._filter.setSourceModel(self._model) 27 | self.ui.listWidget.setModel(self._filter) 28 | 29 | self.ui.listWidget.selectionModel().selectionChanged.connect(self._on_select_model) 30 | self.ui.filterText.textEdited.connect(self._filter.setFilterFixedString) 31 | 32 | def add_select_listener(self, func): 33 | self._onselect_listener.add(func) 34 | 35 | def has_select_listener(self, func): 36 | return self._onselect_listener.has_handler(func) 37 | 38 | def remove_select_listener(self, func): 39 | self._onselect_listener.remove(func) 40 | 41 | def clear_selection(self): 42 | self.ui.listWidget.clearSelection() 43 | 44 | def _on_select_model(self, selected, deselected): 45 | indexes = set() 46 | for index in self.ui.listWidget.selectedIndexes(): 47 | indexes.add(self.data.idea_numbers[index.data(Qt.DisplayRole)]) 48 | 49 | eve = event.Event() 50 | eve.selected_indexes = indexes 51 | self._onselect_listener.invoke(eve) 52 | -------------------------------------------------------------------------------- /Visualizer/frames/MatplotlibFrame.py: -------------------------------------------------------------------------------- 1 | # Nikko Rush 2 | # 7/2/2017 3 | 4 | import PyQt5.QtCore as QtCore 5 | import PyQt5.QtWidgets as QtWidgets 6 | 7 | from matplotlib.figure import Figure 8 | from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg 9 | 10 | 11 | class QMatplotlib(FigureCanvasQTAgg): 12 | """Frame specifically for displaying matplotlib graphs""" 13 | 14 | def __init__(self, figure=None, parent=None, width=None, height=None, dpi=None): 15 | 16 | if figure is not None: 17 | self._figure = figure 18 | else: 19 | if width or height or dpi is None: 20 | self._figure = Figure() 21 | else: 22 | self._figure = Figure(figsize=(width, height), dpi=dpi) 23 | 24 | self._axes = self._figure.gca() 25 | 26 | super(QMatplotlib, self).__init__(self._figure) 27 | self.setParent(parent) 28 | self.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) 29 | # self._set_size() 30 | self.updateGeometry() 31 | 32 | self.setFocusPolicy(QtCore.Qt.ClickFocus) 33 | 34 | self._allow_redraw = True 35 | self._redraw_requested = False 36 | 37 | def _set_size(self): 38 | fig_w, fig_h = map(int, self._figure.get_size_inches() * self._figure.dpi) 39 | self.setMaximumSize(fig_w, fig_h) 40 | self.setMinimumSize(fig_w, fig_h) 41 | 42 | @property 43 | def axes(self): 44 | return self._axes 45 | 46 | @property 47 | def canvas(self): 48 | return self._figure.canvas 49 | 50 | def get_figure(self): 51 | return self._figure 52 | 53 | def redraw(self): 54 | if self._allow_redraw: 55 | self.draw() 56 | else: 57 | self._redraw_requested = True 58 | 59 | def allow_redraw(self): 60 | self._allow_redraw = True 61 | if self._redraw_requested: 62 | self.redraw() 63 | 64 | def prevent_redraw(self): 65 | self._allow_redraw = False 66 | self._redraw_requested = False 67 | 68 | def grow_plot(self, delta): 69 | size = self.figure.get_size_inches() 70 | size[0] = size[0] + delta/self.figure.dpi 71 | self.figure.set_size_inches(size) 72 | # self._set_size() 73 | # self.updateGeometry() 74 | self.redraw() 75 | 76 | def set_plot_size(self, width): 77 | size = self.figure.get_size_inches() 78 | size[0] = width/self.figure.dpi 79 | self.figure.set_size_inches(size) 80 | -------------------------------------------------------------------------------- /Visualizer/frames/PMIPlot.py: -------------------------------------------------------------------------------- 1 | # Nikko Rush 2 | # 7/5/2017 3 | 4 | import logging 5 | import math 6 | 7 | import PyQt5.QtWidgets as QtWidgets 8 | import PyQt5.QtGui as QtGui 9 | 10 | import matplotlib.cm as cm 11 | import matplotlib.colors as colors 12 | import matplotlib.patches as mpatches 13 | import numpy as np 14 | 15 | import colors as visualizer_colors 16 | from events import listener 17 | from frames.MatplotlibFrame import QMatplotlib 18 | from frames.VisualizerFrame import VisualizerFrame 19 | from ui import pmi_control_panel_vert 20 | from ui import pmi_control_panel_horiz 21 | from frames.matplotlib_util import Utils 22 | 23 | # These values are added to the x_lim and y_lim to ensure that all points are drawn on the screen 24 | X_BUFFER = 0.1 25 | Y_BUFFER = 0.1 26 | 27 | 28 | class PMIPlot(VisualizerFrame, Utils): 29 | def __init__(self, parent, data): 30 | super(PMIPlot, self).__init__(parent=parent, data_manager=data) 31 | 32 | # self.layout = QtWidgets.QHBoxLayout(self) 33 | self.layout = QtWidgets.QVBoxLayout(self) 34 | 35 | self._mpl_container = QtWidgets.QWidget(self) 36 | self._mpl_layout = QtWidgets.QHBoxLayout(self._mpl_container) 37 | self._old_mpl_width = self._mpl_container.width() 38 | 39 | self._mpl = QMatplotlib(parent=self._mpl_container) 40 | self._mpl_layout.addWidget(self._mpl) 41 | self._mpl.prevent_redraw() 42 | 43 | self.layout.addWidget(self._mpl_container) 44 | 45 | self.axes = self._mpl.axes 46 | self.canvas = self._mpl.canvas 47 | 48 | self._x_lim = [np.amin(data.ts_correlation) - X_BUFFER, np.amax(data.ts_correlation) + X_BUFFER] 49 | self._y_lim = [np.amin(data.pmi) - Y_BUFFER, np.amax(data.pmi) + Y_BUFFER] 50 | 51 | self._control_panel = QtWidgets.QWidget(self) 52 | self._control_panel_ui = pmi_control_panel_horiz.Ui_pmi_control_panel() 53 | self._control_panel_ui.setupUi(self._control_panel) 54 | self.layout.addWidget(self._control_panel) 55 | 56 | self._init_handlers() 57 | 58 | self.plot_data = None 59 | self.idea_indexes = None 60 | self.x_values = None 61 | self.y_values = None 62 | self.sample_size = None 63 | 64 | # TODO: Allow these to be set dynamically 65 | self.selected_color = "Black" 66 | # Colors should be a list of four matplotlib color maps or rgb tuples 67 | self._colors = ["Greens", 68 | "Oranges", 69 | "Reds", 70 | "Blues"] 71 | self._legend_proxies = list() 72 | 73 | self._prev_selected_ind = None 74 | self._prev_annotation = None 75 | 76 | self._on_select_listener = listener.Listener(self._change_selected_color, self._annotate_selected) 77 | self._on_reset_listener = listener.Listener(self._reset_graph) 78 | self._on_color_changed_listener = listener.Listener() 79 | 80 | self.on_select_clear_listener = listener.Listener(self._clear_selection) 81 | 82 | self._normalizer = colors.Normalize(vmin=0, vmax=1, clip=True) 83 | self._update_colors(self._colors) 84 | self.add_color_changed_listener(self._color_plot) 85 | 86 | self._setup_control_panel() 87 | 88 | self._has_data = False 89 | 90 | self.axes.legend(handles=self._legend_proxies, loc="best") 91 | 92 | self._mpl.allow_redraw() 93 | 94 | def resizeEvent(self, eve): 95 | super(PMIPlot, self).resizeEvent(eve) 96 | if eve.oldSize().width() == -1 or eve.oldSize().height() == -1: 97 | self._old_mpl_width = self._mpl_container.width() 98 | return 99 | 100 | # self._mpl.grow_plot(self._mpl_container.width()-self._old_mpl_width) 101 | self._mpl.set_plot_size(self._mpl_container.width()) 102 | 103 | def _setup_control_panel(self): 104 | # self._gear_icon = images.load_image("gear-2-16.gif") 105 | control_panel_ui = self._control_panel_ui 106 | control_panel_ui.resetButton.clicked.connect(self._on_reset) 107 | 108 | def _init_plot_(self): 109 | """Reset the axes for plotting""" 110 | self.axes.clear() 111 | self._has_data = False 112 | self.axes.set_title("PMI vs. Cooccurrence") 113 | self.axes.set_xlabel("Prevalence Correlation") 114 | self.axes.set_ylabel("Cooccurrence") 115 | self.axes.set_xlim(self._x_lim) 116 | self.axes.set_ylim(self._y_lim) 117 | 118 | def _update_colors(self, color_spec): 119 | """ 120 | Takes a sequence of 4 color tuples, builds the color maps, if 121 | the plot data isn't none will modify the plot colors 122 | """ 123 | 124 | self._colors = color_spec 125 | 126 | self._color_maps = [visualizer_colors.PMIColormap("PMIFriend", color_spec[0]), 127 | visualizer_colors.PMIColormap("PMITryst", color_spec[1]), 128 | visualizer_colors.PMIColormap("PMIHeadToHead", color_spec[2]), 129 | visualizer_colors.PMIColormap("PMIArmsRace", color_spec[3])] 130 | 131 | self.color_mappers = [cm.ScalarMappable(norm=self._normalizer, cmap=self._color_maps[0]), 132 | cm.ScalarMappable(norm=self._normalizer, cmap=self._color_maps[1]), 133 | cm.ScalarMappable(norm=self._normalizer, cmap=self._color_maps[2]), 134 | cm.ScalarMappable(norm=self._normalizer, cmap=self._color_maps[3])] 135 | 136 | self.color_samples = dict() 137 | self._legend_proxies = [] 138 | for mapper, name in zip(self.color_mappers, self.data.relation_types): 139 | rgba = mapper.to_rgba(0.7, bytes=True) 140 | self.color_samples[name] = rgba 141 | self._legend_proxies.append(mpatches.Patch(color=[i/255 for i in rgba], label=name)) 142 | 143 | self._on_color_changed() 144 | self._mpl.redraw() 145 | 146 | def plot(self, sample=None): 147 | # Generate a list of all strictly upper triangular indexes 148 | self.sample_size = sample 149 | points = [] 150 | for i in range(0, self.data.num_ideas): 151 | for j in range(i + 1, self.data.num_ideas): 152 | points.append((i, j)) 153 | 154 | self._plot(points, sample) 155 | 156 | def _plot(self, points, sample=None): 157 | 158 | if sample is not None and isinstance(sample, int): 159 | if 0 < sample and sample < len(points): 160 | indexes = np.random.choice(len(points), sample, replace=False) 161 | points = [points[i] for i in indexes] 162 | assert len(points) == sample 163 | 164 | xs, ys = [], [] 165 | for i, j in points: 166 | if np.isnan(self.data.pmi[i, j]) or np.isnan(self.data.ts_correlation[i, j]): 167 | continue 168 | if np.isinf(self.data.pmi[i, j]) or np.isinf(self.data.ts_correlation[i, j]): 169 | continue 170 | 171 | xs.append(self.data.ts_correlation[i, j]) 172 | ys.append(self.data.pmi[i, j]) 173 | 174 | self._init_plot_() 175 | self.plot_data = self.axes.scatter(xs, ys, picker=True) 176 | self.axes.legend(handles=self._legend_proxies, loc="best") 177 | 178 | self.idea_indexes = points 179 | self.x_values = xs 180 | self.y_values = ys 181 | 182 | self._has_data = True 183 | 184 | self._color_plot() 185 | 186 | def _color_plot(self): 187 | colors = [] 188 | for x, y in zip(self.x_values, self.y_values): 189 | distance = math.sqrt(x ** 2 + y ** 2) 190 | if x >= 0 and y >= 0: # First quadrant, someone has to select zero 191 | colors.append(self.color_mappers[0].to_rgba(distance)) 192 | elif x < 0 and y > 0: # Second quadrant 193 | colors.append(self.color_mappers[1].to_rgba(distance)) 194 | elif x < 0 and y < 0: # Third quadrant 195 | colors.append(self.color_mappers[2].to_rgba(distance)) 196 | elif x > 0 and y < 0: # Fourth quadrant 197 | colors.append(self.color_mappers[3].to_rgba(distance)) 198 | else: 199 | logging.error("({x}, {y}) couldn't be mapped onto grid".format(x=x, y=y)) 200 | colors.append((0, 0, 0, 0)) 201 | self.plot_data.set_color(colors) 202 | self._point_colors = colors 203 | 204 | self.axes.legend(handles=self._legend_proxies, loc="best") 205 | 206 | def _init_handlers(self): 207 | self.canvas.mpl_connect('button_press_event', self._on_click) 208 | self.canvas.mpl_connect('pick_event', self._on_select) 209 | 210 | def _on_click(self, event): 211 | pass 212 | 213 | # Event listener functions 214 | def add_select_listener(self, func): 215 | self._on_select_listener.add(func) 216 | 217 | def has_select_listener(self, func): 218 | return self._on_select_listener.has_handler(func) 219 | 220 | def remove_select_listener(self, func): 221 | self._on_select_listener.remove(func) 222 | 223 | def add_reset_listener(self, func): 224 | self._on_reset_listener.add(func) 225 | 226 | def has_reset_listener(self, func): 227 | return self._on_reset_listener.has_handler(func) 228 | 229 | def remove_reset_listener(self, func): 230 | self._on_reset_listener.remove(func) 231 | 232 | def add_color_changed_listener(self, func): 233 | self._on_color_changed_listener.add(func) 234 | 235 | def has_color_changed_listener(self, func): 236 | return self._on_color_changed_listener.has_handler(func) 237 | 238 | def remove_color_changed_listener(self, func): 239 | self._on_color_changed_listener.remove(func) 240 | 241 | def _on_select(self, event): 242 | i, j = self.idea_indexes[event.ind[0]] 243 | event.selected_indexes = (i, j) 244 | self._on_select_listener.invoke(event) 245 | 246 | def _on_reset(self): 247 | self._on_reset_listener.invoke_empty() 248 | 249 | def _on_color_changed(self): 250 | self._on_color_changed_listener.invoke_empty() 251 | 252 | def _change_selected_color(self, eve): 253 | 254 | ind = self._get_point_index_from_event(eve) 255 | 256 | if self._prev_selected_ind == ind: 257 | return None 258 | 259 | if self._prev_selected_ind is not None: 260 | self.plot_data._edgecolors[self._prev_selected_ind] = self._point_colors[self._prev_selected_ind] 261 | 262 | self.plot_data._edgecolors[ind] = colors.to_rgba(self.selected_color) 263 | self._prev_selected_ind = ind 264 | 265 | self._mpl.redraw() 266 | 267 | def _annotate_selected(self, eve): 268 | ind = self._get_point_index_from_event(eve) 269 | 270 | if self._prev_annotation is not None: 271 | self._clear_selection() 272 | 273 | topic_1_name, topic_2_name = self.data.get_display_idea_names(self.idea_indexes[ind]) 274 | 275 | x_cord, y_cord = self.x_values[ind], self.y_values[ind] 276 | text = "({0},{sep}{1})".format(topic_1_name, topic_2_name, sep=' ' if self.data._is_keywords else '\n') 277 | 278 | offset = self._get_text_offset(text) if x_cord > 0 else 0 279 | 280 | self._prev_annotation = self.axes.annotate(s=text, xy=(self.x_values[ind] - offset, self.y_values[ind]), 281 | annotation_clip=False) 282 | self._mpl.redraw() 283 | 284 | def _get_text_offset(self, text): 285 | text_width = self._get_text_width(text) 286 | 287 | dis_data_trans = self.axes.transData.inverted() 288 | disp_pnt_1 = (0, 0) 289 | disp_pnt_2 = (text_width, 0) 290 | 291 | data_pnt_1, data_pnt_2 = dis_data_trans.transform([disp_pnt_1, disp_pnt_2]) 292 | return data_pnt_2[0] - data_pnt_1[0] 293 | 294 | def _get_text_width(self, text): 295 | t = self.axes.text(0, 0, text) 296 | bb = t.get_window_extent(renderer=self._mpl.figure.canvas.get_renderer()) 297 | width = bb.width 298 | t.remove() 299 | return width 300 | 301 | def _get_point_index_from_event(self, eve): 302 | if hasattr(eve, "ind"): 303 | return eve.ind[0] 304 | elif hasattr(eve, "selected_indexes"): 305 | a, b = tuple(eve.selected_indexes) 306 | if (a, b) in self.idea_indexes: 307 | return self.idea_indexes.index((a, b)) 308 | elif (b, a) in self.idea_indexes: 309 | return self.idea_indexes.index((b, a)) 310 | else: 311 | logging.warn("Selected indexes, {0}, not found on PMI graph".format((a,b))) 312 | return None 313 | 314 | def _clear_selection(self): 315 | if self._prev_annotation is not None: 316 | self._prev_annotation.remove() 317 | self._prev_annotation = None 318 | 319 | def filter_relation(self, eve): 320 | idea_indexes = eve.selected_indexes 321 | 322 | self._clear_selection() 323 | self._clear_filter() 324 | 325 | self._add_filter([self.data.idea_names[index] for index in idea_indexes]) 326 | 327 | old_point = None 328 | if self._prev_selected_ind is not None: 329 | old_point = self.idea_indexes[self._prev_selected_ind] 330 | # You want to plot everything in both the rows and columns specified by idea_indexes 331 | # Take two passes over the matrixes 332 | points = set() 333 | for i in idea_indexes: # Row 334 | for j in range(i + 1, self.data.num_ideas): # Col 335 | points.add((i, j)) 336 | 337 | for j in idea_indexes: 338 | for i in range(0, j): 339 | points.add((i, j)) 340 | 341 | self._plot(list(points)) 342 | 343 | if old_point is not None: 344 | self._prev_selected_ind = None 345 | if old_point in self.idea_indexes: 346 | new_index = self.idea_indexes.index(old_point) 347 | self._on_select(type('', (object,), {"ind": [new_index]})()) 348 | 349 | if hasattr(eve, "should_select") and eve.should_select: 350 | self._on_select_listener.invoke(eve) 351 | 352 | self._mpl.redraw() 353 | 354 | def _reset_graph(self): 355 | self._clear_selection() 356 | self.plot(sample=self.sample_size) 357 | self._clear_filter() 358 | self._mpl.redraw() 359 | 360 | def get_colors_hex(self): 361 | return ["#{:02x}{:02x}{:02x}".format(*rgb) for rgb in self._colors] 362 | 363 | def get_colors_rgb(self): 364 | return self._colors 365 | 366 | def _add_filter(self, names): 367 | self._control_panel_ui.filteredList.addItems(names) 368 | 369 | def _clear_filter(self): 370 | self._control_panel_ui.filteredList.clear() 371 | 372 | 373 | def get_row_col(n, i): 374 | """ 375 | Given the index of a data point of an nxn strictly upper triangular matrix (a_ij = 0 for i>=j), 376 | this will calculate the row and column indexes in the nxn matrix 377 | """ 378 | row = n - 2 - math.floor(math.sqrt(-8 * i + 4 * n * (n - 1) - 7) / 2 - 0.5) 379 | col = i + row + 1 - n * (n - 1) / 2 + (n - row) * ((n - row) - 1) / 2 380 | return int(row), int(col) 381 | -------------------------------------------------------------------------------- /Visualizer/frames/QtMatplotlib.py: -------------------------------------------------------------------------------- 1 | # Nikko Rush 2 | # 8/2/2017 3 | 4 | import sys 5 | 6 | import matplotlib 7 | 8 | import PyQt5.QtCore as QtCore 9 | import PyQt5.QtWidgets as QtWidgets 10 | 11 | from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg 12 | from matplotlib.figure import Figure 13 | import numpy as np 14 | 15 | matplotlib.use("Qt5Agg") 16 | 17 | 18 | class QMatplotlib(FigureCanvasQTAgg): 19 | 20 | def __init__(self, parent=None, width=5, height=4, dpi=100): 21 | self.figure = Figure(figsize=(width, height), dpi=dpi) 22 | self.axes = self.figure.add_subplot(111) 23 | 24 | self.get_initial() 25 | 26 | FigureCanvasQTAgg.__init__(self, self.figure) 27 | self.setParent(parent) 28 | 29 | FigureCanvasQTAgg.setSizePolicy(self, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) 30 | FigureCanvasQTAgg.updateGeometry(self) 31 | 32 | def get_initial(self): 33 | t = np.arange(0.0, 3.0, 0.01) 34 | s = np.sin(2*np.pi*t) 35 | self.axes.plot(t, s, 'r') 36 | 37 | 38 | class Application(QtWidgets.QMainWindow): 39 | 40 | def __init__(self): 41 | QtWidgets.QMainWindow.__init__(self) 42 | 43 | self.setAttribute(QtCore.Qt.WA_DeleteOnClose) 44 | 45 | self.main_widget = QtWidgets.QWidget(self) 46 | 47 | layout = QtWidgets.QVBoxLayout(self.main_widget) 48 | graph = QMatplotlib(parent=self.main_widget) 49 | layout.addWidget(graph) 50 | 51 | self.main_widget.setFocus() 52 | self.setCentralWidget(self.main_widget) 53 | 54 | 55 | def test(): 56 | import sys 57 | import PyQt5.QtWidgets as QtWidgets 58 | 59 | """ 60 | ZetCode PyQt4 tutorial 61 | 62 | In this example, we create a skeleton 63 | of a calculator using a QtGui.QGridLayout. 64 | 65 | author: Jan Bodnar 66 | website: zetcode.com 67 | last edited: July 2014 68 | """ 69 | 70 | class Example(QtWidgets.QWidget): 71 | 72 | def __init__(self): 73 | super(Example, self).__init__() 74 | 75 | self.init_ui() 76 | 77 | def init_ui(self): 78 | 79 | grid = QtWidgets.QGridLayout() 80 | self.setLayout(grid) 81 | 82 | names = ['Cls', 'Bck', '', 'Close', 83 | '7', '8', '9', '/', 84 | '4', '5', '6', '*', 85 | '1', '2', '3', '-', 86 | '0', '.', '=', '+'] 87 | 88 | positions = [(i, j) for i in range(5) for j in range(4)] 89 | 90 | for position, name in zip(positions, names): 91 | 92 | if name == '': 93 | continue 94 | button = QtWidgets.QPushButton(name) 95 | grid.addWidget(button, *position) 96 | 97 | self.move(300, 150) 98 | self.setWindowTitle('Calculator') 99 | self.show() 100 | 101 | def main(): 102 | app = QtWidgets.QApplication(sys.argv) 103 | ex = Example() 104 | sys.exit(app.exec_()) 105 | 106 | main() 107 | 108 | if __name__ == "__main__": 109 | qApp = QtWidgets.QApplication(sys.argv) 110 | window = Application() 111 | 112 | window.show() 113 | sys.exit(qApp.exec_()) 114 | # test() 115 | -------------------------------------------------------------------------------- /Visualizer/frames/RelationTypeFrame.py: -------------------------------------------------------------------------------- 1 | # Nikko Rush 2 | # 7/6/2017 3 | 4 | import PyQt5.QtWidgets as QtWidgets 5 | import PyQt5.QtGui as QtGui 6 | 7 | import numpy as np 8 | 9 | from events import listener, event 10 | from frames.VisualizerFrame import VisualizerFrame 11 | from ui import relation_types_tabs 12 | 13 | 14 | def _sort_by_strength(data, strength_index=2): 15 | sorted_list = sorted(data, key=lambda item: abs(item[strength_index])) 16 | sorted_list.reverse() 17 | return sorted_list 18 | 19 | 20 | class RelationTypeFrame(VisualizerFrame): 21 | def __init__(self, parent, data): 22 | super(RelationTypeFrame, self).__init__(parent=parent, data_manager=data) 23 | 24 | self.ui = relation_types_tabs.Ui_relationTypes() 25 | self.ui.setupUi(self) 26 | 27 | self.data = data 28 | self.types = self.data.relation_types 29 | 30 | self._buttons = [] 31 | self._tables = [] 32 | self._config_ui() 33 | 34 | self.list_data = {name: [] for name in self.types} 35 | 36 | self.active_index = None 37 | self._onselect_listener = listener.Listener() 38 | 39 | self._determine_relations() 40 | self._fill_tables() 41 | 42 | self._set_active_index(0) 43 | 44 | def _config_ui(self): 45 | for i, name in enumerate(self.types): 46 | tbl_name = "{0}Table".format(name.lower().replace('-', '')) 47 | tbl = self.findChild(QtWidgets.QTableWidget, tbl_name) 48 | 49 | tbl.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch) 50 | 51 | self._tables.append(tbl) 52 | tbl.itemSelectionChanged.connect(self._on_select) 53 | 54 | self.ui.tabWidget.currentChanged.connect(lambda i: self._set_active_index(i)) 55 | 56 | def _determine_relations(self): 57 | pmi = self.data.pmi 58 | ts_correlation = self.data.ts_correlation 59 | trysts, friends, head, arms = [], [], [], [] 60 | num_ideas = self.data.num_ideas 61 | cnt = 0 62 | for i in range(0, num_ideas): 63 | for j in range(i + 1, num_ideas): 64 | point_pmi = pmi[i, j] 65 | point_cor = ts_correlation[i, j] 66 | if np.isnan(point_pmi) or np.isnan(point_cor): 67 | continue 68 | if np.isinf(point_pmi) or np.isinf(point_cor): 69 | continue 70 | cnt += 1 71 | strength = point_pmi * point_cor 72 | 73 | if 0 <= point_pmi and point_cor < 0: 74 | trysts.append((i, j, strength)) 75 | elif 0 <= point_pmi and 0 <= point_cor: 76 | friends.append((i, j, strength)) 77 | elif point_pmi < 0 and point_cor < 0: 78 | head.append((i, j, strength)) 79 | elif point_pmi < 0 and 0 <= point_cor: 80 | arms.append((i, j, strength)) 81 | 82 | assert len(trysts) + len(friends) + len(head) + len(arms) == cnt 83 | 84 | self._add_to_list(friends, "Friends", sort=True, top=25) 85 | self._add_to_list(trysts, "Tryst", sort=True, top=25) 86 | self._add_to_list(head, "Head-To-Head", sort=True, top=25) 87 | self._add_to_list(arms, "Arms-Race", sort=True, top=25) 88 | 89 | def _add_to_list(self, items, name, sort=True, top=None): 90 | data = self.list_data[name] 91 | if sort: 92 | items = _sort_by_strength(items) 93 | 94 | if top is not None: 95 | items = items[:top] 96 | 97 | data.extend(items) 98 | 99 | def _fill_tables(self): 100 | for index, name in enumerate(self.data.relation_types): 101 | raw_data = self.list_data[name] 102 | tbl = self._tables[index] 103 | 104 | data = self._get_output_repr(raw_data) 105 | 106 | for i, relation in enumerate(data): 107 | tbl.insertRow(i) 108 | for j, item in enumerate(relation): 109 | qtbl_item = QtWidgets.QTableWidgetItem(item) 110 | tbl.setItem(i, j, qtbl_item) 111 | 112 | def _get_output_repr(self, items): 113 | outputs = [] 114 | for item in items: 115 | output = ("{n:.{d}}".format(n=item[2], d=4), self.data.idea_names[item[0]], self.data.idea_names[item[1]]) 116 | outputs.append(output) 117 | return outputs 118 | 119 | def clear_lists(self): 120 | for table in self._tables: 121 | table.clearContents() 122 | 123 | def add_select_listener(self, func): 124 | self._onselect_listener.add(func) 125 | 126 | def has_select_listener(self, func): 127 | return self._onselect_listener.has_handler(func) 128 | 129 | def remove_select_listener(self, func): 130 | self._onselect_listener.remove(func) 131 | 132 | def _on_select(self): 133 | col_count = self._tables[self.active_index].columnCount() 134 | 135 | selected_items = dict() 136 | for item in self._tables[self.active_index].selectedItems(): 137 | row, col = item.row(), item.column() 138 | if row not in selected_items.keys(): 139 | selected_items[row] = [0] * col_count 140 | selected_items[row][col] = item.text() 141 | 142 | if not len(selected_items): 143 | return None 144 | 145 | selected_indexes = list() 146 | for row, item in selected_items.items(): 147 | selected_indexes.append(self.data.idea_numbers[item[1]]) 148 | selected_indexes.append(self.data.idea_numbers[item[2]]) 149 | 150 | eve = event.Event() 151 | eve.selected_indexes = selected_indexes 152 | eve.should_select = True 153 | self._onselect_listener.invoke(eve) 154 | 155 | def _btn_click(self, name): 156 | self._set_active(name) 157 | 158 | def _set_active(self, name): 159 | index = self.types.index(name) 160 | return self._set_active_index(index) 161 | 162 | def _set_active_index(self, index): 163 | if self.active_index is not None: 164 | self._tables[self.active_index].clearSelection() 165 | 166 | self.active_index = index 167 | 168 | def clear_selection(self): 169 | self._tables[self.active_index].clearSelection() 170 | 171 | def color_tabs(self, color_map): 172 | tab_bar = self.ui.tabWidget.tabBar() 173 | 174 | for i, name in zip(range(tab_bar.count()), self.types): 175 | tab_bar.setTabTextColor(i, QtGui.QColor(*color_map[name])) 176 | 177 | def color_changed(self, pmi_frame): 178 | self.color_tabs(pmi_frame.color_samples) 179 | -------------------------------------------------------------------------------- /Visualizer/frames/TimeSeriesFrame.py: -------------------------------------------------------------------------------- 1 | # Nikko Rush 2 | # 7/5/2017 3 | 4 | import numpy as np 5 | import PyQt5.QtWidgets as QtWidgets 6 | 7 | from frames.VisualizerFrame import VisualizerFrame 8 | from frames.MatplotlibFrame import QMatplotlib 9 | from frames.matplotlib_util import Utils 10 | 11 | 12 | import matplotlib.patches as mpatches 13 | 14 | 15 | class TimeSeriesFrame(VisualizerFrame, Utils): 16 | def __init__(self, parent, data): 17 | super(TimeSeriesFrame, self).__init__(parent=parent, data_manager=data) 18 | 19 | self.layout = QtWidgets.QVBoxLayout(self) 20 | 21 | self._mpl = QMatplotlib(parent=self) 22 | self.axes = self._mpl.axes 23 | 24 | self.layout.addWidget(self._mpl) 25 | 26 | self.data = data 27 | 28 | self._init_plot() 29 | 30 | self.series = None 31 | self._has_data = False 32 | 33 | def _init_plot(self): 34 | self.axes.clear() 35 | self._has_data = False 36 | self.axes.set_title("Time Series") 37 | self.axes.set_xlabel("Year") 38 | self.axes.set_ylabel("Frequency") 39 | self.axes.set_ylim([0, 1]) 40 | 41 | def plot_series(self, y, name=None, redraw=True): 42 | self._add_series(y) 43 | self.axes.plot(self.data.x_values, y) 44 | if name is not None: 45 | self.axes.legend([name], loc="best") 46 | 47 | if redraw: 48 | self.redraw() 49 | 50 | self._has_data = True 51 | 52 | def plot_idea_indexes_event(self, event): 53 | self.plot_idea_indexes(event.selected_indexes) 54 | 55 | def plot_idea_indexes(self, indexes, names=None): 56 | self.clear() 57 | 58 | index_names = [] 59 | for index in indexes: 60 | self.plot_series(self.data.ts_matrix[index], redraw=False) 61 | index_names.append(self.data.get_display_idea_names(index)) 62 | 63 | # self.get_correlation() 64 | 65 | if names is not None: 66 | self.axes.legend(names, loc="best") 67 | else: 68 | self.axes.legend(index_names, loc="best") 69 | 70 | self._mpl.redraw() 71 | 72 | def _add_series(self, data): 73 | if self.series is None: 74 | self.series = np.copy(data) 75 | else: 76 | self.series = np.column_stack((self.series, data)) 77 | 78 | def get_correlation(self): 79 | correlation = np.corrcoef(self.series, rowvar=False) 80 | self._correlation_data.set(round(correlation[0, 1], 5)) 81 | 82 | def clear(self): 83 | self.series = None 84 | self._init_plot() 85 | self._mpl.redraw() 86 | 87 | # self._correlation_data.set(0) 88 | 89 | def select_relation_type(self, event): 90 | self.plot_idea_indexes(event.selected_indexes) 91 | -------------------------------------------------------------------------------- /Visualizer/frames/TimeSeriesVisualizer.py: -------------------------------------------------------------------------------- 1 | # Nikko Rush 2 | # 7/11/17 3 | 4 | import matplotlib.pyplot as plt 5 | 6 | 7 | # Plot the Time Series for the given idea 8 | def plot_idea_timeseries(idea_name, idea_numbers, ts_matrix): 9 | ts_data = ts_matrix[idea_numbers[idea_name]] 10 | 11 | start = 1980 12 | end = 2015 13 | 14 | plt.plot(ts_data) 15 | plt.show() 16 | 17 | 18 | def show_time_series(idea_numbers, ts_matrix): 19 | # Get a list of idea names 20 | ideas = list(idea_numbers.keys()) 21 | 22 | display_help() 23 | 24 | quit = False 25 | while not quit: 26 | selection = input(">") 27 | if selection in ideas: 28 | plot_idea_timeseries(selection, idea_numbers, ts_matrix) 29 | elif selection == "l": 30 | print(ideas) 31 | elif selection == "q": 32 | quit = True 33 | else: 34 | display_help() 35 | 36 | 37 | def display_help(): 38 | print("Enter an idea name to view the time series, l to list ideas, q to quit, or h to view this help message") 39 | -------------------------------------------------------------------------------- /Visualizer/frames/TopRelations.py: -------------------------------------------------------------------------------- 1 | # Nikko Rush 2 | # 7/10/2017 3 | 4 | import PyQt5.QtWidgets as QtWidgets 5 | 6 | import numpy as np 7 | 8 | from events import event, listener 9 | from frames.VisualizerFrame import VisualizerFrame 10 | from ui import top_relations 11 | 12 | 13 | class TopRelations(VisualizerFrame): 14 | def __init__(self, parent, data, topic_index=0): 15 | super(TopRelations, self).__init__(parent, data_manager=data) 16 | 17 | self.ui = top_relations.Ui_topRelation() 18 | self.ui.setupUi(self) 19 | 20 | self._index = topic_index 21 | 22 | self.ui.tableWidget.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch) 23 | 24 | self._onselect_listener = listener.Listener() 25 | self.ui.tableWidget.itemClicked.connect(self._on_select) 26 | 27 | self.idea_index = -1 28 | 29 | def set_idea_name(self, name): 30 | return self.set_idea_index(self.data.idea_numbers[name]) 31 | 32 | def set_idea_index(self, index): 33 | strengths = [] 34 | pmi, ts_cor = self.data.pmi, self.data.ts_correlation 35 | for i in range(0, self.data.num_ideas): 36 | for j in range(i + 1, self.data.num_ideas): 37 | if i != index and j != index: 38 | continue 39 | point_pmi = pmi[i, j] 40 | point_cor = ts_cor[i, j] 41 | if np.isnan(point_pmi) or np.isnan(point_cor): 42 | continue 43 | if np.isinf(point_pmi) or np.isinf(point_cor): 44 | continue 45 | 46 | strength = point_pmi * point_cor 47 | point_type = self._get_point_type(point_pmi, point_cor) 48 | 49 | other_topic_name = self.data.idea_names[i if i != index else j] 50 | self._insert_sorted(strengths, ("{n:.{d}}".format(n=strength, d=3), point_type, other_topic_name), 51 | sort_index=0) 52 | 53 | self._clear_list() 54 | self._insert_list(strengths) 55 | 56 | self.idea_index = index 57 | 58 | # Update the frame title 59 | self.ui.relationName.setText(self.data.get_display_idea_names(index)) 60 | 61 | @staticmethod 62 | def _get_point_type(point_pmi, point_cor): 63 | if 0 <= point_pmi and point_cor < 0: 64 | return "Tryst" 65 | elif 0 <= point_pmi and 0 <= point_cor: 66 | return "Friends" 67 | elif point_pmi < 0 and point_cor < 0: 68 | return "Head-To-Head" 69 | elif point_pmi < 0 and 0 <= point_cor: 70 | return "Arms-Race" 71 | 72 | @staticmethod 73 | def _insert_sorted(items, item, sort_index=0): 74 | index = 0 75 | for index in range(len(items)): 76 | list_item = items[index] 77 | 78 | if item[sort_index] >= list_item[sort_index]: 79 | break 80 | items.insert(index, item) 81 | 82 | def _insert_list(self, items): 83 | tbl = self.ui.tableWidget 84 | for i, relation in enumerate(items): 85 | tbl.insertRow(i) 86 | for j, item in enumerate(relation): 87 | qtbl_item = QtWidgets.QTableWidgetItem(item) 88 | tbl.setItem(i, j, qtbl_item) 89 | 90 | def set_idea_event(self, event): 91 | self.set_idea_index(event.selected_indexes[self._index]) 92 | 93 | def clear(self): 94 | self.ui.relationName.clear() 95 | self._clear_list() 96 | 97 | def _clear_list(self): 98 | self.ui.tableWidget.clearContents() 99 | 100 | def clear_selection(self): 101 | self.ui.tableWidget.clearSelection() 102 | 103 | def _on_select(self): 104 | selected = self.data.idea_numbers[self.ui.tableWidget.selectedItems()[-1].text()] 105 | eve = event.Event() 106 | eve.selected_indexes = [self.idea_index, selected] 107 | self._onselect_listener.invoke(eve) 108 | 109 | def add_select_listener(self, func): 110 | self._onselect_listener.add(func) 111 | 112 | def has_select_listener(self, func): 113 | return self._onselect_listener.has_handler(func) 114 | 115 | def remove_select_listener(self, func): 116 | self._onselect_listener.remove(func) 117 | -------------------------------------------------------------------------------- /Visualizer/frames/VisualizerFrame.py: -------------------------------------------------------------------------------- 1 | # Nikko Rush 2 | # 7/2/2017 3 | 4 | import PyQt5.QtWidgets as QtWidgets 5 | 6 | 7 | class VisualizerFrame(QtWidgets.QWidget): 8 | """Abstract class, please don't instantiate""" 9 | def __init__(self, parent=None, data_manager=None): 10 | super(VisualizerFrame, self).__init__(parent) 11 | self.data = data_manager 12 | -------------------------------------------------------------------------------- /Visualizer/frames/VisualizerWidget.py: -------------------------------------------------------------------------------- 1 | # Nikko Rush 2 | # 8/7/2017 3 | 4 | import logging 5 | 6 | from PyQt5.QtWidgets import QWidget, QHBoxLayout, QTabWidget, QSplitter, QFileDialog 7 | 8 | from events.listener import Listener 9 | from frames.VisualizerFrame import VisualizerFrame 10 | from frames.PMIPlot import PMIPlot 11 | from frames.TimeSeriesFrame import TimeSeriesFrame 12 | from frames.ListFrame import ListFrame 13 | from frames.RelationTypeFrame import RelationTypeFrame 14 | from frames.TopRelations import TopRelations 15 | from ui import visualizer 16 | 17 | import data 18 | 19 | 20 | class VisualizerWidget(VisualizerFrame): 21 | def __init__(self, parent, data_manager): 22 | super(VisualizerWidget, self).__init__(parent=parent) 23 | 24 | self.ui = visualizer.Ui_visualizaerWidget() 25 | self.ui.setupUi(self) 26 | 27 | self._data = data_manager 28 | self._load_visualizer() 29 | 30 | self._reset_listener = Listener() 31 | 32 | def sizeHint(self): 33 | return self.parent().size() 34 | 35 | def _load_visualizer(self): 36 | self._pmi = PMIPlot(self, self._data) 37 | self._pmi.plot(sample=1000) 38 | self.ui.pmiWidget.layout().addWidget(self._pmi) 39 | 40 | self._ts = TimeSeriesFrame(self, self._data) 41 | self.ui.tsWidget.layout().addWidget(self._ts) 42 | 43 | self._idea_list = ListFrame(self.ui.tabWidget, data=self._data) 44 | self.ui.tabWidget.insertTab(0, self._idea_list, "Ideas") 45 | 46 | self._relation_types = RelationTypeFrame(self, data=self._data) 47 | self.ui.tabWidget.insertTab(1, self._relation_types, "Types") 48 | self._relation_types.color_tabs(self._pmi.color_samples) 49 | 50 | self._top_relation_1 = TopRelations(self, data=self._data, topic_index=0) 51 | self.ui.tabWidget.insertTab(2, self._top_relation_1, "Top Relation 1") 52 | self._top_relation_2 = TopRelations(self, data=self._data, topic_index=1) 53 | self.ui.tabWidget.insertTab(3, self._top_relation_2, "Top Relation 2") 54 | 55 | # PMI Select Listener 56 | self._pmi.add_select_listener(self._ts.plot_idea_indexes_event) 57 | self._pmi.add_select_listener(self._top_relation_1.set_idea_event) 58 | self._pmi.add_select_listener(self._top_relation_2.set_idea_event) 59 | 60 | # Idea list Select Listener 61 | self._idea_list.add_select_listener(self._pmi.filter_relation) 62 | 63 | # Relation types Select Listener 64 | self._relation_types.add_select_listener(self._pmi.filter_relation) 65 | 66 | # PMI reset Listeners 67 | self._pmi.add_reset_listener(self._clear_list_selections_factory()) 68 | self._pmi.add_reset_listener(self._ts.clear) 69 | 70 | # Keep lists current with selection 71 | self._relation_types.add_select_listener(self._pmi.filter_relation) 72 | 73 | # Clear other lists on selection 74 | self._relation_types.add_select_listener(self._clear_list_selections_factory(self._relation_types)) 75 | self._idea_list.add_select_listener(self._clear_list_selections_factory(self._idea_list)) 76 | self._top_relation_1.add_select_listener(self._clear_list_selections_factory(self._top_relation_1)) 77 | self._top_relation_2.add_select_listener(self._clear_list_selections_factory(self._top_relation_2)) 78 | 79 | # Auxillary data structures 80 | self._lists = {self._idea_list, self._relation_types, self._top_relation_1, self._top_relation_2} 81 | 82 | def resizeEvent(self, event): 83 | dpi = self.physicalDpiX() 84 | # Make the width of the screen 4 inches or one fifth the width of the screen 85 | self.ui.tabWidget.setFixedWidth(min(4*dpi, int(self.size().width()/5))) 86 | super(VisualizerWidget, self).resizeEvent(event) 87 | 88 | def _clear_list_selections_factory(self, caller=None): 89 | def func(): 90 | for item in self._lists: 91 | if item == caller: 92 | continue 93 | item.clear_selection() 94 | return func 95 | 96 | def save_pmi(self): 97 | self._save_plot(self._pmi, "PMI") 98 | 99 | def save_ts(self): 100 | self._save_plot(self._ts, "Time Series") 101 | 102 | def save_both(self): 103 | self._save_plot(self._pmi, "PMI") 104 | self._save_plot(self._ts, "Time Series") 105 | 106 | def save_data(self, fname): 107 | return data.save_data(fname, self._data) 108 | 109 | def _save_plot(self, target, plot_name): 110 | if not target.confirm_on_empty(plot_name): 111 | return None 112 | 113 | dialog = QFileDialog(self) 114 | dialog.setAcceptMode(QFileDialog.AcceptSave) 115 | dialog.setFileMode(QFileDialog.AnyFile) 116 | formats = self._mpl_output_formats(self._pmi) 117 | dialog.setNameFilters(["Matplotlib Formats " + formats, "All Files (*)"]) 118 | if dialog.exec(): 119 | target.save_plot(dialog.selectedFiles()[0]) 120 | 121 | @staticmethod 122 | def _mpl_output_formats(widget): 123 | items = [] 124 | for key in widget.get_supported_formats(): 125 | items.append("*.{0}".format(key)) 126 | return '(' + ', '.join(items) + ')' 127 | -------------------------------------------------------------------------------- /Visualizer/frames/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nwrush/Visualization/bc134aa02156956ea3644b34d491da9f0e9f63ae/Visualizer/frames/__init__.py -------------------------------------------------------------------------------- /Visualizer/frames/matplotlib_util.py: -------------------------------------------------------------------------------- 1 | # Nikko Rush 2 | # 8/18/17 3 | 4 | from PyQt5.QtWidgets import QMessageBox 5 | 6 | 7 | class Utils: 8 | def save_plot(self, fname): 9 | self._mpl.get_figure().savefig(fname) 10 | 11 | def confirm_on_empty(self, name): 12 | if not self._has_data: 13 | reply = QMessageBox.question(self, "Confirm Save Plot", 14 | "{0} plot is empty\nAre you sure you want to save it?".format(name), 15 | QMessageBox.Yes | QMessageBox.No, QMessageBox.No) 16 | return int(reply) == QMessageBox.Yes 17 | return True 18 | 19 | def get_supported_formats(self): 20 | return self._mpl.canvas.get_supported_filetypes() 21 | -------------------------------------------------------------------------------- /Visualizer/frames/preprocessor_controller.py: -------------------------------------------------------------------------------- 1 | # Nikko Rush 2 | # 7/18/17 3 | 4 | import logging 5 | import os 6 | import os.path 7 | import queue 8 | import shutil 9 | import threading 10 | import sys 11 | 12 | from PyQt5.QtCore import QTimer 13 | from PyQt5.QtWidgets import QFileDialog, QVBoxLayout, QWidget, QMessageBox 14 | from PyQt5.QtGui import QPalette, QColor 15 | 16 | from frames.VisualizerFrame import VisualizerFrame 17 | from ui import preprocessor_form 18 | from ui import preprocessor_run 19 | 20 | has_preprocessor = False 21 | try: 22 | from idea_relations import main as idea_rel_main 23 | has_preprocessor = True 24 | except ImportError as ex: 25 | logging.error("Module idea_relations not found, preprocessor will not run.") 26 | logging.exception(ex) 27 | idea_rel_main = None 28 | 29 | 30 | RUNTYPE_OPTIONS = ["Keywords", "Topics"] 31 | GROUP_BY = ["Year", "Month", "Quarter"] 32 | 33 | DEFAULT_PALETTE = QPalette() 34 | 35 | DEFAULT_PROC_DIR = "./output/{exp_name}/proc/" 36 | DEFAULT_FINAL_DIR = "./output/{exp_name}/final/" 37 | 38 | 39 | def import_preprocessor(): 40 | has_preprocessor = False 41 | try: 42 | from idea_relations import main as idea_rel_main 43 | has_preprocessor = True 44 | except ImportError as ex: 45 | logging.error("Module idea_relations not found, preprocessor will not run.") 46 | logging.exception(ex) 47 | idea_rel_main = None 48 | return has_preprocessor, idea_rel_main 49 | 50 | 51 | class PreprocessorController(VisualizerFrame): 52 | def __init__(self, parent, finished_callback=None): 53 | super(PreprocessorController, self).__init__(parent=parent) 54 | 55 | self._layout = QVBoxLayout(self) 56 | self.setLayout(self._layout) 57 | 58 | self._form_widget = QWidget(self) 59 | self._form_ui = preprocessor_form.Ui_preprocessorForm() 60 | self._form_ui.setupUi(self._form_widget) 61 | self._layout.addWidget(self._form_widget) 62 | self._connect_form_ui() 63 | 64 | self._run_widget = QWidget(self) 65 | self._run_ui = preprocessor_run.Ui_preprocessorRun() 66 | self._run_ui.setupUi(self._run_widget) 67 | self._layout.addWidget(self._run_widget) 68 | self._connect_run_ui() 69 | 70 | self._timer = QTimer() 71 | self._timer.setSingleShot(True) 72 | self._timer.setInterval(500) 73 | 74 | self._preprocessor_thread = None 75 | self._num_steps = None 76 | self._message_queue = queue.Queue() 77 | self._callback = finished_callback 78 | 79 | self.output_name = None 80 | 81 | if not has_preprocessor: 82 | QMessageBox.about(self, "Missing Preprocessor", 83 | "The preprocessor could not be loaded and therefore can't be run\n" 84 | "Check the log file for details") 85 | 86 | def _connect_form_ui(self): 87 | # Setup the option combo box 88 | ui = self._form_ui 89 | ui.optionBox.currentIndexChanged.connect(self._option_box_changed) 90 | self._option_box_changed(0) 91 | 92 | self._file_dialog_factory(ui.inputFileBtn, ui.inputFile, QFileDialog.ExistingFile) 93 | self._file_dialog_factory(ui.outputDirBtn, ui.outputDir, QFileDialog.Directory) 94 | self._file_dialog_factory(ui.finalDirBtn, ui.finalDir, QFileDialog.Directory) 95 | self._file_dialog_factory(ui.bckFileBtn, ui.bckFile, QFileDialog.ExistingFile) 96 | self._file_dialog_factory(ui.malletDirBtn, ui.malletDir, QFileDialog.Directory) 97 | 98 | ui.showAdvancedBox.clicked.connect(self._toggle_advanced) 99 | self._toggle_advanced(ui.showAdvancedBox.isChecked()) 100 | 101 | def _option_box_changed(self, index): 102 | text = self._form_ui.optionBox.currentText() 103 | 104 | is_keywords = text.lower() == "keywords" 105 | 106 | self._form_ui.bckFile.setEnabled(is_keywords) 107 | self._form_ui.bckFileBtn.setEnabled(is_keywords) 108 | 109 | self._form_ui.malletDir.setEnabled(not is_keywords) 110 | self._form_ui.malletDirBtn.setEnabled(not is_keywords) 111 | 112 | @staticmethod 113 | def _file_dialog_factory(btn, line, selection_type): 114 | def tmp(): 115 | dialog = QFileDialog() 116 | dialog.setFileMode(selection_type) 117 | 118 | dialog.exec() 119 | selected_list = dialog.selectedFiles() 120 | if selected_list: 121 | line.setText(selected_list[0]) 122 | 123 | btn.clicked.connect(tmp) 124 | 125 | def _toggle_advanced(self, checked): 126 | ui = self._form_ui 127 | if checked: 128 | ui.outputDir.show() 129 | ui.outputDirLbl.show() 130 | ui.outputDirBtn.show() 131 | 132 | ui.finalDir.show() 133 | ui.finalDirLbl.show() 134 | ui.finalDirBtn.show() 135 | else: 136 | ui.outputDir.hide() 137 | ui.outputDirLbl.hide() 138 | ui.outputDirBtn.hide() 139 | 140 | ui.finalDir.hide() 141 | ui.finalDirLbl.hide() 142 | ui.finalDirBtn.hide() 143 | 144 | def _get_form_values(self): 145 | ui = self._form_ui 146 | 147 | args = dict() 148 | 149 | args["option"] = ui.optionBox.currentText().lower() 150 | args["input_file"] = ui.inputFile.text() 151 | args["mallet_bin_dir"] = ui.malletDir.text() 152 | args["background_file"] = ui.bckFile.text() 153 | args["group_by"] = ui.groupBox.currentText().lower() 154 | args["prefix"] = ui.prefix.text() 155 | args["num_ideas"] = ui.numIdeas.text() 156 | 157 | args["tokenize"] = ui.tokenizeBox.isChecked() 158 | args["lemmatize"] = ui.lemmatizeBox.isChecked() 159 | args["nostopwords"] = ui.stopwordBox.isChecked() 160 | 161 | output_dir_text = ui.outputDir.text() 162 | if not output_dir_text.strip(): 163 | args["data_output_dir"] = DEFAULT_PROC_DIR.format(exp_name=args["prefix"]) 164 | else: 165 | args["data_output_dir"] = output_dir_text 166 | 167 | final_dir_text = ui.finalDir.text() 168 | if not final_dir_text.strip(): 169 | args["final_output_dir"] = DEFAULT_FINAL_DIR.format(exp_name=args["prefix"]) 170 | else: 171 | args["final_output_dir"] = final_dir_text 172 | 173 | return args 174 | 175 | def _connect_run_ui(self): 176 | ui = self._run_ui 177 | ui.progressBar.hide() 178 | ui.progressBar.setMinimum(0) 179 | ui.progressBar.setMaximum(0) 180 | 181 | ui.runPreprocessor.clicked.connect(self._run_preprocessor) 182 | ui.runPreprocessor.setEnabled(has_preprocessor) 183 | 184 | def _start(self): 185 | valid = self._validate_params(None) 186 | if not valid: 187 | return 188 | 189 | self._run_preprocessor() 190 | 191 | def _validate_params(self, args): 192 | valid = True 193 | 194 | ui = self._form_ui 195 | 196 | invalid_palette = QPalette() 197 | invalid_palette.setColor(QPalette.Active, QPalette.Base, QColor("red")) 198 | 199 | if not os.path.isfile(args["input_file"]): 200 | valid = False 201 | ui.inputFile.setPalette(invalid_palette) 202 | self._set_text_changed_signal(ui.inputFile) 203 | 204 | is_keywords = args["option"] == "keywords" 205 | 206 | if is_keywords and not os.path.isfile(args["background_file"]): 207 | valid = False 208 | ui.bckFile.setPalette(invalid_palette) 209 | self._set_text_changed_signal(ui.bckFile) 210 | 211 | if not is_keywords and not os.path.isdir(args["mallet_bin_dir"]): 212 | valid = False 213 | ui.malletDir.setPalette(invalid_palette) 214 | self._set_text_changed_signal(ui.malletDir) 215 | 216 | if not args["prefix"].strip(): 217 | valid = False 218 | ui.prefix.setPalette(invalid_palette) 219 | self._set_text_changed_signal(ui.prefix) 220 | 221 | try: 222 | int(args["num_ideas"]) 223 | except ValueError: 224 | valid = False 225 | ui.numIdeas.setPalette(invalid_palette) 226 | self._set_text_changed_signal(ui.numIdeas) 227 | 228 | if is_keywords: 229 | data_path = os.path.join("idea_relations", args["data_output_dir"]) 230 | final_path = os.path.join("idea_relations", args["final_output_dir"]) 231 | 232 | if os.path.isdir(data_path): 233 | self._warn_existing_output_dir(data_path) 234 | 235 | if os.path.isdir(final_path): 236 | self._warn_existing_output_dir(final_path) 237 | 238 | return valid 239 | 240 | @staticmethod 241 | def _set_text_changed_signal(field): 242 | def unset_connection(): 243 | field.setPalette(DEFAULT_PALETTE) 244 | field.textEdited.disconnect(unset_connection) 245 | 246 | field.textChanged.connect(unset_connection) 247 | 248 | def _warn_existing_output_dir(self, path): 249 | path = os.path.abspath(path) 250 | reply = QMessageBox.question(self, "Reuse previous data", 251 | "Intermediate files were found at\n{0}\nDo you want to reuse them?".format(path), 252 | QMessageBox.Yes | QMessageBox.No, QMessageBox.No) 253 | reuse = int(reply) == QMessageBox.Yes 254 | if not reuse: 255 | shutil.rmtree(path) 256 | 257 | def _run_preprocessor(self): 258 | values = self._get_form_values() 259 | if not self._validate_params(values): 260 | return 261 | 262 | args = list() 263 | for name, value in values.items(): 264 | if value: 265 | args.append("--" + name) 266 | if isinstance(value, str): 267 | args.append(value) 268 | 269 | args.append("--no_create_graphs") 270 | 271 | output_name = "banana.p" 272 | args.extend(["--objects_location", output_name]) 273 | 274 | if os.name == 'nt': 275 | # args = ["python.exe", "-u", "main.py"] + args 276 | # args = ["python.exe", "--version"] 277 | pass 278 | else: 279 | # args = ["python", "--version"] 280 | # args = ["python", "-u", "main.py"] + args 281 | pass 282 | 283 | # has_preprocessor, idea_rel_main = import_preprocessor() 284 | 285 | if has_preprocessor: 286 | logging.info("Starting preprocessor") 287 | self._preprocessor_thread = threading.Thread(target=self._preprocessor_thread_runner, 288 | args=(args, idea_rel_main.main)) 289 | 290 | self._run_ui.progressBar.show() 291 | self._preprocessor_thread.start() 292 | 293 | self._timer.timeout.connect(self._poll_queue) 294 | self._timer.start() 295 | 296 | self.output_name = output_name 297 | else: 298 | logging.info("Preprocessor was not found on import and won't be run.") 299 | 300 | def _preprocessor_thread_runner(self, args, func): 301 | try: 302 | func(args, self._message_queue) 303 | self._message_queue.put(0) 304 | 305 | except Exception as ex: 306 | logging.error("Exception occurred while running the preprocessor") 307 | logging.exception(ex) 308 | self._message_queue.put(-666) 309 | raise ex 310 | 311 | def _poll_queue(self): 312 | if not self._message_queue.empty(): 313 | msg = self._message_queue.get() 314 | if isinstance(msg, int): 315 | code = msg 316 | else: 317 | code = int(msg.split(':')[1]) 318 | if code <= 0: 319 | self._preprocessor_done(-1 * code) 320 | return 321 | 322 | if self._num_steps is None: 323 | self._num_steps = code 324 | self._run_ui.progressBar.setRange(0, code) 325 | self._run_ui.progressBar.setValue(0) 326 | else: 327 | self._run_ui.progressBar.setValue(code) 328 | 329 | self._timer.start() 330 | else: 331 | self._timer.start() 332 | 333 | def _preprocessor_done(self, rtn_code): 334 | self._run_ui.progressBar.setRange(0, 1) 335 | self._run_ui.progressBar.setValue(0) 336 | 337 | self._num_steps = None 338 | 339 | if rtn_code != 666: 340 | self._run_ui.progressBar.setValue(1) 341 | 342 | if self._callback is not None: 343 | self._callback(rtn_code) 344 | logging.info("Finished preprocessor run with code {0}".format(rtn_code)) 345 | 346 | def kill(self): 347 | if self._preprocessor_thread is not None: 348 | logging.info("Killing preprocessor thread") 349 | self._preprocessor_thread.kill() 350 | -------------------------------------------------------------------------------- /Visualizer/main.py: -------------------------------------------------------------------------------- 1 | # Nikko Rush 2 | # 8/28/2017 3 | 4 | import sys 5 | 6 | import Visualizer 7 | 8 | if __name__ == '__main__': 9 | Visualizer.main() 10 | -------------------------------------------------------------------------------- /Visualizer/menu.py: -------------------------------------------------------------------------------- 1 | # Nikko Rush 2 | # 7/22/17 3 | 4 | import tkinter as tk 5 | import tkinter.filedialog as filedialog 6 | 7 | from menus import pmi_menu 8 | 9 | class Menubar(tk.Menu): 10 | 11 | def __init__(self, root, data): 12 | super(Menubar, self).__init__(master=root) 13 | 14 | self._data = data 15 | 16 | self.filemenu = tk.Menu(master=self, tearoff=0) 17 | self.filemenu.add_command(label="Open", command=self.open) 18 | 19 | self.editmenu = tk.Menu(master=self, tearoff=0) 20 | self.editmenu.add_command(label="Edit PMI", command=self.edit_pmi) 21 | 22 | self.add_cascade(label="File", menu=self.filemenu) 23 | self.add_cascade(label="Edit", menu=self.editmenu) 24 | 25 | self._open_handlers = set() 26 | 27 | def hello(self): 28 | print("hello") 29 | 30 | def open(self): 31 | value = filedialog.askopenfilename(parent=self) 32 | for func in self._open_handlers: 33 | func(value) 34 | 35 | def edit_pmi(self): 36 | pmi_menu.PMIMenu(self, self._data) 37 | -------------------------------------------------------------------------------- /Visualizer/menus/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nwrush/Visualization/bc134aa02156956ea3644b34d491da9f0e9f63ae/Visualizer/menus/__init__.py -------------------------------------------------------------------------------- /Visualizer/menus/pmi_menu.py: -------------------------------------------------------------------------------- 1 | # Nikko Rush 2 | # 7/25/2017 3 | 4 | import tkinter as tk 5 | import tkinter.colorchooser 6 | import tkinter.ttk as ttk 7 | 8 | def rgb_to_luma(r, g, b): 9 | luminance = 0.2126*r + 0.7152*g + 0.0722*b 10 | 11 | def create(parent): 12 | return PMIMenu(parent) 13 | 14 | def create_factory(parent): 15 | def func(): 16 | create(parent) 17 | return func 18 | 19 | class PMIMenu(object): 20 | 21 | def __init__(self, parent): 22 | self._window = tk.Toplevel(master=parent.frame) 23 | self._window.title("PMI Graph Settings") 24 | 25 | self._parent = parent 26 | self._data = parent.data 27 | 28 | self._color_settings = ttk.LabelFrame(master=self._window, text="Colors") 29 | self._color_settings.pack() 30 | 31 | self._colors = [] 32 | for index, relation in enumerate(self._data.relation_types): 33 | label = tk.Label(master=self._color_settings, text="{0} Color".format(relation)) 34 | btn = tk.Button(master=self._color_settings, text="Get Color", command=self._get_color_button(index)) 35 | label.grid(row=index, column=0, sticky="we") 36 | btn.grid(row=index, column=1) 37 | 38 | parent_color = self._parent._colors[index] 39 | self._colors.append([label, btn, parent_color]) # Use a better default here 40 | 41 | self._window.protocol("WM_DELETE_WINDOW", self.on_exit) 42 | 43 | def _get_color_button(self, index): 44 | def func(): 45 | rgb, hex = tk.colorchooser.askcolor(parent=self._window) 46 | self._window.lift() 47 | 48 | self._colors[index][2] = rgb 49 | 50 | sample = self._colors[index][0] 51 | sample.config(background=hex) 52 | 53 | r, g, b = rgb 54 | # https://en.wikipedia.org/wiki/Relative_luminance 55 | # ITU-R BT.709 Primaries 56 | luma = (0.2126*r + 0.7152*g + 0.0722*b)/255 57 | if luma >= 0.5: 58 | sample.config(fg="Black") 59 | else: 60 | sample.config(fg="White") 61 | 62 | return func 63 | 64 | def on_exit(self): 65 | new_colors = [] 66 | for index, color in enumerate(self._colors): 67 | _, _, rgb = color 68 | 69 | new_colors.append(rgb) 70 | 71 | self._window.destroy() 72 | self._parent._update_colors(new_colors) 73 | -------------------------------------------------------------------------------- /Visualizer/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nwrush/Visualization/bc134aa02156956ea3644b34d491da9f0e9f63ae/Visualizer/models/__init__.py -------------------------------------------------------------------------------- /Visualizer/models/list_model.py: -------------------------------------------------------------------------------- 1 | # Nikko Rush 2 | # 9/5/2017 3 | 4 | import PyQt5.QtCore as QtCore 5 | from PyQt5.QtCore import Qt 6 | 7 | 8 | class ListModel(QtCore.QAbstractListModel): 9 | 10 | def __init__(self, data, parent=None): 11 | super(ListModel, self).__init__(parent) 12 | 13 | self._data = sorted(list(data)) 14 | 15 | def rowCount(self, parent=None, *args, **kwargs): 16 | return len(self._data) 17 | 18 | def data(self, index, role=None): 19 | if role == Qt.DisplayRole: 20 | return self._data[index.row()] 21 | return QtCore.QVariant() # Return an invalid variant 22 | 23 | 24 | class ListModelFilter(QtCore.QSortFilterProxyModel): 25 | 26 | def __init__(self, parent=None): 27 | super(ListModelFilter, self).__init__(parent) 28 | -------------------------------------------------------------------------------- /Visualizer/pmi_corr_plot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nwrush/Visualization/bc134aa02156956ea3644b34d491da9f0e9f63ae/Visualizer/pmi_corr_plot.png -------------------------------------------------------------------------------- /Visualizer/qt_test.py: -------------------------------------------------------------------------------- 1 | # Nikko Rush 2 | # 8/1/2017 3 | 4 | import sys 5 | 6 | import PyQt5.QtWidgets as QtWidgets 7 | 8 | if __name__ == "__main__": 9 | app = QtWidgets.QApplication(sys.argv) 10 | 11 | w = QtWidgets.QWidget() 12 | w.resize(250, 150) 13 | w.move(300, 300) 14 | w.setWindowTitle("Test") 15 | 16 | layout = QtWidgets.QHBoxLayout(w) 17 | w.setLayout(layout) 18 | 19 | label = QtWidgets.QLabel("test", w) 20 | layout.addWidget(label) 21 | 22 | w.show() 23 | sys.exit(app.exec_()) -------------------------------------------------------------------------------- /Visualizer/requirements.txt: -------------------------------------------------------------------------------- 1 | cycler>=0.10.0 2 | matplotlib>=2.0.2 3 | numpy>=1.13.0 4 | pyparsing>=2.2.0 5 | python-dateutil>=2.6.0 6 | pytz>=2017.2 7 | six>=1.10.0 8 | PyQt5>=5.9 9 | -------------------------------------------------------------------------------- /Visualizer/setup.py: -------------------------------------------------------------------------------- 1 | # Nikko Rush 2 | # 9/12/2017 3 | 4 | # Run "python setup.py build" to create exe, can't be run from within a virtualenv 5 | 6 | import glob 7 | import os 8 | import os.path 9 | import shutil 10 | import sys 11 | 12 | from cx_Freeze import setup, Executable 13 | 14 | # Create a dist version of idea_relations 15 | if not os.path.isdir("../idea_rel_dist"): 16 | os.mkdir("../idea_rel_dist") 17 | 18 | for file in glob.glob("../idea_relations/*.py") + glob.glob("../idea_relations/*.sh") + glob.glob( 19 | "../idea_relations/*.bat"): 20 | shutil.copy2(file, "../idea_rel_dist/") 21 | 22 | base = None 23 | if sys.platform == "win32": 24 | base = "Win32GUI" 25 | 26 | additional_mods = ['numpy', 'numpy.core._methods', 'numpy.lib.format', 'scipy', 'nltk', 'scipy.interpolate', 'scipy.special'] 27 | 28 | 29 | options = { 30 | 'build': {'build_exe': "../build"}, 31 | 'build_exe': {'includes': additional_mods, 32 | 'include_files': [('../idea_rel_dist', 'idea_relations'), "Vis.ico"] 33 | }, 34 | } 35 | 36 | setup(name="Visualizer", 37 | version="1.0", 38 | description="Visualizer", 39 | options=options, 40 | executables=[Executable("main.py", targetName="Visualizer", base=base, icon="Vis.ico")], 41 | ) 42 | -------------------------------------------------------------------------------- /Visualizer/startup.py: -------------------------------------------------------------------------------- 1 | # Nikko Rush 2 | # 7/12/17 3 | 4 | """ 5 | Run the preprocessor (ask for arguments, etc.) and feed the output into the gui 6 | """ 7 | 8 | import datetime 9 | print(datetime.datetime.now()) 10 | import os 11 | import os.path 12 | import subprocess 13 | import sys 14 | 15 | 16 | # Import matplotlib to ensure the backend it set to tk 17 | import matplotlib 18 | matplotlib.use("TKAgg") 19 | 20 | #from data_processor import main 21 | import Visualizer 22 | 23 | class Namespace: 24 | #def __getattr__(self, name): 25 | # return None 26 | pass 27 | 28 | def read_config_file(fname): 29 | if not os.path.isfile(fname): 30 | print("Error: Couldn't find file {0}".format(fname)) 31 | sys.exit(1) 32 | 33 | f = open(fname, 'r') 34 | 35 | args = [] 36 | for line in f.readlines(): 37 | line = line.strip() 38 | if line == '': 39 | continue 40 | 41 | name, value = line.split('=') 42 | 43 | args.append("--{0}".format(name)) 44 | args.append(value) 45 | 46 | return args 47 | 48 | def run_preprocessor(args): 49 | path = ["python", "main.py"] + args 50 | cwd = "../idea_relations/data_processor" 51 | subprocess.run(path, cwd=cwd) 52 | 53 | print(datetime.datetime.now()) 54 | args = read_config_file("Keywords.config") 55 | 56 | print(datetime.datetime.now()) 57 | run_preprocessor(args) 58 | #args = main.main(args, True) 59 | 60 | print(datetime.datetime.now()) 61 | Visualizer.main("keywords_data.p") 62 | -------------------------------------------------------------------------------- /Visualizer/templates/template.tex: -------------------------------------------------------------------------------- 1 | \documentclass[11pt]{{article}} 2 | \usepackage{{times}} 3 | \usepackage{{latexsym}} 4 | 5 | \usepackage{{amsmath}} 6 | \usepackage{{booktabs}} 7 | \usepackage{{array}} 8 | \usepackage{{multirow}} 9 | \usepackage{{graphicx}} 10 | \usepackage{{color}} 11 | \usepackage{{blindtext}} 12 | \usepackage{{subcaption}} 13 | \usepackage{{enumitem}} 14 | \usepackage{{multirow}} 15 | 16 | \newcommand{{\addFigure}}[2]{{\includegraphics[width=#1]{{#2}}}} 17 | \newcommand{{\para}}[1]{{\noindent{{\bf #1}}}} 18 | \newcommand{{\figref}}[1]{{Fig. \ref{{#1}}}} 19 | \newcommand{{\equationref}}[1]{{Eq. \ref{{#1}}}} 20 | \newcommand{{\secref}}[1]{{\S \ref{{#1}}}} 21 | \newcommand{{\tableref}}[1]{{Table \ref{{#1}}}} 22 | \newcommand{{\ideas}}{{ideas\xspace}} 23 | \newcommand{{\Ideas}}{{Ideas\xspace}} 24 | \newcommand{{\idea}}{{idea\xspace}} 25 | \newcommand{{\Idea}}{{Idea\xspace}} 26 | \newcommand{{\cooccurrence}}{{cooccurrence\xspace}} 27 | \newcommand{{\cooccurrences}}{{cooccurrences\xspace}} 28 | \newcommand{{\cooccur}}{{cooccur\xspace}} 29 | \newcommand{{\cooccurs}}{{cooccurs\xspace}} 30 | \newcommand{{\Cooccurrence}}{{Cooccurrence\xspace}} 31 | \newcommand{{\Cooccurrences}}{{Cooccurrences\xspace}} 32 | \newcommand{{\correlation}}{{prevalence correlation\xspace}} 33 | \newcommand{{\Correlation}}{{Prevalence correlation\xspace}} 34 | \newcommand{{\frequency}}{{prevalence\xspace}} 35 | \newcommand{{\pmi}}{{$\widehat{{\operatorname{{PMI}}}}$\xspace}} 36 | \newcommand{{\freqcorr}}{{$\hat{{r}}$\xspace}} 37 | \newcommand{{\kwords}}{{keywords\xspace}} 38 | \newcommand{{\kword}}{{keyword\xspace}} 39 | \newcommand{{\Kwords}}{{Keywords\xspace}} 40 | \newcommand{{\friends}}{{friendship\xspace}} 41 | \newcommand{{\tryst}}{{tryst\xspace}} 42 | \newcommand{{\armsrace}}{{arms-race\xspace}} 43 | \newcommand{{\headtohead}}{{head-to-head\xspace}} 44 | \newcommand{{\Friends}}{{Friendship\xspace}} 45 | \newcommand{{\Tryst}}{{Tryst\xspace}} 46 | \newcommand{{\Armsrace}}{{Arms-race\xspace}} 47 | \newcommand{{\Headtohead}}{{Head-to-head\xspace}} 48 | 49 | \newcommand{{\ideaname}}[1]{{{{\small\sf #1}}\xspace}} 50 | \newcommand{{\ideapair}}[2]{{(\ideaname{{#1}}, \ideaname{{#2}})}} 51 | \definecolor{{friendscolor}}{{RGB}}{{19, 126, 109}} 52 | \definecolor{{armsracecolor}}{{RGB}}{{68, 142, 228}} 53 | \definecolor{{headtoheadcolor}}{{RGB}}{{152, 0, 2}} 54 | \definecolor{{trystcolor}}{{RGB}}{{207, 98, 117}} 55 | \newcommand{{\formalideas}}{{\mathcal{{I}}}} 56 | 57 | \title{{Idea relations}} 58 | 59 | \author{{}} 60 | \begin{{document}} 61 | \maketitle 62 | \section{{Overall distributions}} 63 | 64 | % \figref{{fig:joint}} shows the joint distributions between PMI and prevalence correlation. 65 | % \figref{{fig:average}} shows the average strength of the top 25 pairs in each category. 66 | \begin{{figure*}}[htb!] 67 | \centering 68 | \begin{{subfigure}}{{0.45\textwidth}} 69 | \addFigure{{\textwidth}}{joint_file} 70 | \caption{{Joint distribution between PMI and prevalence correlation.}} 71 | \label{{fig:joint}} 72 | \end{{subfigure}} 73 | \hfill 74 | \begin{{subfigure}}{{0.45\textwidth}} 75 | \addFigure{{\textwidth}}{average_file} 76 | \caption{{Strength of the top 25 pairs in each category.}} 77 | \label{{fig:average}} 78 | \end{{subfigure}} 79 | \end{{figure*}} 80 | 81 | 82 | 83 | \section{{Top relation plots}} 84 | \begin{{figure*}}[htb!] 85 | \centering 86 | {{\large \textcolor{{friendscolor}}{{\Friends}}}}\\ 87 | \begin{{subfigure}}[t]{{0.22\textwidth}} 88 | \addFigure{{\textwidth}}{friends_1} 89 | \caption{{\Friends (\#1).}} 90 | \end{{subfigure}} 91 | \hfill 92 | \begin{{subfigure}}[t]{{0.22\textwidth}} 93 | \addFigure{{\textwidth}}{friends_2} 94 | \caption{{\Friends (\#2).}} 95 | \end{{subfigure}} 96 | \hfill 97 | \begin{{subfigure}}[t]{{0.22\textwidth}} 98 | \addFigure{{\textwidth}}{friends_3} 99 | \caption{{\Friends (\#3).}} 100 | \end{{subfigure}} 101 | \hfill 102 | \begin{{subfigure}}[t]{{0.22\textwidth}} 103 | \addFigure{{\textwidth}}{friends_4} 104 | \caption{{\Friends (\#4).}} 105 | \end{{subfigure}}\\ 106 | {{\large \textcolor{{headtoheadcolor}}{{\Headtohead}}}}\\ 107 | \begin{{subfigure}}[t]{{0.22\textwidth}} 108 | \addFigure{{\textwidth}}{headtohead_1} 109 | \caption{{\Headtohead (\#1).}} 110 | \end{{subfigure}} 111 | \hfill 112 | \begin{{subfigure}}[t]{{0.22\textwidth}} 113 | \addFigure{{\textwidth}}{headtohead_2} 114 | \caption{{\Headtohead (\#2).}} 115 | \end{{subfigure}} 116 | \hfill 117 | \begin{{subfigure}}[t]{{0.22\textwidth}} 118 | \addFigure{{\textwidth}}{headtohead_3} 119 | \caption{{\Headtohead (\#3).}} 120 | \end{{subfigure}} 121 | \hfill 122 | \begin{{subfigure}}[t]{{0.22\textwidth}} 123 | \addFigure{{\textwidth}}{headtohead_4} 124 | \caption{{\Headtohead (\#4).}} 125 | \end{{subfigure}}\\ 126 | {{\large \textcolor{{armsracecolor}}{{\Armsrace}}}}\\ 127 | \begin{{subfigure}}[t]{{0.22\textwidth}} 128 | \addFigure{{\textwidth}}{armsrace_1} 129 | \caption{{\Armsrace (\#1).}} 130 | \end{{subfigure}} 131 | \hfill 132 | \begin{{subfigure}}[t]{{0.22\textwidth}} 133 | \addFigure{{\textwidth}}{armsrace_2} 134 | \caption{{\Armsrace (\#2).}} 135 | \end{{subfigure}} 136 | \hfill 137 | \begin{{subfigure}}[t]{{0.22\textwidth}} 138 | \addFigure{{\textwidth}}{armsrace_3} 139 | \caption{{\Armsrace (\#3).}} 140 | \end{{subfigure}} 141 | \hfill 142 | \begin{{subfigure}}[t]{{0.22\textwidth}} 143 | \addFigure{{\textwidth}}{armsrace_4} 144 | \caption{{\Armsrace (\#4).}} 145 | \end{{subfigure}}\\ 146 | {{\large \textcolor{{trystcolor}}{{\Tryst}}}}\\ 147 | \begin{{subfigure}}[t]{{0.22\textwidth}} 148 | \addFigure{{\textwidth}}{tryst_1} 149 | \caption{{\Tryst (\#1).}} 150 | \end{{subfigure}} 151 | \hfill 152 | \begin{{subfigure}}[t]{{0.22\textwidth}} 153 | \addFigure{{\textwidth}}{tryst_2} 154 | \caption{{\Tryst (\#2).}} 155 | \end{{subfigure}} 156 | \hfill 157 | \begin{{subfigure}}[t]{{0.22\textwidth}} 158 | \addFigure{{\textwidth}}{tryst_3} 159 | \caption{{\Tryst (\#3).}} 160 | \end{{subfigure}} 161 | \hfill 162 | \begin{{subfigure}}[t]{{0.22\textwidth}} 163 | \addFigure{{\textwidth}}{tryst_4} 164 | \caption{{\Tryst (\#4).}} 165 | \end{{subfigure}} 166 | \caption{{Top four figures in each category, more careful investigation is required to interpret these results. Also explore further down the list (e.g., top 50) to get a better understanding.}} 167 | \label{{fig:top}} 168 | \end{{figure*}} 169 | \clearpage 170 | 171 | \section{{Relation tables}} 172 | \begin{{table*}}[htb!] 173 | \centering 174 | \caption{{Relation between ideas. This table shows top pairs. In the table/ directory, there is another file with top 50 pairs, which is worth further investigation.}} 175 | \label{{tab:top}} 176 | \scriptsize 177 | \input{table_file} 178 | \end{{table*}} 179 | \end{{document}} 180 | 181 | -------------------------------------------------------------------------------- /Visualizer/ui/GeneratePy.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | call ..\venv\Scripts\activate.bat 4 | 5 | for %%f in (*.ui) do ( 6 | 7 | pyuic5 -o "%%~nf.py" "%%~nf.ui" 8 | ) 9 | -------------------------------------------------------------------------------- /Visualizer/ui/GeneratePy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source ../venv/bin/activate 4 | 5 | for f in *.ui 6 | do 7 | filename=$(basename "$f") 8 | filename="${filename%.*}" 9 | pyuic5 -o "$filename.py" $f 10 | done 11 | -------------------------------------------------------------------------------- /Visualizer/ui/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nwrush/Visualization/bc134aa02156956ea3644b34d491da9f0e9f63ae/Visualizer/ui/__init__.py -------------------------------------------------------------------------------- /Visualizer/ui/main_window.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'main_window.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.9 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | from PyQt5 import QtCore, QtGui, QtWidgets 10 | 11 | class Ui_mainWindow(object): 12 | def setupUi(self, mainWindow): 13 | mainWindow.setObjectName("mainWindow") 14 | mainWindow.resize(800, 600) 15 | icon = QtGui.QIcon() 16 | icon.addPixmap(QtGui.QPixmap("Vis.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off) 17 | mainWindow.setWindowIcon(icon) 18 | self.main_widget = QtWidgets.QWidget(mainWindow) 19 | self.main_widget.setObjectName("main_widget") 20 | self.horizontalLayout = QtWidgets.QHBoxLayout(self.main_widget) 21 | self.horizontalLayout.setObjectName("horizontalLayout") 22 | self.tabWidget = QtWidgets.QTabWidget(self.main_widget) 23 | self.tabWidget.setTabsClosable(True) 24 | self.tabWidget.setObjectName("tabWidget") 25 | self.horizontalLayout.addWidget(self.tabWidget) 26 | mainWindow.setCentralWidget(self.main_widget) 27 | self.menubar = QtWidgets.QMenuBar(mainWindow) 28 | self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21)) 29 | self.menubar.setObjectName("menubar") 30 | self.openMenu = QtWidgets.QMenu(self.menubar) 31 | self.openMenu.setEnabled(True) 32 | self.openMenu.setObjectName("openMenu") 33 | self.visualizationMenu = QtWidgets.QMenu(self.menubar) 34 | self.visualizationMenu.setObjectName("visualizationMenu") 35 | mainWindow.setMenuBar(self.menubar) 36 | self.statusbar = QtWidgets.QStatusBar(mainWindow) 37 | self.statusbar.setObjectName("statusbar") 38 | mainWindow.setStatusBar(self.statusbar) 39 | self.actionOpen = QtWidgets.QAction(mainWindow) 40 | self.actionOpen.setVisible(True) 41 | self.actionOpen.setObjectName("actionOpen") 42 | self.actionSave_PMI = QtWidgets.QAction(mainWindow) 43 | self.actionSave_PMI.setObjectName("actionSave_PMI") 44 | self.actionSave_TS = QtWidgets.QAction(mainWindow) 45 | self.actionSave_TS.setObjectName("actionSave_TS") 46 | self.actionSave_Both = QtWidgets.QAction(mainWindow) 47 | self.actionSave_Both.setObjectName("actionSave_Both") 48 | self.actionSave = QtWidgets.QAction(mainWindow) 49 | self.actionSave.setObjectName("actionSave") 50 | self.openMenu.addAction(self.actionOpen) 51 | self.openMenu.addAction(self.actionSave) 52 | self.visualizationMenu.addAction(self.actionSave_PMI) 53 | self.visualizationMenu.addAction(self.actionSave_TS) 54 | self.visualizationMenu.addAction(self.actionSave_Both) 55 | self.menubar.addAction(self.openMenu.menuAction()) 56 | self.menubar.addAction(self.visualizationMenu.menuAction()) 57 | 58 | self.retranslateUi(mainWindow) 59 | self.tabWidget.setCurrentIndex(-1) 60 | QtCore.QMetaObject.connectSlotsByName(mainWindow) 61 | 62 | def retranslateUi(self, mainWindow): 63 | _translate = QtCore.QCoreApplication.translate 64 | mainWindow.setWindowTitle(_translate("mainWindow", "MainWindow")) 65 | self.openMenu.setTitle(_translate("mainWindow", "File")) 66 | self.visualizationMenu.setTitle(_translate("mainWindow", "Visualization")) 67 | self.actionOpen.setText(_translate("mainWindow", "Open")) 68 | self.actionOpen.setShortcut(_translate("mainWindow", "Ctrl+O")) 69 | self.actionSave_PMI.setText(_translate("mainWindow", "Save PMI")) 70 | self.actionSave_TS.setText(_translate("mainWindow", "Save TS")) 71 | self.actionSave_Both.setText(_translate("mainWindow", "Save Both")) 72 | self.actionSave.setText(_translate("mainWindow", "Save")) 73 | self.actionSave.setShortcut(_translate("mainWindow", "Ctrl+S")) 74 | 75 | -------------------------------------------------------------------------------- /Visualizer/ui/main_window.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | mainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 800 10 | 600 11 | 12 | 13 | 14 | MainWindow 15 | 16 | 17 | 18 | Vis.icoVis.ico 19 | 20 | 21 | 22 | 23 | 24 | 25 | -1 26 | 27 | 28 | true 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 0 38 | 0 39 | 800 40 | 21 41 | 42 | 43 | 44 | 45 | true 46 | 47 | 48 | File 49 | 50 | 51 | 52 | 53 | 54 | 55 | Visualization 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | Open 68 | 69 | 70 | Ctrl+O 71 | 72 | 73 | true 74 | 75 | 76 | 77 | 78 | Save PMI 79 | 80 | 81 | 82 | 83 | Save TS 84 | 85 | 86 | 87 | 88 | Save Both 89 | 90 | 91 | 92 | 93 | Save 94 | 95 | 96 | Ctrl+S 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /Visualizer/ui/pmi_control_panel_horiz.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'pmi_control_panel_horiz.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.9 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | from PyQt5 import QtCore, QtGui, QtWidgets 10 | 11 | class Ui_pmi_control_panel(object): 12 | def setupUi(self, pmi_control_panel): 13 | pmi_control_panel.setObjectName("pmi_control_panel") 14 | pmi_control_panel.resize(623, 200) 15 | pmi_control_panel.setMaximumSize(QtCore.QSize(16777215, 200)) 16 | self.horizontalLayout = QtWidgets.QHBoxLayout(pmi_control_panel) 17 | self.horizontalLayout.setObjectName("horizontalLayout") 18 | self.groupBox_2 = QtWidgets.QGroupBox(pmi_control_panel) 19 | self.groupBox_2.setObjectName("groupBox_2") 20 | self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.groupBox_2) 21 | self.verticalLayout_3.setObjectName("verticalLayout_3") 22 | self.filteredList = QtWidgets.QListWidget(self.groupBox_2) 23 | self.filteredList.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) 24 | self.filteredList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) 25 | self.filteredList.setObjectName("filteredList") 26 | self.verticalLayout_3.addWidget(self.filteredList) 27 | self.resetButton = QtWidgets.QPushButton(self.groupBox_2) 28 | self.resetButton.setObjectName("resetButton") 29 | self.verticalLayout_3.addWidget(self.resetButton) 30 | self.horizontalLayout.addWidget(self.groupBox_2) 31 | 32 | self.retranslateUi(pmi_control_panel) 33 | QtCore.QMetaObject.connectSlotsByName(pmi_control_panel) 34 | 35 | def retranslateUi(self, pmi_control_panel): 36 | _translate = QtCore.QCoreApplication.translate 37 | pmi_control_panel.setWindowTitle(_translate("pmi_control_panel", "Form")) 38 | self.groupBox_2.setTitle(_translate("pmi_control_panel", "Filtered By")) 39 | self.resetButton.setText(_translate("pmi_control_panel", "Reset")) 40 | 41 | -------------------------------------------------------------------------------- /Visualizer/ui/pmi_control_panel_horiz.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | pmi_control_panel 4 | 5 | 6 | 7 | 0 8 | 0 9 | 623 10 | 200 11 | 12 | 13 | 14 | 15 | 16777215 16 | 200 17 | 18 | 19 | 20 | Form 21 | 22 | 23 | 24 | 25 | 26 | Filtered By 27 | 28 | 29 | 30 | 31 | 32 | QAbstractItemView::NoEditTriggers 33 | 34 | 35 | QAbstractItemView::NoSelection 36 | 37 | 38 | 39 | 40 | 41 | 42 | Reset 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /Visualizer/ui/pmi_control_panel_vert.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'pmi_control_panel_vert.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.9 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | from PyQt5 import QtCore, QtGui, QtWidgets 10 | 11 | class Ui_pmi_control_panel(object): 12 | def setupUi(self, pmi_control_panel): 13 | pmi_control_panel.setObjectName("pmi_control_panel") 14 | pmi_control_panel.resize(258, 415) 15 | self.verticalLayout = QtWidgets.QVBoxLayout(pmi_control_panel) 16 | self.verticalLayout.setObjectName("verticalLayout") 17 | self.horizontalLayout = QtWidgets.QHBoxLayout() 18 | self.horizontalLayout.setObjectName("horizontalLayout") 19 | self.groupBox = QtWidgets.QGroupBox(pmi_control_panel) 20 | font = QtGui.QFont() 21 | font.setPointSize(10) 22 | self.groupBox.setFont(font) 23 | self.groupBox.setObjectName("groupBox") 24 | self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.groupBox) 25 | self.verticalLayout_4.setObjectName("verticalLayout_4") 26 | self.filteredList = QtWidgets.QListWidget(self.groupBox) 27 | self.filteredList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) 28 | self.filteredList.setWordWrap(False) 29 | self.filteredList.setObjectName("filteredList") 30 | self.verticalLayout_4.addWidget(self.filteredList) 31 | self.horizontalLayout.addWidget(self.groupBox) 32 | self.verticalLayout.addLayout(self.horizontalLayout) 33 | self.resetButton = QtWidgets.QPushButton(pmi_control_panel) 34 | self.resetButton.setObjectName("resetButton") 35 | self.verticalLayout.addWidget(self.resetButton) 36 | self.line = QtWidgets.QFrame(pmi_control_panel) 37 | self.line.setFrameShape(QtWidgets.QFrame.HLine) 38 | self.line.setFrameShadow(QtWidgets.QFrame.Sunken) 39 | self.line.setObjectName("line") 40 | self.verticalLayout.addWidget(self.line) 41 | self.colorLabels = QtWidgets.QGroupBox(pmi_control_panel) 42 | self.colorLabels.setEnabled(True) 43 | self.colorLabels.setFlat(False) 44 | self.colorLabels.setObjectName("colorLabels") 45 | self.gridLayout = QtWidgets.QGridLayout(self.colorLabels) 46 | self.gridLayout.setObjectName("gridLayout") 47 | self.colorLabelsLayout = QtWidgets.QGridLayout() 48 | self.colorLabelsLayout.setObjectName("colorLabelsLayout") 49 | self.headtoheadLabel = QtWidgets.QLabel(self.colorLabels) 50 | font = QtGui.QFont() 51 | font.setPointSize(9) 52 | self.headtoheadLabel.setFont(font) 53 | self.headtoheadLabel.setAutoFillBackground(True) 54 | self.headtoheadLabel.setAlignment(QtCore.Qt.AlignCenter) 55 | self.headtoheadLabel.setObjectName("headtoheadLabel") 56 | self.colorLabelsLayout.addWidget(self.headtoheadLabel, 1, 0, 1, 1) 57 | self.trystLabel = QtWidgets.QLabel(self.colorLabels) 58 | font = QtGui.QFont() 59 | font.setPointSize(9) 60 | self.trystLabel.setFont(font) 61 | self.trystLabel.setAutoFillBackground(True) 62 | self.trystLabel.setAlignment(QtCore.Qt.AlignCenter) 63 | self.trystLabel.setObjectName("trystLabel") 64 | self.colorLabelsLayout.addWidget(self.trystLabel, 0, 0, 1, 1) 65 | self.friendsLabel = QtWidgets.QLabel(self.colorLabels) 66 | font = QtGui.QFont() 67 | font.setPointSize(9) 68 | self.friendsLabel.setFont(font) 69 | self.friendsLabel.setAutoFillBackground(True) 70 | self.friendsLabel.setAlignment(QtCore.Qt.AlignCenter) 71 | self.friendsLabel.setObjectName("friendsLabel") 72 | self.colorLabelsLayout.addWidget(self.friendsLabel, 0, 1, 1, 1) 73 | self.armsraceLabel = QtWidgets.QLabel(self.colorLabels) 74 | font = QtGui.QFont() 75 | font.setPointSize(9) 76 | self.armsraceLabel.setFont(font) 77 | self.armsraceLabel.setAutoFillBackground(True) 78 | self.armsraceLabel.setAlignment(QtCore.Qt.AlignCenter) 79 | self.armsraceLabel.setObjectName("armsraceLabel") 80 | self.colorLabelsLayout.addWidget(self.armsraceLabel, 1, 1, 1, 1) 81 | self.gridLayout.addLayout(self.colorLabelsLayout, 0, 0, 1, 1) 82 | self.verticalLayout.addWidget(self.colorLabels) 83 | 84 | self.retranslateUi(pmi_control_panel) 85 | QtCore.QMetaObject.connectSlotsByName(pmi_control_panel) 86 | 87 | def retranslateUi(self, pmi_control_panel): 88 | _translate = QtCore.QCoreApplication.translate 89 | pmi_control_panel.setWindowTitle(_translate("pmi_control_panel", "Form")) 90 | self.groupBox.setTitle(_translate("pmi_control_panel", "Filtered By")) 91 | self.resetButton.setText(_translate("pmi_control_panel", "Reset")) 92 | self.colorLabels.setTitle(_translate("pmi_control_panel", "Legend")) 93 | self.headtoheadLabel.setText(_translate("pmi_control_panel", "Head-To-Head")) 94 | self.trystLabel.setText(_translate("pmi_control_panel", "Tryst")) 95 | self.friendsLabel.setText(_translate("pmi_control_panel", "Friends")) 96 | self.armsraceLabel.setText(_translate("pmi_control_panel", "Arms-Race")) 97 | 98 | -------------------------------------------------------------------------------- /Visualizer/ui/pmi_control_panel_vert.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | pmi_control_panel 4 | 5 | 6 | 7 | 0 8 | 0 9 | 258 10 | 415 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 10 24 | 25 | 26 | 27 | Filtered By 28 | 29 | 30 | 31 | 32 | 33 | QAbstractItemView::NoSelection 34 | 35 | 36 | false 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | Reset 49 | 50 | 51 | 52 | 53 | 54 | 55 | Qt::Horizontal 56 | 57 | 58 | 59 | 60 | 61 | 62 | true 63 | 64 | 65 | Legend 66 | 67 | 68 | false 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 9 78 | 79 | 80 | 81 | true 82 | 83 | 84 | Head-To-Head 85 | 86 | 87 | Qt::AlignCenter 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 9 96 | 97 | 98 | 99 | true 100 | 101 | 102 | Tryst 103 | 104 | 105 | Qt::AlignCenter 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 9 114 | 115 | 116 | 117 | true 118 | 119 | 120 | Friends 121 | 122 | 123 | Qt::AlignCenter 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 9 132 | 133 | 134 | 135 | true 136 | 137 | 138 | Arms-Race 139 | 140 | 141 | Qt::AlignCenter 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /Visualizer/ui/preprocessor_form.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'preprocessor_form.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.9 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | from PyQt5 import QtCore, QtGui, QtWidgets 10 | 11 | class Ui_preprocessorForm(object): 12 | def setupUi(self, preprocessorForm): 13 | preprocessorForm.setObjectName("preprocessorForm") 14 | preprocessorForm.resize(784, 591) 15 | self.formLayout = QtWidgets.QFormLayout(preprocessorForm) 16 | self.formLayout.setObjectName("formLayout") 17 | self.label = QtWidgets.QLabel(preprocessorForm) 18 | font = QtGui.QFont() 19 | font.setPointSize(10) 20 | self.label.setFont(font) 21 | self.label.setObjectName("label") 22 | self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label) 23 | self.optionBox = QtWidgets.QComboBox(preprocessorForm) 24 | self.optionBox.setObjectName("optionBox") 25 | self.optionBox.addItem("") 26 | self.optionBox.addItem("") 27 | self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.optionBox) 28 | self.label_2 = QtWidgets.QLabel(preprocessorForm) 29 | font = QtGui.QFont() 30 | font.setPointSize(10) 31 | self.label_2.setFont(font) 32 | self.label_2.setObjectName("label_2") 33 | self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_2) 34 | self.horizontalLayout = QtWidgets.QHBoxLayout() 35 | self.horizontalLayout.setObjectName("horizontalLayout") 36 | self.inputFile = QtWidgets.QLineEdit(preprocessorForm) 37 | self.inputFile.setFocusPolicy(QtCore.Qt.StrongFocus) 38 | self.inputFile.setAutoFillBackground(False) 39 | self.inputFile.setStyleSheet("base: rgb(170, 255, 255)") 40 | self.inputFile.setObjectName("inputFile") 41 | self.horizontalLayout.addWidget(self.inputFile) 42 | self.inputFileBtn = QtWidgets.QPushButton(preprocessorForm) 43 | self.inputFileBtn.setFocusPolicy(QtCore.Qt.ClickFocus) 44 | self.inputFileBtn.setObjectName("inputFileBtn") 45 | self.horizontalLayout.addWidget(self.inputFileBtn) 46 | self.formLayout.setLayout(2, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout) 47 | self.label_5 = QtWidgets.QLabel(preprocessorForm) 48 | font = QtGui.QFont() 49 | font.setPointSize(10) 50 | self.label_5.setFont(font) 51 | self.label_5.setObjectName("label_5") 52 | self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.label_5) 53 | self.horizontalLayout_4 = QtWidgets.QHBoxLayout() 54 | self.horizontalLayout_4.setObjectName("horizontalLayout_4") 55 | self.bckFile = QtWidgets.QLineEdit(preprocessorForm) 56 | self.bckFile.setFocusPolicy(QtCore.Qt.StrongFocus) 57 | self.bckFile.setObjectName("bckFile") 58 | self.horizontalLayout_4.addWidget(self.bckFile) 59 | self.bckFileBtn = QtWidgets.QPushButton(preprocessorForm) 60 | self.bckFileBtn.setFocusPolicy(QtCore.Qt.ClickFocus) 61 | self.bckFileBtn.setObjectName("bckFileBtn") 62 | self.horizontalLayout_4.addWidget(self.bckFileBtn) 63 | self.formLayout.setLayout(4, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout_4) 64 | self.label_4 = QtWidgets.QLabel(preprocessorForm) 65 | font = QtGui.QFont() 66 | font.setPointSize(10) 67 | self.label_4.setFont(font) 68 | self.label_4.setObjectName("label_4") 69 | self.formLayout.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.label_4) 70 | self.horizontalLayout_5 = QtWidgets.QHBoxLayout() 71 | self.horizontalLayout_5.setObjectName("horizontalLayout_5") 72 | self.malletDir = QtWidgets.QLineEdit(preprocessorForm) 73 | self.malletDir.setFocusPolicy(QtCore.Qt.StrongFocus) 74 | self.malletDir.setObjectName("malletDir") 75 | self.horizontalLayout_5.addWidget(self.malletDir) 76 | self.malletDirBtn = QtWidgets.QPushButton(preprocessorForm) 77 | self.malletDirBtn.setFocusPolicy(QtCore.Qt.ClickFocus) 78 | self.malletDirBtn.setObjectName("malletDirBtn") 79 | self.horizontalLayout_5.addWidget(self.malletDirBtn) 80 | self.formLayout.setLayout(6, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout_5) 81 | self.label_3 = QtWidgets.QLabel(preprocessorForm) 82 | font = QtGui.QFont() 83 | font.setPointSize(10) 84 | self.label_3.setFont(font) 85 | self.label_3.setObjectName("label_3") 86 | self.formLayout.setWidget(8, QtWidgets.QFormLayout.LabelRole, self.label_3) 87 | self.groupBox = QtWidgets.QComboBox(preprocessorForm) 88 | self.groupBox.setObjectName("groupBox") 89 | self.groupBox.addItem("") 90 | self.groupBox.addItem("") 91 | self.groupBox.addItem("") 92 | self.formLayout.setWidget(8, QtWidgets.QFormLayout.FieldRole, self.groupBox) 93 | self.label_8 = QtWidgets.QLabel(preprocessorForm) 94 | font = QtGui.QFont() 95 | font.setPointSize(10) 96 | self.label_8.setFont(font) 97 | self.label_8.setObjectName("label_8") 98 | self.formLayout.setWidget(10, QtWidgets.QFormLayout.LabelRole, self.label_8) 99 | self.prefix = QtWidgets.QLineEdit(preprocessorForm) 100 | self.prefix.setObjectName("prefix") 101 | self.formLayout.setWidget(10, QtWidgets.QFormLayout.FieldRole, self.prefix) 102 | self.label_9 = QtWidgets.QLabel(preprocessorForm) 103 | font = QtGui.QFont() 104 | font.setPointSize(10) 105 | self.label_9.setFont(font) 106 | self.label_9.setObjectName("label_9") 107 | self.formLayout.setWidget(12, QtWidgets.QFormLayout.LabelRole, self.label_9) 108 | self.numIdeas = QtWidgets.QLineEdit(preprocessorForm) 109 | self.numIdeas.setObjectName("numIdeas") 110 | self.formLayout.setWidget(12, QtWidgets.QFormLayout.FieldRole, self.numIdeas) 111 | self.label_10 = QtWidgets.QLabel(preprocessorForm) 112 | font = QtGui.QFont() 113 | font.setPointSize(10) 114 | self.label_10.setFont(font) 115 | self.label_10.setObjectName("label_10") 116 | self.formLayout.setWidget(14, QtWidgets.QFormLayout.LabelRole, self.label_10) 117 | self.tokenizeBox = QtWidgets.QCheckBox(preprocessorForm) 118 | self.tokenizeBox.setText("") 119 | self.tokenizeBox.setObjectName("tokenizeBox") 120 | self.formLayout.setWidget(14, QtWidgets.QFormLayout.FieldRole, self.tokenizeBox) 121 | self.label_11 = QtWidgets.QLabel(preprocessorForm) 122 | font = QtGui.QFont() 123 | font.setPointSize(10) 124 | self.label_11.setFont(font) 125 | self.label_11.setObjectName("label_11") 126 | self.formLayout.setWidget(15, QtWidgets.QFormLayout.LabelRole, self.label_11) 127 | self.lemmatizeBox = QtWidgets.QCheckBox(preprocessorForm) 128 | self.lemmatizeBox.setText("") 129 | self.lemmatizeBox.setObjectName("lemmatizeBox") 130 | self.formLayout.setWidget(15, QtWidgets.QFormLayout.FieldRole, self.lemmatizeBox) 131 | self.label_12 = QtWidgets.QLabel(preprocessorForm) 132 | font = QtGui.QFont() 133 | font.setPointSize(10) 134 | self.label_12.setFont(font) 135 | self.label_12.setObjectName("label_12") 136 | self.formLayout.setWidget(16, QtWidgets.QFormLayout.LabelRole, self.label_12) 137 | self.stopwordBox = QtWidgets.QCheckBox(preprocessorForm) 138 | self.stopwordBox.setText("") 139 | self.stopwordBox.setObjectName("stopwordBox") 140 | self.formLayout.setWidget(16, QtWidgets.QFormLayout.FieldRole, self.stopwordBox) 141 | self.outputDirLbl = QtWidgets.QLabel(preprocessorForm) 142 | font = QtGui.QFont() 143 | font.setPointSize(10) 144 | self.outputDirLbl.setFont(font) 145 | self.outputDirLbl.setObjectName("outputDirLbl") 146 | self.formLayout.setWidget(19, QtWidgets.QFormLayout.LabelRole, self.outputDirLbl) 147 | self.horizontalLayout_2 = QtWidgets.QHBoxLayout() 148 | self.horizontalLayout_2.setObjectName("horizontalLayout_2") 149 | self.outputDir = QtWidgets.QLineEdit(preprocessorForm) 150 | self.outputDir.setFocusPolicy(QtCore.Qt.StrongFocus) 151 | self.outputDir.setObjectName("outputDir") 152 | self.horizontalLayout_2.addWidget(self.outputDir) 153 | self.outputDirBtn = QtWidgets.QPushButton(preprocessorForm) 154 | self.outputDirBtn.setFocusPolicy(QtCore.Qt.ClickFocus) 155 | self.outputDirBtn.setObjectName("outputDirBtn") 156 | self.horizontalLayout_2.addWidget(self.outputDirBtn) 157 | self.formLayout.setLayout(19, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout_2) 158 | self.horizontalLayout_3 = QtWidgets.QHBoxLayout() 159 | self.horizontalLayout_3.setObjectName("horizontalLayout_3") 160 | self.finalDir = QtWidgets.QLineEdit(preprocessorForm) 161 | self.finalDir.setFocusPolicy(QtCore.Qt.StrongFocus) 162 | self.finalDir.setObjectName("finalDir") 163 | self.horizontalLayout_3.addWidget(self.finalDir) 164 | self.finalDirBtn = QtWidgets.QPushButton(preprocessorForm) 165 | self.finalDirBtn.setFocusPolicy(QtCore.Qt.ClickFocus) 166 | self.finalDirBtn.setObjectName("finalDirBtn") 167 | self.horizontalLayout_3.addWidget(self.finalDirBtn) 168 | self.formLayout.setLayout(20, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout_3) 169 | self.finalDirLbl = QtWidgets.QLabel(preprocessorForm) 170 | font = QtGui.QFont() 171 | font.setPointSize(10) 172 | self.finalDirLbl.setFont(font) 173 | self.finalDirLbl.setObjectName("finalDirLbl") 174 | self.formLayout.setWidget(20, QtWidgets.QFormLayout.LabelRole, self.finalDirLbl) 175 | self.line = QtWidgets.QFrame(preprocessorForm) 176 | self.line.setFrameShape(QtWidgets.QFrame.HLine) 177 | self.line.setFrameShadow(QtWidgets.QFrame.Sunken) 178 | self.line.setObjectName("line") 179 | self.formLayout.setWidget(17, QtWidgets.QFormLayout.SpanningRole, self.line) 180 | self.label_13 = QtWidgets.QLabel(preprocessorForm) 181 | font = QtGui.QFont() 182 | font.setPointSize(10) 183 | self.label_13.setFont(font) 184 | self.label_13.setObjectName("label_13") 185 | self.formLayout.setWidget(18, QtWidgets.QFormLayout.LabelRole, self.label_13) 186 | self.showAdvancedBox = QtWidgets.QCheckBox(preprocessorForm) 187 | self.showAdvancedBox.setText("") 188 | self.showAdvancedBox.setObjectName("showAdvancedBox") 189 | self.formLayout.setWidget(18, QtWidgets.QFormLayout.FieldRole, self.showAdvancedBox) 190 | 191 | self.retranslateUi(preprocessorForm) 192 | QtCore.QMetaObject.connectSlotsByName(preprocessorForm) 193 | preprocessorForm.setTabOrder(self.optionBox, self.inputFile) 194 | preprocessorForm.setTabOrder(self.inputFile, self.bckFile) 195 | preprocessorForm.setTabOrder(self.bckFile, self.malletDir) 196 | preprocessorForm.setTabOrder(self.malletDir, self.groupBox) 197 | preprocessorForm.setTabOrder(self.groupBox, self.prefix) 198 | preprocessorForm.setTabOrder(self.prefix, self.numIdeas) 199 | preprocessorForm.setTabOrder(self.numIdeas, self.tokenizeBox) 200 | preprocessorForm.setTabOrder(self.tokenizeBox, self.lemmatizeBox) 201 | preprocessorForm.setTabOrder(self.lemmatizeBox, self.stopwordBox) 202 | 203 | def retranslateUi(self, preprocessorForm): 204 | _translate = QtCore.QCoreApplication.translate 205 | preprocessorForm.setWindowTitle(_translate("preprocessorForm", "Form")) 206 | self.label.setText(_translate("preprocessorForm", "Option")) 207 | self.optionBox.setItemText(0, _translate("preprocessorForm", "Keywords")) 208 | self.optionBox.setItemText(1, _translate("preprocessorForm", "Topics")) 209 | self.label_2.setText(_translate("preprocessorForm", "Input File")) 210 | self.inputFileBtn.setText(_translate("preprocessorForm", "...")) 211 | self.label_5.setText(_translate("preprocessorForm", "Background File")) 212 | self.bckFileBtn.setText(_translate("preprocessorForm", "...")) 213 | self.label_4.setText(_translate("preprocessorForm", "Mallet Bin Directory")) 214 | self.malletDirBtn.setText(_translate("preprocessorForm", "...")) 215 | self.label_3.setText(_translate("preprocessorForm", "Group By")) 216 | self.groupBox.setItemText(0, _translate("preprocessorForm", "Year")) 217 | self.groupBox.setItemText(1, _translate("preprocessorForm", "Month")) 218 | self.groupBox.setItemText(2, _translate("preprocessorForm", "Quarter")) 219 | self.label_8.setText(_translate("preprocessorForm", "Exploration Name")) 220 | self.label_9.setText(_translate("preprocessorForm", "Number of Ideas")) 221 | self.label_10.setText(_translate("preprocessorForm", "Tokenize")) 222 | self.label_11.setText(_translate("preprocessorForm", "Lemmatize")) 223 | self.label_12.setText(_translate("preprocessorForm", "No Stop Words")) 224 | self.outputDirLbl.setText(_translate("preprocessorForm", "Output Directory")) 225 | self.outputDirBtn.setText(_translate("preprocessorForm", "...")) 226 | self.finalDirBtn.setText(_translate("preprocessorForm", "...")) 227 | self.finalDirLbl.setText(_translate("preprocessorForm", "Final Output Directory")) 228 | self.label_13.setText(_translate("preprocessorForm", "Advanced Options")) 229 | 230 | -------------------------------------------------------------------------------- /Visualizer/ui/preprocessor_form.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | preprocessorForm 4 | 5 | 6 | 7 | 0 8 | 0 9 | 784 10 | 591 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 20 | 21 | 10 22 | 23 | 24 | 25 | Option 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | Keywords 34 | 35 | 36 | 37 | 38 | Topics 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 10 48 | 49 | 50 | 51 | Input File 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | Qt::StrongFocus 61 | 62 | 63 | false 64 | 65 | 66 | base: rgb(170, 255, 255) 67 | 68 | 69 | 70 | 71 | 72 | 73 | Qt::ClickFocus 74 | 75 | 76 | ... 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 10 87 | 88 | 89 | 90 | Background File 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | Qt::StrongFocus 100 | 101 | 102 | 103 | 104 | 105 | 106 | Qt::ClickFocus 107 | 108 | 109 | ... 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 10 120 | 121 | 122 | 123 | Mallet Bin Directory 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | Qt::StrongFocus 133 | 134 | 135 | 136 | 137 | 138 | 139 | Qt::ClickFocus 140 | 141 | 142 | ... 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 10 153 | 154 | 155 | 156 | Group By 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | Year 165 | 166 | 167 | 168 | 169 | Month 170 | 171 | 172 | 173 | 174 | Quarter 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 10 184 | 185 | 186 | 187 | Exploration Name 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 10 199 | 200 | 201 | 202 | Number of Ideas 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 10 214 | 215 | 216 | 217 | Tokenize 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 10 233 | 234 | 235 | 236 | Lemmatize 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 10 252 | 253 | 254 | 255 | No Stop Words 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 10 271 | 272 | 273 | 274 | Output Directory 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | Qt::StrongFocus 284 | 285 | 286 | 287 | 288 | 289 | 290 | Qt::ClickFocus 291 | 292 | 293 | ... 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | Qt::StrongFocus 305 | 306 | 307 | 308 | 309 | 310 | 311 | Qt::ClickFocus 312 | 313 | 314 | ... 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 10 325 | 326 | 327 | 328 | Final Output Directory 329 | 330 | 331 | 332 | 333 | 334 | 335 | Qt::Horizontal 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 10 344 | 345 | 346 | 347 | Advanced Options 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | optionBox 362 | inputFile 363 | bckFile 364 | malletDir 365 | groupBox 366 | prefix 367 | numIdeas 368 | tokenizeBox 369 | lemmatizeBox 370 | stopwordBox 371 | 372 | 373 | 374 | 375 | -------------------------------------------------------------------------------- /Visualizer/ui/preprocessor_run.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'preprocessor_run.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.9 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | from PyQt5 import QtCore, QtGui, QtWidgets 10 | 11 | class Ui_preprocessorRun(object): 12 | def setupUi(self, preprocessorRun): 13 | preprocessorRun.setObjectName("preprocessorRun") 14 | preprocessorRun.resize(414, 87) 15 | self.verticalLayout = QtWidgets.QVBoxLayout(preprocessorRun) 16 | self.verticalLayout.setObjectName("verticalLayout") 17 | self.runPreprocessor = QtWidgets.QPushButton(preprocessorRun) 18 | self.runPreprocessor.setObjectName("runPreprocessor") 19 | self.verticalLayout.addWidget(self.runPreprocessor) 20 | self.progressBar = QtWidgets.QProgressBar(preprocessorRun) 21 | self.progressBar.setEnabled(False) 22 | self.progressBar.setMaximum(0) 23 | self.progressBar.setProperty("value", -1) 24 | self.progressBar.setAlignment(QtCore.Qt.AlignCenter) 25 | self.progressBar.setInvertedAppearance(False) 26 | self.progressBar.setObjectName("progressBar") 27 | self.verticalLayout.addWidget(self.progressBar) 28 | 29 | self.retranslateUi(preprocessorRun) 30 | QtCore.QMetaObject.connectSlotsByName(preprocessorRun) 31 | 32 | def retranslateUi(self, preprocessorRun): 33 | _translate = QtCore.QCoreApplication.translate 34 | preprocessorRun.setWindowTitle(_translate("preprocessorRun", "Form")) 35 | self.runPreprocessor.setText(_translate("preprocessorRun", "Run Preprocessor")) 36 | 37 | -------------------------------------------------------------------------------- /Visualizer/ui/preprocessor_run.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | preprocessorRun 4 | 5 | 6 | 7 | 0 8 | 0 9 | 414 10 | 87 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 20 | Run Preprocessor 21 | 22 | 23 | 24 | 25 | 26 | 27 | false 28 | 29 | 30 | 0 31 | 32 | 33 | -1 34 | 35 | 36 | Qt::AlignCenter 37 | 38 | 39 | false 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Visualizer/ui/relation_types.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'relation_types.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.9 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | from PyQt5 import QtCore, QtGui, QtWidgets 10 | 11 | class Ui_relationTypes(object): 12 | def setupUi(self, relationTypes): 13 | relationTypes.setObjectName("relationTypes") 14 | relationTypes.resize(322, 451) 15 | self.verticalLayout = QtWidgets.QVBoxLayout(relationTypes) 16 | self.verticalLayout.setObjectName("verticalLayout") 17 | self.label = QtWidgets.QLabel(relationTypes) 18 | font = QtGui.QFont() 19 | font.setPointSize(12) 20 | self.label.setFont(font) 21 | self.label.setAlignment(QtCore.Qt.AlignCenter) 22 | self.label.setObjectName("label") 23 | self.verticalLayout.addWidget(self.label) 24 | self.gridLayout = QtWidgets.QGridLayout() 25 | self.gridLayout.setObjectName("gridLayout") 26 | self.friendsButton = QtWidgets.QPushButton(relationTypes) 27 | self.friendsButton.setObjectName("friendsButton") 28 | self.gridLayout.addWidget(self.friendsButton, 0, 0, 1, 1) 29 | self.trystButton = QtWidgets.QPushButton(relationTypes) 30 | self.trystButton.setObjectName("trystButton") 31 | self.gridLayout.addWidget(self.trystButton, 0, 1, 1, 1) 32 | self.headtoheadButton = QtWidgets.QPushButton(relationTypes) 33 | self.headtoheadButton.setObjectName("headtoheadButton") 34 | self.gridLayout.addWidget(self.headtoheadButton, 1, 0, 1, 1) 35 | self.armsraceButton = QtWidgets.QPushButton(relationTypes) 36 | self.armsraceButton.setObjectName("armsraceButton") 37 | self.gridLayout.addWidget(self.armsraceButton, 1, 1, 1, 1) 38 | self.verticalLayout.addLayout(self.gridLayout) 39 | self.horizontalLayout_2 = QtWidgets.QHBoxLayout() 40 | self.horizontalLayout_2.setObjectName("horizontalLayout_2") 41 | self.friendsTable = QtWidgets.QTableWidget(relationTypes) 42 | self.friendsTable.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) 43 | self.friendsTable.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) 44 | self.friendsTable.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) 45 | self.friendsTable.setCornerButtonEnabled(False) 46 | self.friendsTable.setObjectName("friendsTable") 47 | self.friendsTable.setColumnCount(3) 48 | self.friendsTable.setRowCount(0) 49 | item = QtWidgets.QTableWidgetItem() 50 | self.friendsTable.setHorizontalHeaderItem(0, item) 51 | item = QtWidgets.QTableWidgetItem() 52 | self.friendsTable.setHorizontalHeaderItem(1, item) 53 | item = QtWidgets.QTableWidgetItem() 54 | self.friendsTable.setHorizontalHeaderItem(2, item) 55 | self.friendsTable.verticalHeader().setVisible(False) 56 | self.horizontalLayout_2.addWidget(self.friendsTable) 57 | self.armsraceTable = QtWidgets.QTableWidget(relationTypes) 58 | self.armsraceTable.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) 59 | self.armsraceTable.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) 60 | self.armsraceTable.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) 61 | self.armsraceTable.setCornerButtonEnabled(False) 62 | self.armsraceTable.setObjectName("armsraceTable") 63 | self.armsraceTable.setColumnCount(3) 64 | self.armsraceTable.setRowCount(0) 65 | item = QtWidgets.QTableWidgetItem() 66 | self.armsraceTable.setHorizontalHeaderItem(0, item) 67 | item = QtWidgets.QTableWidgetItem() 68 | self.armsraceTable.setHorizontalHeaderItem(1, item) 69 | item = QtWidgets.QTableWidgetItem() 70 | self.armsraceTable.setHorizontalHeaderItem(2, item) 71 | self.armsraceTable.verticalHeader().setVisible(False) 72 | self.horizontalLayout_2.addWidget(self.armsraceTable) 73 | self.headtoheadTable = QtWidgets.QTableWidget(relationTypes) 74 | self.headtoheadTable.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) 75 | self.headtoheadTable.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) 76 | self.headtoheadTable.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) 77 | self.headtoheadTable.setCornerButtonEnabled(False) 78 | self.headtoheadTable.setObjectName("headtoheadTable") 79 | self.headtoheadTable.setColumnCount(3) 80 | self.headtoheadTable.setRowCount(0) 81 | item = QtWidgets.QTableWidgetItem() 82 | self.headtoheadTable.setHorizontalHeaderItem(0, item) 83 | item = QtWidgets.QTableWidgetItem() 84 | self.headtoheadTable.setHorizontalHeaderItem(1, item) 85 | item = QtWidgets.QTableWidgetItem() 86 | self.headtoheadTable.setHorizontalHeaderItem(2, item) 87 | self.headtoheadTable.verticalHeader().setVisible(False) 88 | self.horizontalLayout_2.addWidget(self.headtoheadTable) 89 | self.trystTable = QtWidgets.QTableWidget(relationTypes) 90 | self.trystTable.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) 91 | self.trystTable.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) 92 | self.trystTable.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) 93 | self.trystTable.setCornerButtonEnabled(False) 94 | self.trystTable.setObjectName("trystTable") 95 | self.trystTable.setColumnCount(3) 96 | self.trystTable.setRowCount(0) 97 | item = QtWidgets.QTableWidgetItem() 98 | self.trystTable.setHorizontalHeaderItem(0, item) 99 | item = QtWidgets.QTableWidgetItem() 100 | self.trystTable.setHorizontalHeaderItem(1, item) 101 | item = QtWidgets.QTableWidgetItem() 102 | self.trystTable.setHorizontalHeaderItem(2, item) 103 | self.trystTable.verticalHeader().setVisible(False) 104 | self.horizontalLayout_2.addWidget(self.trystTable) 105 | self.verticalLayout.addLayout(self.horizontalLayout_2) 106 | 107 | self.retranslateUi(relationTypes) 108 | QtCore.QMetaObject.connectSlotsByName(relationTypes) 109 | 110 | def retranslateUi(self, relationTypes): 111 | _translate = QtCore.QCoreApplication.translate 112 | relationTypes.setWindowTitle(_translate("relationTypes", "Form")) 113 | self.label.setText(_translate("relationTypes", "Top Relations")) 114 | self.friendsButton.setText(_translate("relationTypes", "Friends")) 115 | self.trystButton.setText(_translate("relationTypes", "Tryst")) 116 | self.headtoheadButton.setText(_translate("relationTypes", "Head-To-Head")) 117 | self.armsraceButton.setText(_translate("relationTypes", "Arms-Race")) 118 | item = self.friendsTable.horizontalHeaderItem(0) 119 | item.setText(_translate("relationTypes", "Strength")) 120 | item = self.friendsTable.horizontalHeaderItem(1) 121 | item.setText(_translate("relationTypes", "Relation 1")) 122 | item = self.friendsTable.horizontalHeaderItem(2) 123 | item.setText(_translate("relationTypes", "Relation 2")) 124 | item = self.armsraceTable.horizontalHeaderItem(0) 125 | item.setText(_translate("relationTypes", "Strength")) 126 | item = self.armsraceTable.horizontalHeaderItem(1) 127 | item.setText(_translate("relationTypes", "Relation 1")) 128 | item = self.armsraceTable.horizontalHeaderItem(2) 129 | item.setText(_translate("relationTypes", "Relation 2")) 130 | item = self.headtoheadTable.horizontalHeaderItem(0) 131 | item.setText(_translate("relationTypes", "Strength")) 132 | item = self.headtoheadTable.horizontalHeaderItem(1) 133 | item.setText(_translate("relationTypes", "Relation 1")) 134 | item = self.headtoheadTable.horizontalHeaderItem(2) 135 | item.setText(_translate("relationTypes", "Relation 2")) 136 | item = self.trystTable.horizontalHeaderItem(0) 137 | item.setText(_translate("relationTypes", "Strength")) 138 | item = self.trystTable.horizontalHeaderItem(1) 139 | item.setText(_translate("relationTypes", "Relation 1")) 140 | item = self.trystTable.horizontalHeaderItem(2) 141 | item.setText(_translate("relationTypes", "Relation 2")) 142 | 143 | -------------------------------------------------------------------------------- /Visualizer/ui/relation_types.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | relationTypes 4 | 5 | 6 | 7 | 0 8 | 0 9 | 322 10 | 451 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 20 | 21 | 12 22 | 23 | 24 | 25 | Top Relations 26 | 27 | 28 | Qt::AlignCenter 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | Friends 38 | 39 | 40 | 41 | 42 | 43 | 44 | Tryst 45 | 46 | 47 | 48 | 49 | 50 | 51 | Head-To-Head 52 | 53 | 54 | 55 | 56 | 57 | 58 | Arms-Race 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | QAbstractItemView::NoEditTriggers 70 | 71 | 72 | QAbstractItemView::SingleSelection 73 | 74 | 75 | QAbstractItemView::SelectRows 76 | 77 | 78 | false 79 | 80 | 81 | false 82 | 83 | 84 | 85 | Strength 86 | 87 | 88 | 89 | 90 | Relation 1 91 | 92 | 93 | 94 | 95 | Relation 2 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | QAbstractItemView::NoEditTriggers 104 | 105 | 106 | QAbstractItemView::SingleSelection 107 | 108 | 109 | QAbstractItemView::SelectRows 110 | 111 | 112 | false 113 | 114 | 115 | false 116 | 117 | 118 | 119 | Strength 120 | 121 | 122 | 123 | 124 | Relation 1 125 | 126 | 127 | 128 | 129 | Relation 2 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | QAbstractItemView::NoEditTriggers 138 | 139 | 140 | QAbstractItemView::SingleSelection 141 | 142 | 143 | QAbstractItemView::SelectRows 144 | 145 | 146 | false 147 | 148 | 149 | false 150 | 151 | 152 | 153 | Strength 154 | 155 | 156 | 157 | 158 | Relation 1 159 | 160 | 161 | 162 | 163 | Relation 2 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | QAbstractItemView::NoEditTriggers 172 | 173 | 174 | QAbstractItemView::SingleSelection 175 | 176 | 177 | QAbstractItemView::SelectRows 178 | 179 | 180 | false 181 | 182 | 183 | false 184 | 185 | 186 | 187 | Strength 188 | 189 | 190 | 191 | 192 | Relation 1 193 | 194 | 195 | 196 | 197 | Relation 2 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | -------------------------------------------------------------------------------- /Visualizer/ui/relation_types_tabs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'relation_types_tabs.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.9 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | from PyQt5 import QtCore, QtGui, QtWidgets 10 | 11 | class Ui_relationTypes(object): 12 | def setupUi(self, relationTypes): 13 | relationTypes.setObjectName("relationTypes") 14 | relationTypes.setEnabled(True) 15 | relationTypes.resize(494, 373) 16 | self.verticalLayout = QtWidgets.QVBoxLayout(relationTypes) 17 | self.verticalLayout.setObjectName("verticalLayout") 18 | self.label = QtWidgets.QLabel(relationTypes) 19 | font = QtGui.QFont() 20 | font.setPointSize(12) 21 | self.label.setFont(font) 22 | self.label.setAlignment(QtCore.Qt.AlignCenter) 23 | self.label.setObjectName("label") 24 | self.verticalLayout.addWidget(self.label) 25 | self.tabWidget = QtWidgets.QTabWidget(relationTypes) 26 | self.tabWidget.setAutoFillBackground(True) 27 | self.tabWidget.setStyleSheet("QTabBar::tab:selected { text-decoration: underline; }") 28 | self.tabWidget.setTabPosition(QtWidgets.QTabWidget.North) 29 | self.tabWidget.setTabShape(QtWidgets.QTabWidget.Rounded) 30 | self.tabWidget.setObjectName("tabWidget") 31 | self.friendsTab = QtWidgets.QWidget() 32 | self.friendsTab.setObjectName("friendsTab") 33 | self.horizontalLayout = QtWidgets.QHBoxLayout(self.friendsTab) 34 | self.horizontalLayout.setObjectName("horizontalLayout") 35 | self.friendsTable = QtWidgets.QTableWidget(self.friendsTab) 36 | self.friendsTable.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) 37 | self.friendsTable.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) 38 | self.friendsTable.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) 39 | self.friendsTable.setCornerButtonEnabled(False) 40 | self.friendsTable.setObjectName("friendsTable") 41 | self.friendsTable.setColumnCount(3) 42 | self.friendsTable.setRowCount(0) 43 | item = QtWidgets.QTableWidgetItem() 44 | self.friendsTable.setHorizontalHeaderItem(0, item) 45 | item = QtWidgets.QTableWidgetItem() 46 | self.friendsTable.setHorizontalHeaderItem(1, item) 47 | item = QtWidgets.QTableWidgetItem() 48 | self.friendsTable.setHorizontalHeaderItem(2, item) 49 | self.friendsTable.verticalHeader().setVisible(False) 50 | self.horizontalLayout.addWidget(self.friendsTable) 51 | self.tabWidget.addTab(self.friendsTab, "") 52 | self.trystTab = QtWidgets.QWidget() 53 | self.trystTab.setObjectName("trystTab") 54 | self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.trystTab) 55 | self.horizontalLayout_2.setObjectName("horizontalLayout_2") 56 | self.trystTable = QtWidgets.QTableWidget(self.trystTab) 57 | self.trystTable.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) 58 | self.trystTable.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) 59 | self.trystTable.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) 60 | self.trystTable.setCornerButtonEnabled(False) 61 | self.trystTable.setObjectName("trystTable") 62 | self.trystTable.setColumnCount(3) 63 | self.trystTable.setRowCount(0) 64 | item = QtWidgets.QTableWidgetItem() 65 | self.trystTable.setHorizontalHeaderItem(0, item) 66 | item = QtWidgets.QTableWidgetItem() 67 | self.trystTable.setHorizontalHeaderItem(1, item) 68 | item = QtWidgets.QTableWidgetItem() 69 | self.trystTable.setHorizontalHeaderItem(2, item) 70 | self.trystTable.verticalHeader().setVisible(False) 71 | self.horizontalLayout_2.addWidget(self.trystTable) 72 | self.tabWidget.addTab(self.trystTab, "") 73 | self.headtoheadTab = QtWidgets.QWidget() 74 | self.headtoheadTab.setObjectName("headtoheadTab") 75 | self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.headtoheadTab) 76 | self.horizontalLayout_3.setObjectName("horizontalLayout_3") 77 | self.headtoheadTable = QtWidgets.QTableWidget(self.headtoheadTab) 78 | self.headtoheadTable.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) 79 | self.headtoheadTable.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) 80 | self.headtoheadTable.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) 81 | self.headtoheadTable.setCornerButtonEnabled(False) 82 | self.headtoheadTable.setObjectName("headtoheadTable") 83 | self.headtoheadTable.setColumnCount(3) 84 | self.headtoheadTable.setRowCount(0) 85 | item = QtWidgets.QTableWidgetItem() 86 | self.headtoheadTable.setHorizontalHeaderItem(0, item) 87 | item = QtWidgets.QTableWidgetItem() 88 | self.headtoheadTable.setHorizontalHeaderItem(1, item) 89 | item = QtWidgets.QTableWidgetItem() 90 | self.headtoheadTable.setHorizontalHeaderItem(2, item) 91 | self.headtoheadTable.verticalHeader().setVisible(False) 92 | self.horizontalLayout_3.addWidget(self.headtoheadTable) 93 | self.tabWidget.addTab(self.headtoheadTab, "") 94 | self.armsraceTab = QtWidgets.QWidget() 95 | self.armsraceTab.setObjectName("armsraceTab") 96 | self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.armsraceTab) 97 | self.horizontalLayout_4.setObjectName("horizontalLayout_4") 98 | self.armsraceTable = QtWidgets.QTableWidget(self.armsraceTab) 99 | self.armsraceTable.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) 100 | self.armsraceTable.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) 101 | self.armsraceTable.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) 102 | self.armsraceTable.setCornerButtonEnabled(False) 103 | self.armsraceTable.setObjectName("armsraceTable") 104 | self.armsraceTable.setColumnCount(3) 105 | self.armsraceTable.setRowCount(0) 106 | item = QtWidgets.QTableWidgetItem() 107 | self.armsraceTable.setHorizontalHeaderItem(0, item) 108 | item = QtWidgets.QTableWidgetItem() 109 | self.armsraceTable.setHorizontalHeaderItem(1, item) 110 | item = QtWidgets.QTableWidgetItem() 111 | self.armsraceTable.setHorizontalHeaderItem(2, item) 112 | self.armsraceTable.verticalHeader().setVisible(False) 113 | self.horizontalLayout_4.addWidget(self.armsraceTable) 114 | self.tabWidget.addTab(self.armsraceTab, "") 115 | self.verticalLayout.addWidget(self.tabWidget) 116 | 117 | self.retranslateUi(relationTypes) 118 | self.tabWidget.setCurrentIndex(0) 119 | QtCore.QMetaObject.connectSlotsByName(relationTypes) 120 | 121 | def retranslateUi(self, relationTypes): 122 | _translate = QtCore.QCoreApplication.translate 123 | relationTypes.setWindowTitle(_translate("relationTypes", "Form")) 124 | self.label.setText(_translate("relationTypes", "Top Relations")) 125 | item = self.friendsTable.horizontalHeaderItem(0) 126 | item.setText(_translate("relationTypes", "Strength")) 127 | item = self.friendsTable.horizontalHeaderItem(1) 128 | item.setText(_translate("relationTypes", "Relation 1")) 129 | item = self.friendsTable.horizontalHeaderItem(2) 130 | item.setText(_translate("relationTypes", "Relation 2")) 131 | self.tabWidget.setTabText(self.tabWidget.indexOf(self.friendsTab), _translate("relationTypes", "Friends")) 132 | item = self.trystTable.horizontalHeaderItem(0) 133 | item.setText(_translate("relationTypes", "Strength")) 134 | item = self.trystTable.horizontalHeaderItem(1) 135 | item.setText(_translate("relationTypes", "Relation 1")) 136 | item = self.trystTable.horizontalHeaderItem(2) 137 | item.setText(_translate("relationTypes", "Relation 2")) 138 | self.tabWidget.setTabText(self.tabWidget.indexOf(self.trystTab), _translate("relationTypes", "Tryst")) 139 | item = self.headtoheadTable.horizontalHeaderItem(0) 140 | item.setText(_translate("relationTypes", "Strength")) 141 | item = self.headtoheadTable.horizontalHeaderItem(1) 142 | item.setText(_translate("relationTypes", "Relation 1")) 143 | item = self.headtoheadTable.horizontalHeaderItem(2) 144 | item.setText(_translate("relationTypes", "Relation 2")) 145 | self.tabWidget.setTabText(self.tabWidget.indexOf(self.headtoheadTab), _translate("relationTypes", "Head-To-Head")) 146 | item = self.armsraceTable.horizontalHeaderItem(0) 147 | item.setText(_translate("relationTypes", "Strength")) 148 | item = self.armsraceTable.horizontalHeaderItem(1) 149 | item.setText(_translate("relationTypes", "Relation 1")) 150 | item = self.armsraceTable.horizontalHeaderItem(2) 151 | item.setText(_translate("relationTypes", "Relation 2")) 152 | self.tabWidget.setTabText(self.tabWidget.indexOf(self.armsraceTab), _translate("relationTypes", "Arms-Race")) 153 | 154 | -------------------------------------------------------------------------------- /Visualizer/ui/relation_types_tabs.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | relationTypes 4 | 5 | 6 | true 7 | 8 | 9 | 10 | 0 11 | 0 12 | 494 13 | 373 14 | 15 | 16 | 17 | Form 18 | 19 | 20 | 21 | 22 | 23 | 24 | 12 25 | 26 | 27 | 28 | Top Relations 29 | 30 | 31 | Qt::AlignCenter 32 | 33 | 34 | 35 | 36 | 37 | 38 | true 39 | 40 | 41 | QTabBar::tab:selected { text-decoration: underline; } 42 | 43 | 44 | QTabWidget::North 45 | 46 | 47 | QTabWidget::Rounded 48 | 49 | 50 | 0 51 | 52 | 53 | 54 | Friends 55 | 56 | 57 | 58 | 59 | 60 | QAbstractItemView::NoEditTriggers 61 | 62 | 63 | QAbstractItemView::SingleSelection 64 | 65 | 66 | QAbstractItemView::SelectRows 67 | 68 | 69 | false 70 | 71 | 72 | false 73 | 74 | 75 | 76 | Strength 77 | 78 | 79 | 80 | 81 | Relation 1 82 | 83 | 84 | 85 | 86 | Relation 2 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | Tryst 96 | 97 | 98 | 99 | 100 | 101 | QAbstractItemView::NoEditTriggers 102 | 103 | 104 | QAbstractItemView::SingleSelection 105 | 106 | 107 | QAbstractItemView::SelectRows 108 | 109 | 110 | false 111 | 112 | 113 | false 114 | 115 | 116 | 117 | Strength 118 | 119 | 120 | 121 | 122 | Relation 1 123 | 124 | 125 | 126 | 127 | Relation 2 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | Head-To-Head 137 | 138 | 139 | 140 | 141 | 142 | QAbstractItemView::NoEditTriggers 143 | 144 | 145 | QAbstractItemView::SingleSelection 146 | 147 | 148 | QAbstractItemView::SelectRows 149 | 150 | 151 | false 152 | 153 | 154 | false 155 | 156 | 157 | 158 | Strength 159 | 160 | 161 | 162 | 163 | Relation 1 164 | 165 | 166 | 167 | 168 | Relation 2 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | Arms-Race 178 | 179 | 180 | 181 | 182 | 183 | QAbstractItemView::NoEditTriggers 184 | 185 | 186 | QAbstractItemView::SingleSelection 187 | 188 | 189 | QAbstractItemView::SelectRows 190 | 191 | 192 | false 193 | 194 | 195 | false 196 | 197 | 198 | 199 | Strength 200 | 201 | 202 | 203 | 204 | Relation 1 205 | 206 | 207 | 208 | 209 | Relation 2 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | -------------------------------------------------------------------------------- /Visualizer/ui/top_relations.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'top_relations.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.9 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | from PyQt5 import QtCore, QtGui, QtWidgets 10 | 11 | class Ui_topRelation(object): 12 | def setupUi(self, topRelation): 13 | topRelation.setObjectName("topRelation") 14 | topRelation.resize(324, 300) 15 | self.verticalLayout = QtWidgets.QVBoxLayout(topRelation) 16 | self.verticalLayout.setObjectName("verticalLayout") 17 | self.horizontalLayout = QtWidgets.QHBoxLayout() 18 | self.horizontalLayout.setObjectName("horizontalLayout") 19 | self.staticLabel = QtWidgets.QLabel(topRelation) 20 | font = QtGui.QFont() 21 | font.setPointSize(12) 22 | self.staticLabel.setFont(font) 23 | self.staticLabel.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) 24 | self.staticLabel.setObjectName("staticLabel") 25 | self.horizontalLayout.addWidget(self.staticLabel) 26 | self.relationName = QtWidgets.QLabel(topRelation) 27 | font = QtGui.QFont() 28 | font.setPointSize(12) 29 | self.relationName.setFont(font) 30 | self.relationName.setText("") 31 | self.relationName.setScaledContents(False) 32 | self.relationName.setObjectName("relationName") 33 | self.horizontalLayout.addWidget(self.relationName) 34 | self.verticalLayout.addLayout(self.horizontalLayout) 35 | self.tableWidget = QtWidgets.QTableWidget(topRelation) 36 | self.tableWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) 37 | self.tableWidget.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) 38 | self.tableWidget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) 39 | self.tableWidget.setObjectName("tableWidget") 40 | self.tableWidget.setColumnCount(3) 41 | self.tableWidget.setRowCount(0) 42 | item = QtWidgets.QTableWidgetItem() 43 | self.tableWidget.setHorizontalHeaderItem(0, item) 44 | item = QtWidgets.QTableWidgetItem() 45 | self.tableWidget.setHorizontalHeaderItem(1, item) 46 | item = QtWidgets.QTableWidgetItem() 47 | self.tableWidget.setHorizontalHeaderItem(2, item) 48 | self.tableWidget.verticalHeader().setVisible(False) 49 | self.verticalLayout.addWidget(self.tableWidget) 50 | 51 | self.retranslateUi(topRelation) 52 | QtCore.QMetaObject.connectSlotsByName(topRelation) 53 | 54 | def retranslateUi(self, topRelation): 55 | _translate = QtCore.QCoreApplication.translate 56 | topRelation.setWindowTitle(_translate("topRelation", "Form")) 57 | self.staticLabel.setText(_translate("topRelation", "Selected Relation:")) 58 | item = self.tableWidget.horizontalHeaderItem(0) 59 | item.setText(_translate("topRelation", "Strength")) 60 | item = self.tableWidget.horizontalHeaderItem(1) 61 | item.setText(_translate("topRelation", "Relation Type")) 62 | item = self.tableWidget.horizontalHeaderItem(2) 63 | item.setText(_translate("topRelation", "Relation 2")) 64 | 65 | -------------------------------------------------------------------------------- /Visualizer/ui/top_relations.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | topRelation 4 | 5 | 6 | 7 | 0 8 | 0 9 | 324 10 | 300 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 12 24 | 25 | 26 | 27 | Selected Relation: 28 | 29 | 30 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 12 39 | 40 | 41 | 42 | 43 | 44 | 45 | false 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | QAbstractItemView::NoEditTriggers 55 | 56 | 57 | QAbstractItemView::SingleSelection 58 | 59 | 60 | QAbstractItemView::SelectRows 61 | 62 | 63 | false 64 | 65 | 66 | 67 | Strength 68 | 69 | 70 | 71 | 72 | Relation Type 73 | 74 | 75 | 76 | 77 | Relation 2 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /Visualizer/ui/topics_list.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'topics_list.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.9 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | from PyQt5 import QtCore, QtGui, QtWidgets 10 | 11 | class Ui_topicsList(object): 12 | def setupUi(self, topicsList): 13 | topicsList.setObjectName("topicsList") 14 | topicsList.resize(642, 730) 15 | self.verticalLayout = QtWidgets.QVBoxLayout(topicsList) 16 | self.verticalLayout.setObjectName("verticalLayout") 17 | self.label = QtWidgets.QLabel(topicsList) 18 | font = QtGui.QFont() 19 | font.setPointSize(12) 20 | self.label.setFont(font) 21 | self.label.setAlignment(QtCore.Qt.AlignCenter) 22 | self.label.setObjectName("label") 23 | self.verticalLayout.addWidget(self.label) 24 | self.filterText = QtWidgets.QLineEdit(topicsList) 25 | self.filterText.setObjectName("filterText") 26 | self.verticalLayout.addWidget(self.filterText) 27 | self.listWidget = QtWidgets.QListView(topicsList) 28 | self.listWidget.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) 29 | self.listWidget.setObjectName("listWidget") 30 | self.verticalLayout.addWidget(self.listWidget) 31 | 32 | self.retranslateUi(topicsList) 33 | QtCore.QMetaObject.connectSlotsByName(topicsList) 34 | 35 | def retranslateUi(self, topicsList): 36 | _translate = QtCore.QCoreApplication.translate 37 | topicsList.setWindowTitle(_translate("topicsList", "Form")) 38 | self.label.setText(_translate("topicsList", "Topics")) 39 | self.filterText.setPlaceholderText(_translate("topicsList", "Filter Topics")) 40 | 41 | -------------------------------------------------------------------------------- /Visualizer/ui/topics_list.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | topicsList 4 | 5 | 6 | 7 | 0 8 | 0 9 | 642 10 | 730 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 20 | 21 | 12 22 | 23 | 24 | 25 | Topics 26 | 27 | 28 | Qt::AlignCenter 29 | 30 | 31 | 32 | 33 | 34 | 35 | Filter Topics 36 | 37 | 38 | 39 | 40 | 41 | 42 | QAbstractItemView::ExtendedSelection 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /Visualizer/ui/visualizer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'visualizer.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.9 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | from PyQt5 import QtCore, QtGui, QtWidgets 10 | 11 | class Ui_visualizaerWidget(object): 12 | def setupUi(self, visualizaerWidget): 13 | visualizaerWidget.setObjectName("visualizaerWidget") 14 | visualizaerWidget.resize(1024, 657) 15 | self.horizontalLayout = QtWidgets.QHBoxLayout(visualizaerWidget) 16 | self.horizontalLayout.setObjectName("horizontalLayout") 17 | self.tabWidget = QtWidgets.QTabWidget(visualizaerWidget) 18 | self.tabWidget.setMinimumSize(QtCore.QSize(0, 0)) 19 | self.tabWidget.setMaximumSize(QtCore.QSize(400, 16777215)) 20 | self.tabWidget.setTabPosition(QtWidgets.QTabWidget.South) 21 | self.tabWidget.setObjectName("tabWidget") 22 | self.horizontalLayout.addWidget(self.tabWidget) 23 | self.pmiWidget = QtWidgets.QWidget(visualizaerWidget) 24 | self.pmiWidget.setObjectName("pmiWidget") 25 | self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.pmiWidget) 26 | self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0) 27 | self.horizontalLayout_4.setObjectName("horizontalLayout_4") 28 | self.horizontalLayout.addWidget(self.pmiWidget) 29 | self.tsWidget = QtWidgets.QWidget(visualizaerWidget) 30 | self.tsWidget.setObjectName("tsWidget") 31 | self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.tsWidget) 32 | self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) 33 | self.horizontalLayout_2.setObjectName("horizontalLayout_2") 34 | self.horizontalLayout.addWidget(self.tsWidget) 35 | 36 | self.retranslateUi(visualizaerWidget) 37 | QtCore.QMetaObject.connectSlotsByName(visualizaerWidget) 38 | 39 | def retranslateUi(self, visualizaerWidget): 40 | _translate = QtCore.QCoreApplication.translate 41 | visualizaerWidget.setWindowTitle(_translate("visualizaerWidget", "Form")) 42 | 43 | -------------------------------------------------------------------------------- /Visualizer/ui/visualizer.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | visualizaerWidget 4 | 5 | 6 | 7 | 0 8 | 0 9 | 1024 10 | 657 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 20 | 21 | 0 22 | 0 23 | 24 | 25 | 26 | 27 | 400 28 | 16777215 29 | 30 | 31 | 32 | QTabWidget::South 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /Visualizer/widgets/ColoredBox.py: -------------------------------------------------------------------------------- 1 | # Nikko Rush 2 | # 7/6/2017 3 | 4 | import random 5 | 6 | import tkinter as tk 7 | 8 | 9 | used_colors = set() 10 | def get_color(): 11 | r = random.randint(0, 255) 12 | g = random.randint(0, 255) 13 | b = random.randint(0, 255) 14 | 15 | color = "#{:02x}{:02x}{:02x}".format(r, g, b) 16 | 17 | if color not in used_colors: 18 | used_colors.add(color) 19 | return color 20 | else: 21 | return get_color() 22 | 23 | class ColoredBox(object): 24 | """ 25 | Randomly colored frame 26 | Default width/height of 100 27 | """ 28 | 29 | def __init__(self, master, width=100, height=100): 30 | self.frame = tk.Frame(master=master, background=get_color(), width=width, height=height) 31 | 32 | def pack(self, **kwargs): 33 | self.frame.pack(**kwargs) 34 | 35 | def grid(self, **kwargs): 36 | self.frame.grid(**kwargs) 37 | 38 | def set_color(self, color): 39 | self.frame.configure(background=color) 40 | 41 | if __name__ == "__main__": 42 | root = tk.Tk() 43 | box = ColoredBox(root) 44 | box.pack(fill=tk.X) 45 | 46 | root.mainloop() 47 | 48 | -------------------------------------------------------------------------------- /Visualizer/widgets/ListBoxColumn.py: -------------------------------------------------------------------------------- 1 | # Nikko Rush 2 | # 7/8/2017 3 | 4 | import functools 5 | import sys 6 | 7 | windows, linux, osx = False, False, False 8 | if sys.platform == 'win32': 9 | windows = True 10 | elif sys.platform == 'linux': 11 | linux = True # Assume that a display exists, cause if one doesn't exist you should have crashed a long time ago 12 | elif sys.platform == 'darwin': 13 | osx = True 14 | else: 15 | print("Error: Unknown system platform type") 16 | 17 | import tkinter as tk 18 | 19 | class ListBoxColumn(tk.Frame): 20 | """Works more or less like a listbox, but with columns""" 21 | def __init__(self, ncolumns=1, master=None, cnf={}, listboxkwargs={}, **kw): 22 | super(ListBoxColumn, self).__init__(master=master, cnf=cnf, **kw); 23 | 24 | self.list = tk.Listbox() 25 | 26 | self.ncolumns = ncolumns 27 | self.lists = [tk.Listbox(self, exportselection=False, **listboxkwargs) for i in range(ncolumns)] 28 | 29 | for listBox in self.lists: 30 | listBox.pack(side=tk.LEFT, fill=tk.Y, expand=1) 31 | 32 | listBox.bind("<>", self._on_select) 33 | if windows or osx: 34 | listBox.bind("", self._yscroll) 35 | else: 36 | listBox.bind("", functools.partial(self._yscroll, direction=-1)) 37 | listBox.bind("", functools.partial(self._yscroll, direction=1)) 38 | 39 | 40 | self.xscrollbar = tk.Scrollbar(self, orient=tk.VERTICAL) 41 | self.lists[0].config(xscrollcommand=self.xscrollbar.set) 42 | self.xscrollbar.config(command=self._xscroll) 43 | 44 | self.yscrollbar = tk.Scrollbar(self, orient=tk.VERTICAL) 45 | self.lists[0].config(yscrollcommand=self.yscrollbar.set) 46 | self.yscrollbar.config(command=self._yscroll) 47 | 48 | self.select_handlers = list() 49 | 50 | self.add_select_handler(self._sync_selection) 51 | 52 | def show_xscrollbar(self): 53 | self.xscrollbar.pack(side=tk.BOTTOM, fill=tk.BOTH, expand=1) 54 | 55 | def show_yscrollbar(self): 56 | self.yscrollbar.pack(side=tk.LEFT, fill=tk.BOTH, expand=1) 57 | 58 | def hide_xscrollbar(self): 59 | self._remove_scrollbar(self.xscrollbar) 60 | 61 | def hide_yscrollbar(self): 62 | self._remove_scrollbar(self.yscrollbar) 63 | 64 | def _remove_scrollbar(self, scrollbar): 65 | scrollbar.pack_forget() 66 | 67 | 68 | def activate(self, index): 69 | for listBox in self.lists: 70 | listBox.activate(index) 71 | 72 | def bbox(self, index): 73 | raise NotImplementedError 74 | 75 | def config(self, **options): 76 | for listBox in self.lists: 77 | listBox.config(**options) 78 | 79 | def curselection(self): 80 | return self.lists[0].curselection() 81 | 82 | def delete(self, first, last=None): 83 | for listBox in self.lists: 84 | listBox.delete(first, last) 85 | 86 | def get(self, first, last=None): 87 | for listBox in self.lists: 88 | listBox.get(first, last) 89 | 90 | def index(self, index): 91 | return self.lists[0].index(index) 92 | 93 | def insert(self, index, *elements): 94 | """ 95 | Elements should be an iterable with atleast self.ncolumns items 96 | If an element that you try to add has fewer than self.ncolumns items it will be padded with empty strings 97 | """ 98 | for element in elements: 99 | adding = list(element) + [' '] * (self.ncolumns - len(element)) 100 | for item, listBox in zip(adding, self.lists): 101 | listBox.insert(index, item) 102 | 103 | def delete(self, first, last=None): 104 | for list in self.lists: 105 | list.delete(first, last) 106 | 107 | def itemcget(self, index, option): 108 | return self.lists[0].itemcget(index, option) 109 | 110 | def nearest(self, y): 111 | return self.lists[0].nearest(y) 112 | 113 | def scan_dragto(self, x, y): 114 | raise NotImplementedError 115 | 116 | def see(self, index): 117 | for listBox in self.lists: 118 | listBox.see(index) 119 | 120 | def selection_anchor(self, index): 121 | for listBox in self.lists: 122 | listBox.select_anchor(index) 123 | 124 | def selection_clear(self, first, last=None): 125 | for listBox in self.lists: 126 | listBox.select_clear(first, last) 127 | 128 | def selection_includes(self, index): 129 | return self.lists[0].select_includes(index) 130 | 131 | def selection_set(self, first, last=None): 132 | for listBox in self.lists: 133 | listBox.select_set(first, last) 134 | 135 | select_anchor = selection_anchor 136 | select_clear = selection_clear 137 | select_includes = selection_includes 138 | select_set = selection_set 139 | 140 | def size(self): 141 | return self.lists[0].size() 142 | 143 | def xview(self, column, *extra): 144 | for listBox in self.lists: 145 | listBox.xview(column, *extra) 146 | 147 | def xview_moveto(self, fraction): 148 | for listBox in self.lists: 149 | listBox.xview_moveto(fraction) 150 | 151 | def xview_scroll(self, number, what): 152 | for listBox in self.lists: 153 | listBox.xview_scroll(number, what) 154 | return "break" 155 | 156 | def yview(self, *what): 157 | print("YView: " + str(what)) 158 | for listBox in self.lists: 159 | listBox.yview(*what) 160 | 161 | def yview_moveto(self, fraction): 162 | for listBox in self.lists: 163 | listBox.yview_moveto(fraction) 164 | 165 | def yview_scroll(self, number, what): 166 | for listBox in self.lists: 167 | listBox.yview_scroll(number, what) 168 | return "break" 169 | 170 | def bind(self, sequence=None, func=None, add=None): 171 | def handler(event): 172 | event.ListBoxColumn = self 173 | func(event) 174 | 175 | for listBox in self.lists: 176 | listBox.bind(sequence, handler) 177 | 178 | def add_select_handler(self, handler): 179 | self.select_handlers.append(handler) 180 | 181 | def remove_select_handler(self, handler): 182 | self.select_handlers.remove(handler) 183 | 184 | def _on_select(self, event): 185 | for func in self.select_handlers: 186 | event.ListBoxColumn = self 187 | func(event) 188 | 189 | def _sync_selection(self, event): 190 | selected = event.widget.curselection() 191 | 192 | self.select_clear(0, tk.END) 193 | for index in selected: 194 | self.select_set(index) 195 | 196 | def set_width(self): 197 | for listBox in self.lists: 198 | self._update_width(listBox) 199 | 200 | def _update_width(self, listBox): 201 | max_width = -1 202 | for item in listBox.get(0, tk.END): 203 | if len(str(item)) > max_width: 204 | max_width = len(str(item)) 205 | 206 | listBox.config(width=max_width) 207 | 208 | def _tmp(self, *args): 209 | return self._yscroll(args[0]) 210 | 211 | def _yscroll(self, event, direction=None): 212 | if windows: 213 | return self.yview_scroll(int(event.delta / -120), "units") 214 | elif linux: 215 | return self.yview_scroll(direction, "units") 216 | elif osx: 217 | return self.yview_scroll(int(event.delta), "units") 218 | 219 | 220 | def _xscroll(self, event): 221 | return self.xview_scroll(int(event.delta/-120), "units") -------------------------------------------------------------------------------- /Visualizer/widgets/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nwrush/Visualization/bc134aa02156956ea3644b34d491da9f0e9f63ae/Visualizer/widgets/__init__.py -------------------------------------------------------------------------------- /examples/acl.jsonlist.gz: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:c5a150926e109703bd963c5ef152e772e629aa833b55a731a6e2f8c4c5674b92 3 | size 55069109 4 | -------------------------------------------------------------------------------- /examples/acl.p: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nwrush/Visualization/bc134aa02156956ea3644b34d491da9f0e9f63ae/examples/acl.p -------------------------------------------------------------------------------- /examples/nips.jsonlist.gz: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:7b95da8a5cd76a734a33daca528f76f6f0ba77e7f956c8446944125969e2b2e8 3 | size 61624904 4 | -------------------------------------------------------------------------------- /examples/nips_t.p: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nwrush/Visualization/bc134aa02156956ea3644b34d491da9f0e9f63ae/examples/nips_t.p --------------------------------------------------------------------------------