From 490a50c08f6e1beb7c21d0a2ab5ae57086b1f0be Mon Sep 17 00:00:00 2001 From: BLKSerene Date: Thu, 20 Jun 2024 21:25:43 +0800 Subject: [PATCH] File Area: Display warning when opening non-text files; Menu: Update File - Open Files; Settings: Add Settings - Files - Miscellaneous Settings - Display warning when opening non-text files --- CHANGELOG.md | 2 + doc/doc.md | 360 ++++++++--------- tests/test_main.py | 2 +- tests/tests_dialogs/test_dialogs.py | 2 +- tests/tests_dialogs/test_dialogs_misc.py | 8 - tests/tests_nlp/tests_spacy/test_spacy.py | 7 +- tests/tests_nlp/tests_stanza/test_stanza.py | 14 +- tests/tests_results/test_results_filter.py | 16 +- tests/tests_results/test_results_search.py | 4 +- tests/tests_results/test_results_sort.py | 6 +- tests/wl_test_init.py | 9 +- wordless/wl_colligation_extractor.py | 14 +- wordless/wl_collocation_extractor.py | 14 +- wordless/wl_concordancer.py | 24 +- wordless/wl_concordancer_parallel.py | 14 +- wordless/wl_dependency_parser.py | 14 +- wordless/wl_dialogs/wl_dialogs.py | 79 +++- wordless/wl_dialogs/wl_dialogs_errs.py | 42 +- wordless/wl_dialogs/wl_dialogs_misc.py | 85 +--- wordless/wl_dialogs/wl_msg_boxes.py | 35 +- wordless/wl_file_area.py | 135 +++++-- wordless/wl_keyword_extractor.py | 7 +- wordless/wl_main.py | 362 +++++++++--------- .../wl_measures/wl_measures_readability.py | 8 +- wordless/wl_ngram_generator.py | 14 +- wordless/wl_nlp/wl_lemmatization.py | 7 +- wordless/wl_nlp/wl_nlp_utils.py | 7 +- wordless/wl_nlp/wl_token_processing.py | 9 +- wordless/wl_profiler.py | 22 +- wordless/wl_results/wl_results_search.py | 12 +- wordless/wl_results/wl_results_sort.py | 39 +- wordless/wl_settings/wl_settings.py | 12 +- wordless/wl_settings/wl_settings_default.py | 3 +- .../wl_settings_dependency_parsing.py | 8 +- wordless/wl_settings/wl_settings_figs.py | 8 +- wordless/wl_settings/wl_settings_files.py | 27 +- wordless/wl_settings/wl_settings_general.py | 17 +- wordless/wl_settings/wl_settings_global.py | 37 +- .../wl_settings/wl_settings_lemmatization.py | 23 +- .../wl_settings/wl_settings_pos_tagging.py | 17 +- .../wl_settings_sentence_tokenization.py | 14 +- .../wl_settings_sentiment_analysis.py | 14 +- .../wl_settings_stop_word_lists.py | 8 +- .../wl_settings_syl_tokenization.py | 23 +- .../wl_settings_word_tokenization.py | 14 +- wordless/wl_utils/wl_threading.py | 7 +- wordless/wl_widgets/wl_boxes.py | 6 +- wordless/wl_widgets/wl_buttons.py | 7 +- wordless/wl_widgets/wl_labels.py | 12 +- wordless/wl_widgets/wl_layouts.py | 11 +- wordless/wl_widgets/wl_lists.py | 13 +- wordless/wl_widgets/wl_tables.py | 33 +- wordless/wl_widgets/wl_widgets.py | 18 +- wordless/wl_wordlist_generator.py | 14 +- 54 files changed, 1078 insertions(+), 641 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d1b2c55e2..54d53fcd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ ### πŸŽ‰ New Features - File Area: Add support for .lrc and .pptx files - Measures: Add lexical density/diversity - BrunΓ©t's Index / HonorΓ©'s statistic / Lexical Density +- Settings: Add Settings - Files - Miscellaneous Settings - Display warning when opening non-text files - Settings: Add Settings - Stop Word Lists - Stop Word List Settings - Case-sensitive - Settings: Add Settings - Tables - Dependency Parser - Utils: Add encoding detection - UTF-8 with BOM @@ -37,6 +38,7 @@ - Work Area: Add Profiler - Lexical Density/Diversity - BrunΓ©t's Index / HonorΓ©'s statistic / Lexical Density ### ✨ Improvements +- File Area: Display warning when opening non-text files - Measures: Update readability - OSMAN - Settings: Settings - Part-of-speeach Tagging - Tagsets - Mapping Settings - Allow editing of tagset mapping of spaCy's Catalan, Danish, French, Greek (Modern), Macedonian, Norwegian (BokmΓ₯l), Portuguese, Russian, Spanish, and Ukrainian part-of-speech taggers - Settings: Settings - Part-of-speeach Tagging - Tagsets - Mapping Settings - Allow editing of tagset mapping of Stanza's Armenian (Eastern), Armenian (Western), Basque, Buryat (Russia), Danish, French, Greek (Modern), Hebrew (Modern), Hungarian, Ligurian, Manx, Marathi, Nigerian Pidgin, Pomak, Portuguese, Russian, Sanskrit, Sindhi, Sorbian (Upper), and Telugu part-of-speech taggers diff --git a/doc/doc.md b/doc/doc.md index cfd0b298f..852f8d3c4 100644 --- a/doc/doc.md +++ b/doc/doc.md @@ -22,26 +22,25 @@ ## Table of Contents - [1 Main Window](#doc-1) - [2 File Area](#doc-2) -- [3 Work Area](#doc-3) - - [3.1 Profiler](#doc-3-1) - - [3.2 Concordancer](#doc-3-2) - - [3.3 Parallel Concordancer](#doc-3-3) - - [3.4 Dependency Parser](#doc-3-4) - - [3.5 Wordlist Generator](#doc-3-5) - - [3.6 N-gram Generator](#doc-3-6) - - [3.7 Collocation Extractor](#doc-3-7) - - [3.8 Colligation Extractor](#doc-3-8) - - [3.9 Keyword Extractor](#doc-3-9) -- [4 Appendixes](#doc-4) - - [4.1 Supported Languages](#doc-4-1) - - [4.2 Supported File Types](#doc-4-2) - - [4.3 Supported File Encodings](#doc-4-3) - - [4.4 Supported Measures](#doc-4-4) - - [4.4.1 Readability Formulas](#doc-4-4-1) - - [4.4.2 Indicators of Lexical Density/Diversity](#doc-4-4-2) - - [4.4.3 Measures of Dispersion and Adjusted Frequency](#doc-4-4-3) - - [4.4.4 Tests of Statistical Significance, Measures of Bayes Factor, and Measures of Effect Size](#doc-4-4-4) -- [5 References](#doc-5) +- [3 Profiler](#doc-3) +- [4 Concordancer](#doc-4) +- [5 Parallel Concordancer](#doc-5) +- [6 Dependency Parser](#doc-6) +- [7 Wordlist Generator](#doc-7) +- [8 N-gram Generator](#doc-8) +- [9 Collocation Extractor](#doc-9) +- [10 Colligation Extractor](#doc-10) +- [11 Keyword Extractor](#doc-11) +- [12 Appendixes](#doc-12) + - [12.1 Supported Languages](#doc-12-1) + - [12.2 Supported File Types](#doc-12-2) + - [12.3 Supported File Encodings](#doc-12-3) + - [12.4 Supported Measures](#doc-12-4) + - [12.4.1 Readability Formulas](#doc-12-4-1) + - [12.4.2 Indicators of Lexical Density/Diversity](#doc-12-4-2) + - [12.4.3 Measures of Dispersion and Adjusted Frequency](#doc-12-4-3) + - [12.4.4 Tests of Statistical Significance, Measures of Bayes Factor, and Measures of Effect Size](#doc-12-4-4) +- [13 References](#doc-13) ## [1 Main Window](#doc) @@ -71,43 +70,60 @@ In most cases, the first thing to do in *Wordless* is open and select your files Files are loaded, cached and selected automatically after being added to the *File Table*. **Only selected files will be processed by *Wordless***. You can drag and drop files around the *File Table* to change their orders, which would be reflected in the results. -By default, *Wordless* tries to detect the encoding and language settings of all files for you, you should double check and make sure that the settings of each and every file are correct. If you prefer changing file settings manually, you could uncheck **Auto-detect encodings** and/or **Auto-detect languages** in the *Open Files* dialog. The default file settings could be modified via **Menu Bar β†’ Preferences β†’ Settings β†’ Files β†’ Default Settings**. +By default, *Wordless* would try to detect the encoding and language settings of all files for you, you should double check and make sure that the settings of each and every file are correct. If you prefer changing file settings manually, you could uncheck ***Open Files* dialog β†’ Auto-detect encodings** and/or ***Open Files* dialog β†’ Auto-detect languages**. The default file settings could be modified via **Menu Bar β†’ Preferences β†’ Settings β†’ Files β†’ Default Settings**. Additionally, you need to change ***Open Files* dialog β†’ Tokenized** and ***Open Files* dialog β†’ Tagged** options of each files according to whether or not the file has been tokenized or tagged. -- **2.1 Open Files**
- Add one single file or multiple files to the *File Table*. +- **2.1 Menu Bar β†’ File**
+ - **2.1.1 Open Files**
+ Open the *Open Files* dialog to add file(s) to the *File Table*. - \* You can use the **Ctrl** key (**Command** key on macOS) and/or the **Shift** key to select multiple files. + - **2.1.2 Reopen Closed Files**
+ Add file(s) that are closed the last time back to the *File Table*. -- **2.2 Open Folder**
- Add all files in the folder to the *File Table*. + \* The history of all closed files will be erased upon exit of *Wordless*. - By default, all files in the chosen folder and the sub-folders of the chosen folder (and sub-folders of sub-folders, and so on) are added to the *File Table*. If you do not want to add files in sub-folders to the *File Table*, you could uncheck **Include files in sub-folders** in the *Open Files* dialog. + - **2.1.3 Select All**
+ Select all files in the *File Table*. -- **2.3 Reopen Closed Files**
- Add file(s) that are closed the last time back to the *File Table*. + - **2.1.4 Deselect All**
+ Deselect all files in the *File Table*. - \* The history of all closed files will be erased upon exit of *Wordless*. + - **2.1.5 Invert Selection**
+ Select files that are not currently selected and deselect files that are currently selected in the *File Table*. -- **2.4 Select All**
- Select all files in the *File Table*. + - **2.1.6 Close Selected**
+ Remove files that are currently selected from the *File Table*. -- **2.5 Deselect All**
- Deselect all files in the *File Table*. + - **2.1.7 Close All**
+ Remove all files from the *File Table*. -- **2.6 Invert Selection**
- Select files that are not currently selected and deselect files that are currently selected in the *File Table*. +- **2.2 *Open Files* dialog**
+ - **2.2.1 Add files**
+ Add one single file or multiple files into the table. -- **2.7 Close Selected**
- Remove files that are currently selected from the *File Table*. + \* You can use the **Ctrl** key (**Command** key on macOS) and/or the **Shift** key to select multiple files. -- **2.8 Close All**
- Remove all files from the *File Table*. + - **2.2.2 Add folder**
+ Add all files in the folder into the table. - -## [3 Work Area](#doc) + By default, all files in the chosen folder and the subfolders of the chosen folder (and subfolders of subfolders, and so on) are added to the table. If you do not want to add files in subfolders to the table, you could uncheck **Include files in subfolders**. + + - **2.2.3 Remove files**
+ Remove the selected files from the table. + + - **2.2.4 Clear table**
+ Remove all files from the table. + + - **2.2.5 Auto-detect encodings**
+ Auto-detect the encodings of all files when they are added into the table. If the detection results are incorrect, you can manually modify encoding settings in the table. + + - **2.2.6 Auto-detect languages**
+ Auto-detect the languages of all files when they are added into the table. If the detection results are incorrect, you can manually modify language settings in the table. + + - **2.2.7 Include files in subfolders**
+ When adding a folder to the table, recursively add all files in the chosen folder and subfolders of the chosen folder (and subfolders of subfolders, and so on) into the table - -### [3.1 Profiler](#doc) + +### [3 Profiler](#doc) > [!NOTE] > Renamed from **Overview** to **Profiler** in *Wordless* 2.2.0 @@ -116,7 +132,7 @@ In *Profiler*, you can check and compare general linguistic features of differen All statistics are grouped into 5 tables for better readability: Readability, Counts, Lexical Density/Diversity, Lengths, Length Breakdown. - **3.1.1 Readability**
- Readability statistics of each file calculated according to the different readability tests used. See section [4.4.1 Readability Formulas](#doc-4-4-1) for more details. + Readability statistics of each file calculated according to the different readability tests used. See section [12.4.1 Readability Formulas](#doc-12-4-1) for more details. - **3.1.2 Counts**
- **3.1.2.1 Count of Paragraphs**
@@ -164,7 +180,7 @@ All statistics are grouped into 5 tables for better readability: Readability, Co The percentage of the number of characters in each file out of the total number of characters in all files. - **3.1.3 Lexical Density/Diversity**
- Statistics of lexical density/diversity which reflect the the extend to which the vocabulary used in each file varies. See section [4.4.2 Indicators of Lexical Density/Diversity](#doc-4-4-2) for more details. + Statistics of lexical density/diversity which reflect the the extend to which the vocabulary used in each file varies. See section [12.4.2 Indicators of Lexical Density/Diversity](#doc-12-4-2) for more details. - **3.1.4 Lengths**
- **3.1.4.1 Paragraph Length in Sentences / Sentence Segments / Tokens (Mean)**
@@ -318,55 +334,55 @@ All statistics are grouped into 5 tables for better readability: Readability, Co - **3.1.5.6 Count of n-character-long Tokens %**
The percentage of the number of n-character-long tokens in each file out of the total number of n-character-long tokens in all files, where n = 1, 2, 3, etc. - -### [3.2 Concordancer](#doc) + +### [4 Concordancer](#doc) In *Concordancer*, you can search for tokens in different files and generate concordance lines. You can adjust settings for data generation via **Generation Settings**. After the concordance lines are generated and displayed in the table, you can sort the results by clicking **Sort Results** or search in *Data Table* for parts that might be of interest to you by clicking **Search in results**. Highlight colors for sorting can be modified via **Menu Bar β†’ Preferences β†’ Settings β†’ Tables β†’ Concordancer β†’ Sorting**. You can generate concordance plots for all search terms. You can modify the settings for the generated figure via **Figure Settings**. -- **3.2.1 Left**
+- **4.1 Left**
The context before each search term, which displays 10 tokens left to the **Node** by default. You can change this behavior via **Generation Settings**. -- **3.2.2 Node**
+- **4.2 Node**
The search term(s) specified in **Search Settings β†’ Search Term**. -- **3.2.3 Right**
+- **4.3 Right**
The context after each search term, which displays 10 tokens right to the **Node** by default. You can change this behavior via **Generation Settings**. -- **3.2.4 Sentiment**
+- **4.4 Sentiment**
The sentiment of the **Node** combined with its context (**Left** and **Right**). -- **3.2.5 Token No.**
+- **4.5 Token No.**
The position of the first token of **Node** in each file. -- **3.2.6 Token No. %**
+- **4.6 Token No. %**
The percentage of the position of the first token of **Node** in each file. -- **3.2.7 Sentence Segment No.**
+- **4.7 Sentence Segment No.**
The position of the sentence segment where the **Node** is found in each file. -- **3.2.8 Sentence Segment No. %**
+- **4.8 Sentence Segment No. %**
The percentage of the position of the sentence segment where the **Node** is found in each file. -- **3.2.9 Sentence No.**
+- **4.9 Sentence No.**
The position of the sentence where the **Node** is found in each file. -- **3.2.10 Sentence No. %**
+- **4.10 Sentence No. %**
The percentage of the position of the sentence where the **Node** is found in each file. -- **3.2.11 Paragraph No.**
+- **4.11 Paragraph No.**
The position of the paragraph where the **Node** is found in each file. -- **3.2.12 Paragraph No. %**
+- **4.12 Paragraph No. %**
The percentage of the position of the paragraph where the **Node** is found in each file. -- **3.2.13 File**
+- **4.13 File**
The name of the file where the **Node** is found. - -### [3.3 Parallel Concordancer](#doc) + +### [5 Parallel Concordancer](#doc) > [!NOTE] > 1. Added in *Wordless* 2.0.0 > 1. Renamed from **Concordancer (Parallel Mode)** to **Parallel Concordancer** in *Wordless* 2.2.0 @@ -375,19 +391,19 @@ In *Parallel Concordancer*, you can search for tokens in parallel corpora and ge You can search in *Data Table* for parts that might be of interest to you by clicking **Search in results**. -- **3.3.1 Parallel Unit No.**
+- **5.1 Parallel Unit No.**
The position of the alignment unit (paragraph) where the the search term is found. -- **3.3.2 Parallel Unit No. %**
+- **5.2 Parallel Unit No. %**
The percentage of the position of the alignment unit (paragraph) where the the search term is found. -- **3.3.3 Parallel Units**
+- **5.3 Parallel Units**
The parallel unit (paragraph) where the search term is found in each file. Highlight colors for search terms can be modified via **Menu Bar β†’ Preferences β†’ Settings β†’ Tables β†’ Parallel Concordancer β†’ Highlight Color Settings**. - -### [3.4 Dependency Parser](#doc) + +### [6 Dependency Parser](#doc) > [!NOTE] > Added in *Wordless* 3.0.0 @@ -397,34 +413,34 @@ You can filter the results by clicking **Filter results** or search in *Data Tab You can select lines in the *Results Area* and then click *Generate Figure* to show dependency graphs for all selected sentences. You can modify the settings for the generated figure via **Figure Settings** and decide how the figures should be displayed. -- **3.4.1 Head**
+- **6.1 Head**
The token functioning as the head in the dependency structure. -- **3.4.2 Dependent**
+- **6.2 Dependent**
The token functioning as the dependent in the dependency structure. -- **3.4.3 Dependency Length**
+- **6.3 Dependency Length**
The dependency length (distance) between the head and dependent in the dependency structure. The dependency length is positive when the head follows the dependent and would be negative if the head precedes the dependent. -- **3.4.4 Dependency Length (Absolute)**
+- **6.4 Dependency Length (Absolute)**
The absolute value of the dependency length (distance) between the head and dependent in the dependency structure. The absolute dependency length is always positive. -- **3.4.5 Sentence**
+- **6.5 Sentence**
The sentence where the dependency structure is found. Highlight colors for the head and the dependent can be modified via **Menu Bar β†’ Preferences β†’ Settings β†’ Tables β†’ Dependency Parser β†’ Highlight Color Settings**. -- **3.4.6 Sentence No.**
+- **6.6 Sentence No.**
The position of the sentence where the dependency structure is found. -- **3.4.7 Sentence No. %**
+- **6.7 Sentence No. %**
The percentage of the position of the sentence where the dependency structure is found. -- **3.4.8 File**
+- **6.8 File**
The name of the file where the dependency structure is found. - -### [3.5 Wordlist Generator](#doc) + +### [7 Wordlist Generator](#doc) > [!NOTE] > Renamed from **Wordlist** to **Wordlist Generator** in *Wordless* 2.2.0 @@ -434,36 +450,36 @@ You can filter the results by clicking **Filter results** or search in *Data Tab You can generate line charts or word clouds for wordlists using any statistics. You can modify the settings for the generated figure via **Figure Settings**. -- **3.5.1 Rank**
+- **7.1 Rank**
The rank of the token sorted by its frequency in the first file in descending order (by default). You can sort the results again by clicking the column headers. You can use continuous numbering after tied ranks (eg. 1/1/1/2/2/3 instead of 1/1/1/4/4/6) by checking **Menu Bar β†’ Preferences β†’ Settings β†’ Tables β†’ Rank Settings β†’ Continue numbering after ties**. -- **3.5.2 Token**
+- **7.2 Token**
You can specify what should be counted as a "token" via **Token Settings**. -- **3.5.3 Syllabification**
+- **7.3 Syllabification**
The syllabified form of each token. If the token happens to exist in the vocabulary of multiple languages, all syllabified forms with their applicable languages will be listed. - If there is no syllable tokenization support for the language where the token is found, "No language support" is displayed instead. To check which languages have syllable tokenization support, please refer to section [4.1 Supported Languages](#doc-4-1). + If there is no syllable tokenization support for the language where the token is found, "No language support" is displayed instead. To check which languages have syllable tokenization support, please refer to section [12.1 Supported Languages](#doc-12-1). -- **3.5.4 Frequency**
+- **7.4 Frequency**
The number of occurrences of the token in each file. -- **3.5.5 Dispersion**
- The dispersion of the token in each file. You can change the measure of dispersion used via **Generation Settings β†’ Measure of Dispersion**. See section [4.4.3 Measures of Dispersion & Adjusted Frequency](#doc-4-4-3) for more details. +- **7.5 Dispersion**
+ The dispersion of the token in each file. You can change the measure of dispersion used via **Generation Settings β†’ Measure of Dispersion**. See section [12.4.3 Measures of Dispersion & Adjusted Frequency](#doc-12-4-3) for more details. -- **3.5.6 Adjusted Frequency**
- The adjusted frequency of the token in each file. You can change the measure of adjusted frequency used via **Generation Settings β†’ Measure of Adjusted Frequency**. See section [4.4.3 Measures of Dispersion & Adjusted Frequency](#doc-4-4-3) for more details. +- **7.6 Adjusted Frequency**
+ The adjusted frequency of the token in each file. You can change the measure of adjusted frequency used via **Generation Settings β†’ Measure of Adjusted Frequency**. See section [12.4.3 Measures of Dispersion & Adjusted Frequency](#doc-12-4-3) for more details. -- **3.5.7 Number of Files Found**
+- **7.7 Number of Files Found**
The number of files in which the token appears at least once. -- **3.5.8 Number of Files Found %**
+- **7.8 Number of Files Found %**
The percentage of the number of files in which the token appears at least once out of the total number of files that are cureently selected. - -### [3.6 N-gram Generator](#doc) + +### [8 N-gram Generator](#doc) > [!NOTE] > Renamed from **N-gram** to **N-gram Generator** in *Wordless* 2.2.0 @@ -473,29 +489,29 @@ You can filter the results by clicking **Filter results** or search in *Data Tab You can generate line charts or word clouds for n-grams using any statistics. You can modify the settings for the generated figure via **Figure Settings**. -- **3.6.1 Rank**
+- **8.1 Rank**
The rank of the n-gram sorted by its frequency in the first file in descending order (by default). You can sort the results again by clicking the column headers. You can use continuous numbering after tied ranks (eg. 1/1/1/2/2/3 instead of 1/1/1/4/4/6) by checking **Menu Bar β†’ Preferences β†’ Settings β†’ Tables β†’ Rank Settings β†’ Continue numbering after ties**. -- **3.6.2 N-gram**
+- **8.2 N-gram**
You can specify what should be counted as a "n-gram" via **Token Settings**. -- **3.6.3 Frequency**
+- **8.3 Frequency**
The number of occurrences of the n-gram in each file. -- **3.6.4 Dispersion**
- The dispersion of the n-gram in each file. You can change the measure of dispersion used via **Generation Settings β†’ Measure of Dispersion**. See section [4.4.3 Measures of Dispersion & Adjusted Frequency](#doc-4-4-3) for more details. +- **8.4 Dispersion**
+ The dispersion of the n-gram in each file. You can change the measure of dispersion used via **Generation Settings β†’ Measure of Dispersion**. See section [12.4.3 Measures of Dispersion & Adjusted Frequency](#doc-12-4-3) for more details. -- **3.6.5 Adjusted Frequency**
- The adjusted frequency of the n-gram in each file. You can change the measure of adjusted frequency used via **Generation Settings β†’ Measure of Adjusted Frequency**. See section [4.4.3 Measures of Dispersion & Adjusted Frequency](#doc-4-4-3) for more details. +- **8.5 Adjusted Frequency**
+ The adjusted frequency of the n-gram in each file. You can change the measure of adjusted frequency used via **Generation Settings β†’ Measure of Adjusted Frequency**. See section [12.4.3 Measures of Dispersion & Adjusted Frequency](#doc-12-4-3) for more details. -- **3.6.6 Number of Files Found**
+- **8.6 Number of Files Found**
The number of files in which the n-gram appears at least once. -- **3.6.7 Number of Files Found %**
+- **8.7 Number of Files Found %**
The percentage of the number of files in which the n-gram appears at least once out of the total number of files that are currently selected. - -### [3.7 Collocation Extractor](#doc) + +### [9 Collocation Extractor](#doc) > [!NOTE] > Renamed from **Collocation** to **Collocation Extractor** in *Wordless* 2.2.0 @@ -505,43 +521,43 @@ You can filter the results by clicking **Filter results** or search in *Data Tab You can generate line charts, word clouds, and network graphs for patterns of collocation using any statistics. You can modify the settings for the generated figure via **Figure Settings**. -- **3.7.1 Rank**
+- **9.1 Rank**
The rank of the collocating token sorted by the p-value of the significance test conducted on the node and the collocating token in the first file in ascending order (by default). You can sort the results again by clicking the column headers. You can use continuous numbering after tied ranks (eg. 1/1/1/2/2/3 instead of 1/1/1/4/4/6) by checking **Menu Bar β†’ Preferences β†’ Settings β†’ Tables β†’ Rank Settings β†’ Continue numbering after ties**. -- **3.7.2 Node**
+- **9.2 Node**
The search term. You can specify what should be counted as a "token" via **Token Settings**. -- **3.7.3 Collocate**
+- **9.3 Collocate**
The collocating token. You can specify what should be counted as a "token" via **Token Settings**. -- **3.7.4 Ln, ..., L3, L2, L1, R1, R2, R3, ..., Rn**
+- **9.4 Ln, ..., L3, L2, L1, R1, R2, R3, ..., Rn**
The number of co-occurrences of the node and the collocating token with the collocating token at the given position in each file. -- **3.7.5 Frequency**
+- **9.5 Frequency**
The total number of co-occurrences of the node and the collocating token with the collocating token at all possible positions in each file. -- **3.7.6 Test Statistic**
- The test statistic of the significance test conducted on the node and the collocating token in each file. You can change the test of statistical significance used via **Generation Settings β†’ Test of Statistical Significance**. See section [4.4.4 Tests of Statistical Significance, Measures of Bayes Factor, & Measures of Effect Size](#doc-4-4-4) for more details. +- **9.6 Test Statistic**
+ The test statistic of the significance test conducted on the node and the collocating token in each file. You can change the test of statistical significance used via **Generation Settings β†’ Test of Statistical Significance**. See section [12.4.4 Tests of Statistical Significance, Measures of Bayes Factor, & Measures of Effect Size](#doc-12-4-4) for more details. Please note that test statistic is not available for some tests of statistical significance. -- **3.7.7 p-value**
- The p-value of the significance test conducted on the node and the collocating token in each file. You can change the test of statistical significance used via **Generation Settings β†’ Test of Statistical Significance**. See section [4.4.4 Tests of Statistical Significance, Measures of Bayes Factor, & Measures of Effect Size](#doc-4-4-4) for more details. +- **9.7 p-value**
+ The p-value of the significance test conducted on the node and the collocating token in each file. You can change the test of statistical significance used via **Generation Settings β†’ Test of Statistical Significance**. See section [12.4.4 Tests of Statistical Significance, Measures of Bayes Factor, & Measures of Effect Size](#doc-12-4-4) for more details. -- **3.7.8 Bayes Factor**
- The Bayes factor the node and the collocating token in each file. You can change the measure of Bayes factor used via **Generation Settings β†’ Measure of Bayes Factor**. See section [4.4.4 Tests of Statistical Significance, Measures of Bayes Factor, & Measures of Effect Size](#doc-4-4-4) for more details. +- **9.8 Bayes Factor**
+ The Bayes factor the node and the collocating token in each file. You can change the measure of Bayes factor used via **Generation Settings β†’ Measure of Bayes Factor**. See section [12.4.4 Tests of Statistical Significance, Measures of Bayes Factor, & Measures of Effect Size](#doc-12-4-4) for more details. -- **3.7.9 Effect Size**
- The effect size of the node and the collocating token in each file. You can change the measure of effect size used via **Generation Settings β†’ Measure of Effect Size**. See section [4.4.4 Tests of Statistical Significance, Measures of Bayes Factor, & Measures of Effect Size](#doc-4-4-4) for more details. +- **9.9 Effect Size**
+ The effect size of the node and the collocating token in each file. You can change the measure of effect size used via **Generation Settings β†’ Measure of Effect Size**. See section [12.4.4 Tests of Statistical Significance, Measures of Bayes Factor, & Measures of Effect Size](#doc-12-4-4) for more details. -- **3.7.10 Number of Files Found**
+- **9.10 Number of Files Found**
The number of files in which the node and the collocating token co-occur at least once. -- **3.7.11 Number of Files Found %**
+- **9.11 Number of Files Found %**
The percentage of the number of files in which the node and the collocating token co-occur at least once out of the total number of files that are currently selected. - -### [3.8 Colligation Extractor](#doc) + +### [10 Colligation Extractor](#doc) > [!NOTE] > Renamed from **Colligation** to **Colligation Extractor** in *Wordless* 2.2.0 @@ -553,43 +569,43 @@ You can filter the results by clicking **Filter results** or search in *Data Tab You can generate line charts or word clouds for patterns of colligation using any statistics. You can modify the settings for the generated figure via **Figure Settings**. -- **3.8.1 Rank**
+- **10.1 Rank**
The rank of the collocating part of speech sorted by the p-value of the significance test conducted on the node and the collocating part of speech in the first file in ascending order (by default). You can sort the results again by clicking the column headers. You can use continuous numbering after tied ranks (eg. 1/1/1/2/2/3 instead of 1/1/1/4/4/6) by checking **Menu Bar β†’ Preferences β†’ Settings β†’ Tables β†’ Rank Settings β†’ Continue numbering after ties**. -- **3.8.2 Node**
+- **10.2 Node**
The search term. You can specify what should be counted as a "token" via **Token Settings**. -- **3.8.3 Collocate**
+- **10.3 Collocate**
The collocating part of speech. You can specify what should be counted as a "token" via **Token Settings**. -- **3.8.4 Ln, ..., L3, L2, L1, R1, R2, R3, ..., Rn**
+- **10.4 Ln, ..., L3, L2, L1, R1, R2, R3, ..., Rn**
The number of co-occurrences of the node and the collocating part of speech with the collocating part of speech at the given position in each file. -- **3.8.5 Frequency**
+- **10.5 Frequency**
The total number of co-occurrences of the node and the collocating part of speech with the collocating part of speech at all possible positions in each file. -- **3.8.6 Test Statistic**
- The test statistic of the significance test conducted on the node and the collocating part of speech in each file. You can change the test of statistical significance used via **Generation Settings β†’ Test of Statistical Significance**. See section [4.4.4 Tests of Statistical Significance, Measures of Bayes Factor, & Measures of Effect Size](#doc-4-4-4) for more details. +- **10.6 Test Statistic**
+ The test statistic of the significance test conducted on the node and the collocating part of speech in each file. You can change the test of statistical significance used via **Generation Settings β†’ Test of Statistical Significance**. See section [12.4.4 Tests of Statistical Significance, Measures of Bayes Factor, & Measures of Effect Size](#doc-12-4-4) for more details. Please note that test statistic is not available for some tests of statistical significance. -- **3.8.7 p-value**
- The p-value of the significance test conducted on the node and the collocating part of speech in each file. You can change the test of statistical significance used via **Generation Settings β†’ Test of Statistical Significance**. See section [4.4.4 Tests of Statistical Significance, Measures of Bayes Factor, & Measures of Effect Size](#doc-4-4-4) for more details. +- **10.7 p-value**
+ The p-value of the significance test conducted on the node and the collocating part of speech in each file. You can change the test of statistical significance used via **Generation Settings β†’ Test of Statistical Significance**. See section [12.4.4 Tests of Statistical Significance, Measures of Bayes Factor, & Measures of Effect Size](#doc-12-4-4) for more details. -- **3.8.8 Bayes Factor**
- The Bayes factor of the node and the collocating part of speech in each file. You can change the measure of Bayes factor used via **Generation Settings β†’ Measure of Bayes Factor**. See section [4.4.4 Tests of Statistical Significance, Measures of Bayes Factor, & Measures of Effect Size](#doc-4-4-4) for more details. +- **10.8 Bayes Factor**
+ The Bayes factor of the node and the collocating part of speech in each file. You can change the measure of Bayes factor used via **Generation Settings β†’ Measure of Bayes Factor**. See section [12.4.4 Tests of Statistical Significance, Measures of Bayes Factor, & Measures of Effect Size](#doc-12-4-4) for more details. -- **3.8.9 Effect Size**
- The effect size of the node and the collocating part of speech in each file. You can change the measure of effect size used via **Generation Settings β†’ Measure of Effect Size**. See section [4.4.4 Tests of Statistical Significance, Measures of Bayes Factor, & Measures of Effect Size](#doc-4-4-4) for more details. +- **10.9 Effect Size**
+ The effect size of the node and the collocating part of speech in each file. You can change the measure of effect size used via **Generation Settings β†’ Measure of Effect Size**. See section [12.4.4 Tests of Statistical Significance, Measures of Bayes Factor, & Measures of Effect Size](#doc-12-4-4) for more details. -- **3.8.10 Number of Files Found**
+- **10.10 Number of Files Found**
The number of files in which the node and the collocating part of speech co-occur at least once. -- **3.8.11 Number of Files Found %**
+- **10.11 Number of Files Found %**
The percentage of the number of files in which the node and the collocating part of speech co-occur at least once out of the total number of file that are currently selected. - -### [3.9 Keyword Extractor](#doc) + +### [11 Keyword Extractor](#doc) > [!NOTE] > Renamed from **Keyword** to **Keyword Extractor** in *Wordless* 2.2 @@ -599,43 +615,43 @@ You can filter the results by clicking **Filter results** or search in *Data Tab You can generate line charts or word clouds for keywords using any statistics. You can modify the settings for the generated figure via **Figure Settings**. -- **3.9.1 Rank**
+- **11.1 Rank**
The rank of the keyword sorted by the p-value of the significance test conducted on the keyword in the first file in ascending order (by default). You can sort the results again by clicking the column headers. You can use continuous numbering after tied ranks (eg. 1/1/1/2/2/3 instead of 1/1/1/4/4/6) by checking **Menu Bar β†’ Preferences β†’ Settings β†’ Tables β†’ Rank Settings β†’ Continue numbering after ties**. -- **3.9.2 Keyword**
+- **11.2 Keyword**
The potential keyword. You can specify what should be counted as a "token" via **Token Settings**. -- **3.9.3 Frequency (in Reference File)**
+- **11.3 Frequency (in Reference File)**
The number of occurrences of the keyword in the reference file. -- **3.9.4 Frequency (in Observed Files)**
+- **11.4 Frequency (in Observed Files)**
The number of occurrences of the keyword in each observed file. -- **3.9.5 Test Statistic**
- The test statistic of the significance test conducted on the keyword in each file. You can change the test of statistical significance used via **Generation Settings β†’ Test of Statistical Significance**. See section [4.4.4 Tests of Statistical Significance, Measures of Bayes Factor, & Measures of Effect Size](#doc-4-4-4) for more details. +- **11.5 Test Statistic**
+ The test statistic of the significance test conducted on the keyword in each file. You can change the test of statistical significance used via **Generation Settings β†’ Test of Statistical Significance**. See section [12.4.4 Tests of Statistical Significance, Measures of Bayes Factor, & Measures of Effect Size](#doc-12-4-4) for more details. Please note that test statistic is not available for some tests of statistical significance. -- **3.9.6 p-value**
- The p-value of the significance test conducted on the keyword in each file. You can change the test of statistical significance used via **Generation Settings β†’ Test of Statistical Significance**. See section [4.4.4 Tests of Statistical Significance, Measures of Bayes Factor, & Measures of Effect Size](#doc-4-4-4) for more details. +- **11.6 p-value**
+ The p-value of the significance test conducted on the keyword in each file. You can change the test of statistical significance used via **Generation Settings β†’ Test of Statistical Significance**. See section [12.4.4 Tests of Statistical Significance, Measures of Bayes Factor, & Measures of Effect Size](#doc-12-4-4) for more details. -- **3.9.7 Bayes Factor**
- The Bayes factor of the keyword in each file. You can change the measure of Bayes factor used via **Generation Settings β†’ Measure of Bayes Factor**. See section [4.4.4 Tests of Statistical Significance, Measures of Bayes Factor, & Measures of Effect Size](#doc-4-4-4) for more details. +- **11.7 Bayes Factor**
+ The Bayes factor of the keyword in each file. You can change the measure of Bayes factor used via **Generation Settings β†’ Measure of Bayes Factor**. See section [12.4.4 Tests of Statistical Significance, Measures of Bayes Factor, & Measures of Effect Size](#doc-12-4-4) for more details. -- **3.9.8 Effect Size**
- The effect size of on the keyword in each file. You can change the measure of effect size used via **Generation Settings β†’ Measure of Effect Size**. See section [4.4.4 Tests of Statistical Significance, Measures of Bayes Factor, & Measures of Effect Size](#doc-4-4-4) for more details. +- **11.8 Effect Size**
+ The effect size of on the keyword in each file. You can change the measure of effect size used via **Generation Settings β†’ Measure of Effect Size**. See section [12.4.4 Tests of Statistical Significance, Measures of Bayes Factor, & Measures of Effect Size](#doc-12-4-4) for more details. -- **3.9.9 Number of Files Found**
+- **11.9 Number of Files Found**
The number of files in which the keyword appears at least once. -- **3.9.10 Number of Files Found %**
+- **11.10 Number of Files Found %**
The percentage of the number of files in which the keyword appears at least once out of the total number of files that are currently selected. - -## [4 Appendixes](#doc) + +## [12 Appendixes](#doc) - -### [4.1 Supported Languages](#doc) + +### [12.1 Supported Languages](#doc) Language|Sentence Token-ization|Word Token-ization|Syllable Token-ization|Part-of-speech Tagging|Lemma-tization|Stop Word List|Depen-dency Parsing|Senti-ment Analysis :-----------------------:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-: @@ -767,8 +783,8 @@ Other languages |⭕️ |⭕️ |βœ–οΈ|βœ–οΈ|βœ–οΈ|βœ–οΈ|βœ–οΈ|βœ– > ⭕️: Supported but falls back to the default English (United States) tokenizer
> βœ–οΈ: Not supported - -### [4.2 Supported File Types](#doc) + +### [12.2 Supported File Types](#doc) File Type |File Extensions|Remarks --------------------------|---------------|------- @@ -785,10 +801,10 @@ XML filesΒΉ |\*.xml | > [!IMPORTANT] > 1. Non-TXT files will be automatically converted to TXT files when being imported into *Wordless*. You can check the converted files under folder **imports** at the installation location of *Wordless* on your computer (as for macOS users, right click **Wordless.app**, select **Show Package Contents** and navigate to **Contents/MacOS/imports/**). You can change this location via **Menu Bar β†’ Preferences β†’ Settings β†’ General β†’ Import β†’ Temporary Files β†’ Default path**. -> 1. It is **not recommended to directly import non-text files into *Wordless***, as accuracy of text extraction could not be guaranteed and unintended data loss might occur, for which reason users are encouraged to **convert their files using specialized tools and make discreet decisions** on which part of your data should be kept, transformed, and discarded. +> 1. It is **not recommended to directly import non-text files into *Wordless*** and the support for doing so is provided only for convenience, since accuracy of text extraction could never be guaranteed and unintended data loss might occur, for which reason users are encouraged to **convert their files using specialized tools and make their own choices** on which part of the data should be kept or discarded. - -### [4.3 Supported File Encodings](#doc) + +### [12.3 Supported File Encodings](#doc) Language |File Encoding |Auto-detection -----------------------|-----------------------|:------------: @@ -896,14 +912,14 @@ Ukrainian |KOI8-U |βœ” Urdu |CP1006 |βœ” Vietnamese |CP1258 |βœ” - -### [4.4 Supported Measures](#doc) + +### [12.4 Supported Measures](#doc) - -#### [4.4.1 Readability Formulas](#doc) + +#### [12.4.1 Readability Formulas](#doc) The readability of a text depends on several variables including the average sentence length, average word length in characters, average word length in syllables, number of monosyllabic words, number of polysyllabic words, number of difficult words, etc. -It should be noted that some readability measures are **language-specific**, or applicable only to texts in languages for which *Wordless* have **built-in syllable tokenization support** (check [4.4.1](#doc-4-1) for reference), while others can be applied to texts in all languages. +It should be noted that some readability measures are **language-specific**, or applicable only to texts in languages for which *Wordless* have **built-in syllable tokenization support** (check [12.1](#doc-12-1) for reference), while others can be applied to texts in all languages. The following variables would be used in formulas:
**NumSentences**: Number of sentences
@@ -1161,8 +1177,8 @@ Readability Formula|Formula|Supported Languages > 1. Requires **built-in syllable tokenization support** > 1. Requires **built-in part-of-speech tagging support** - -#### [4.4.2 Indicators of Lexical Density/Diversity](#doc) + +#### [12.4.2 Indicators of Lexical Density/Diversity](#doc) Lexical density/diversity is the measurement of the extent to which the vocabulary used in the text varies. The following variables would be used in formulas:
@@ -1276,8 +1292,8 @@ Indicator of Lexical Density/Diversity|Formula > [!NOTE] > 1. Variants available and can be selected via **Menu Bar β†’ Preferences β†’ Settings β†’ Measures β†’ Lexical Density/Diversity** - -#### [4.4.3 Measures of Dispersion and Adjusted Frequency](#doc) + +#### [12.4.3 Measures of Dispersion and Adjusted Frequency](#doc) For parts-based measures, each file is divided into **n** (whose value you could modify via **Menu Bar β†’ Preferences β†’ Settings β†’ Measures β†’ Dispersion / Adjusted Frequency β†’ General Settings β†’ Divide each file into subsections**) sub-sections and the frequency of the word in each part is counted and denoted by **F₁**, **Fβ‚‚**, **F₃**, ..., **Fβ‚™** respectively. The total frequency of the word in each file is denoted by **F** and the mean value of the frequencies over all sub-sections is denoted by **FΜ…**. @@ -1361,8 +1377,8 @@ Measure of Dispersion (Distance-based)|Measure of Adjusted Frequency (Distance-b Average Reduced Frequency
([Savický & HlavÑčovÑ, 2002](#ref-savicky-hlavacova-2002))|Average Reduced Frequency
([Savický & HlavÑčovÑ, 2002](#ref-savicky-hlavacova-2002))|![Formula](/doc/measures/dispersion_adjusted_frequency/arf.svg) Average Waiting Time
([Savický & HlavÑčovÑ, 2002](#ref-savicky-hlavacova-2002))|Average Waiting Time
([Savický & HlavÑčovÑ, 2002](#ref-savicky-hlavacova-2002))|![Formula](/doc/measures/dispersion_adjusted_frequency/awt.svg) - -#### [4.4.4 Tests of Statistical Significance, Measures of Bayes Factor, and Measures of Effect Size](#doc) + +#### [12.4.4 Tests of Statistical Significance, Measures of Bayes Factor, and Measures of Effect Size](#doc) In order to calculate the statistical significance, Bayes factor, and effect size (except **Mann-Whitney U Test**, **Student's t-test (2-sample)**, and **Welch's t-test**) for two words in the same file (collocates) or for one specific word in two different files (keywords), two contingency tables must be constructed first, one for observed values, the other for expected values. @@ -1528,8 +1544,8 @@ Measure of Effect Size|Formula Poisson Collocation Measure
([Quasthoff & Wolff, 2002](#ref-quasthoff-wolff-2002))|![Formula](/doc/measures/effect_size/poisson_collocation_measure.svg) Squared Phi Coefficient
([Church & Gale, 1991](#ref-church-gale-1991))|![Formula](/doc/measures/effect_size/squared_phi_coeff.svg) - -## [5 References](#doc) + +## [13 References](#doc) 1. [**^**](#ref-rd) Al-Heeti, K. N. (1984). *Judgment analysis technique applied to readability prediction of Arabic reading material* [Doctoral dissertation, University of Northern Colorado]. ProQuest Dissertations and Theses Global. diff --git a/tests/test_main.py b/tests/test_main.py index ee4c214eb..765988bee 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -34,7 +34,7 @@ def test_wl_dialog_confirm_exit(): wl_dialog_confirm_exit = wl_main.Wl_Dialog_Confirm_Exit(main) wl_dialog_confirm_exit.open() wl_dialog_confirm_exit.load_settings() - wl_dialog_confirm_exit.confirm_on_exit_changed() + wl_dialog_confirm_exit.always_confirm_on_exit_changed() def test_wl_dialog_need_help(): wl_dialog_need_help = wl_main.Wl_Dialog_Need_Help(main) diff --git a/tests/tests_dialogs/test_dialogs.py b/tests/tests_dialogs/test_dialogs.py index e0e54ea74..eace3450e 100644 --- a/tests/tests_dialogs/test_dialogs.py +++ b/tests/tests_dialogs/test_dialogs.py @@ -24,7 +24,7 @@ def test_wl_dialog(): wl_dialog = wl_dialogs.Wl_Dialog(main, title = 'test') wl_dialog.open() - wl_dialog.set_fixed_height() + wl_dialog.adjust_size() wl_dialog.move_to_center() wl_dialog = wl_dialogs.Wl_Dialog(main, title = 'test', resizable = True) diff --git a/tests/tests_dialogs/test_dialogs_misc.py b/tests/tests_dialogs/test_dialogs_misc.py index a5c0bc57d..e11b6cc73 100644 --- a/tests/tests_dialogs/test_dialogs_misc.py +++ b/tests/tests_dialogs/test_dialogs_misc.py @@ -33,12 +33,6 @@ def test_wl_dialog_progress_process_data(): def test_wl_dialog_progress_download_model(): wl_dialogs_misc.Wl_Dialog_Progress_Download_Model(main).open() -def test_wl_dialog_clr_table(): - wl_dialogs_misc.Wl_Dialog_Clr_Table(main).open() - -def test_wl_dialog_clr_all_tables(): - wl_dialogs_misc.Wl_Dialog_Clr_All_Tables(main).open() - def test_wl_dialog_restart_required(): wl_dialogs_misc.Wl_Dialog_Restart_Required(main).open() @@ -46,6 +40,4 @@ def test_wl_dialog_restart_required(): test_wl_dialog_progress() test_wl_dialog_progress_process_data() test_wl_dialog_progress_download_model() - test_wl_dialog_clr_table() - test_wl_dialog_clr_all_tables() test_wl_dialog_restart_required() diff --git a/tests/tests_nlp/tests_spacy/test_spacy.py b/tests/tests_nlp/tests_spacy/test_spacy.py index 1104f7757..23a79f7a9 100644 --- a/tests/tests_nlp/tests_spacy/test_spacy.py +++ b/tests/tests_nlp/tests_spacy/test_spacy.py @@ -18,7 +18,12 @@ from tests import wl_test_init, wl_test_lang_examples from tests.tests_nlp import test_dependency_parsing, test_lemmatization, test_pos_tagging -from wordless.wl_nlp import wl_nlp_utils, wl_sentence_tokenization, wl_texts, wl_word_tokenization +from wordless.wl_nlp import ( + wl_nlp_utils, + wl_sentence_tokenization, + wl_texts, + wl_word_tokenization +) from wordless.wl_utils import wl_conversion main = wl_test_init.Wl_Test_Main(switch_lang_utils = 'spacy') diff --git a/tests/tests_nlp/tests_stanza/test_stanza.py b/tests/tests_nlp/tests_stanza/test_stanza.py index 6d437bd82..a3b55b279 100644 --- a/tests/tests_nlp/tests_stanza/test_stanza.py +++ b/tests/tests_nlp/tests_stanza/test_stanza.py @@ -17,8 +17,18 @@ # ---------------------------------------------------------------------- from tests import wl_test_init, wl_test_lang_examples -from tests.tests_nlp import test_dependency_parsing, test_lemmatization, test_pos_tagging, test_sentiment_analysis -from wordless.wl_nlp import wl_nlp_utils, wl_sentence_tokenization, wl_texts, wl_word_tokenization +from tests.tests_nlp import ( + test_dependency_parsing, + test_lemmatization, + test_pos_tagging, + test_sentiment_analysis +) +from wordless.wl_nlp import ( + wl_nlp_utils, + wl_sentence_tokenization, + wl_texts, + wl_word_tokenization +) from wordless.wl_utils import wl_conversion main = wl_test_init.Wl_Test_Main(switch_lang_utils = 'stanza') diff --git a/tests/tests_results/test_results_filter.py b/tests/tests_results/test_results_filter.py index c4c249f7c..fb2816fac 100644 --- a/tests/tests_results/test_results_filter.py +++ b/tests/tests_results/test_results_filter.py @@ -64,7 +64,7 @@ def test_add_layouts_filters(): def test_get_filter_min_max(): wl_results_filter.get_filter_min_max(settings = settings, filter_name = 'test') -def test_dialog_results_filter(): +def test_wl_dialog_results_filter(): dialog_results_filter = wl_results_filter.Wl_Dialog_Results_Filter( main, table = wl_test_init.Wl_Test_Table(main, tab = 'dependency_parser') @@ -89,7 +89,7 @@ def test_dialog_results_filter(): dialog_results_filter.file_to_filter_changed() dialog_results_filter.show() -def test_dialog_results_filter_dependency_parser(): +def test_wl_dialog_results_filter_dependency_parser(): dialog_results_filter_dependency_parser = wl_results_filter.Wl_Dialog_Results_Filter_Dependency_Parser( main, table = wl_test_init.Wl_Test_Table(main, tab = 'dependency_parser') @@ -98,7 +98,7 @@ def test_dialog_results_filter_dependency_parser(): dialog_results_filter_dependency_parser.load_settings(defaults = True) dialog_results_filter_dependency_parser.load_settings(defaults = False) -def test_dialog_results_filter_wordlist_generator(): +def test_wl_dialog_results_filter_wordlist_generator(): dialog_results_filter_wordlist_generator = wl_results_filter.Wl_Dialog_Results_Filter_Wordlist_Generator( main, table = wl_test_init.Wl_Test_Table(main, tab = 'wordlist_generator') @@ -111,7 +111,7 @@ def test_dialog_results_filter_wordlist_generator(): table = wl_test_init.Wl_Test_Table(main, tab = 'ngram_generator') ) -def test_dialog_results_filter_collocation_extractor(): +def test_wl_dialog_results_filter_collocation_extractor(): dialog_results_filter_collocation_extractor = wl_results_filter.Wl_Dialog_Results_Filter_Collocation_Extractor( main, table = wl_test_init.Wl_Test_Table(main, tab = 'collocation_extractor') @@ -135,7 +135,7 @@ def test_dialog_results_filter_collocation_extractor(): test_add_layouts_filters() test_get_filter_min_max() - test_dialog_results_filter() - test_dialog_results_filter_dependency_parser() - test_dialog_results_filter_wordlist_generator() - test_dialog_results_filter_collocation_extractor() + test_wl_dialog_results_filter() + test_wl_dialog_results_filter_dependency_parser() + test_wl_dialog_results_filter_wordlist_generator() + test_wl_dialog_results_filter_collocation_extractor() diff --git a/tests/tests_results/test_results_search.py b/tests/tests_results/test_results_search.py index c9e06827e..b7de64cb9 100644 --- a/tests/tests_results/test_results_search.py +++ b/tests/tests_results/test_results_search.py @@ -21,7 +21,7 @@ main = wl_test_init.Wl_Test_Main() -def test_dialog_results_search(): +def test_wl_dialog_results_search(): table = wl_test_init.Wl_Test_Table(main, tab = 'dependency_parser') table.settings['file_area']['files_open'] = [{'selected': True, 'lang': 'test'}] @@ -48,4 +48,4 @@ def test_dialog_results_search(): dialog_results_search.clr_history() if __name__ == '__main__': - test_dialog_results_search() + test_wl_dialog_results_search() diff --git a/tests/tests_results/test_results_sort.py b/tests/tests_results/test_results_sort.py index 656ad6042..6bb158e95 100644 --- a/tests/tests_results/test_results_sort.py +++ b/tests/tests_results/test_results_sort.py @@ -24,7 +24,7 @@ main = wl_test_init.Wl_Test_Main() table = wl_test_init.Wl_Test_Table(main, tab = 'concordancer') -def test_dialog_results_sort_concordancer(): +def test_wl_dialog_results_sort_concordancer(): dialog_results_sort_concordancer = wl_results_sort.Wl_Dialog_Results_Sort_Concordancer( main, table = table @@ -45,7 +45,7 @@ def test_dialog_results_sort_concordancer(): dialog_results_sort_concordancer.update_gui([]) def test_table_results_sort_concordancer(): - table_results_sort_concordancer = wl_results_sort.Wl_Table_Results_Sort_Conordancer( + table_results_sort_concordancer = wl_results_sort.Table_Results_Sort_Conordancer( main, table = table ) @@ -96,5 +96,5 @@ def test_table_results_sort_concordancer(): table_results_sort_concordancer.load_settings(defaults = False) if __name__ == '__main__': - test_dialog_results_sort_concordancer() + test_wl_dialog_results_sort_concordancer() test_table_results_sort_concordancer() diff --git a/tests/wl_test_init.py b/tests/wl_test_init.py index f63fc35dd..f92a09422 100644 --- a/tests/wl_test_init.py +++ b/tests/wl_test_init.py @@ -26,8 +26,13 @@ from PyQt5.QtCore import QObject from PyQt5.QtGui import QStandardItemModel, QStandardItem from PyQt5.QtWidgets import ( - QApplication, QLabel, QMainWindow, QPushButton, QStatusBar, - QTableView, QTabWidget + QApplication, + QLabel, + QMainWindow, + QPushButton, + QStatusBar, + QTableView, + QTabWidget ) from tests import wl_test_file_area diff --git a/wordless/wl_colligation_extractor.py b/wordless/wl_colligation_extractor.py index 1e96bbbf8..fb446386a 100644 --- a/wordless/wl_colligation_extractor.py +++ b/wordless/wl_colligation_extractor.py @@ -32,9 +32,19 @@ from wordless.wl_checks import wl_checks_work_area from wordless.wl_dialogs import wl_dialogs_misc from wordless.wl_figs import wl_figs, wl_figs_freqs, wl_figs_stats -from wordless.wl_nlp import wl_matching, wl_nlp_utils, wl_texts, wl_token_processing +from wordless.wl_nlp import ( + wl_matching, + wl_nlp_utils, + wl_texts, + wl_token_processing +) from wordless.wl_utils import wl_misc, wl_sorting, wl_threading -from wordless.wl_widgets import wl_boxes, wl_layouts, wl_tables, wl_widgets +from wordless.wl_widgets import ( + wl_boxes, + wl_layouts, + wl_tables, + wl_widgets +) _tr = QCoreApplication.translate diff --git a/wordless/wl_collocation_extractor.py b/wordless/wl_collocation_extractor.py index 5173d7c56..bf17b9cb9 100644 --- a/wordless/wl_collocation_extractor.py +++ b/wordless/wl_collocation_extractor.py @@ -32,9 +32,19 @@ from wordless.wl_checks import wl_checks_work_area from wordless.wl_dialogs import wl_dialogs_misc from wordless.wl_figs import wl_figs, wl_figs_freqs, wl_figs_stats -from wordless.wl_nlp import wl_matching, wl_nlp_utils, wl_texts, wl_token_processing +from wordless.wl_nlp import ( + wl_matching, + wl_nlp_utils, + wl_texts, + wl_token_processing +) from wordless.wl_utils import wl_misc, wl_sorting, wl_threading -from wordless.wl_widgets import wl_boxes, wl_layouts, wl_tables, wl_widgets +from wordless.wl_widgets import ( + wl_boxes, + wl_layouts, + wl_tables, + wl_widgets +) _tr = QCoreApplication.translate diff --git a/wordless/wl_concordancer.py b/wordless/wl_concordancer.py index 6fb640400..ed375579e 100644 --- a/wordless/wl_concordancer.py +++ b/wordless/wl_concordancer.py @@ -26,14 +26,32 @@ import matplotlib.pyplot import numpy from PyQt5.QtCore import pyqtSignal, QCoreApplication, Qt -from PyQt5.QtWidgets import QCheckBox, QLabel, QLineEdit, QGroupBox, QStackedWidget +from PyQt5.QtWidgets import ( + QCheckBox, + QLabel, + QLineEdit, + QGroupBox, + QStackedWidget +) from wordless.wl_checks import wl_checks_work_area from wordless.wl_dialogs import wl_dialogs_misc from wordless.wl_figs import wl_figs -from wordless.wl_nlp import wl_matching, wl_nlp_utils, wl_texts, wl_token_processing, wl_sentiment_analysis +from wordless.wl_nlp import ( + wl_matching, + wl_nlp_utils, + wl_texts, + wl_token_processing, + wl_sentiment_analysis +) from wordless.wl_utils import wl_misc, wl_threading -from wordless.wl_widgets import wl_boxes, wl_labels, wl_layouts, wl_tables, wl_widgets +from wordless.wl_widgets import ( + wl_boxes, + wl_labels, + wl_layouts, + wl_tables, + wl_widgets +) _tr = QCoreApplication.translate diff --git a/wordless/wl_concordancer_parallel.py b/wordless/wl_concordancer_parallel.py index c36c9e946..7575f4ad8 100644 --- a/wordless/wl_concordancer_parallel.py +++ b/wordless/wl_concordancer_parallel.py @@ -27,9 +27,19 @@ from wordless.wl_checks import wl_checks_work_area from wordless.wl_dialogs import wl_dialogs_misc, wl_msg_boxes -from wordless.wl_nlp import wl_matching, wl_nlp_utils, wl_texts, wl_token_processing +from wordless.wl_nlp import ( + wl_matching, + wl_nlp_utils, + wl_texts, + wl_token_processing +) from wordless.wl_utils import wl_misc, wl_threading -from wordless.wl_widgets import wl_labels, wl_layouts, wl_tables, wl_widgets +from wordless.wl_widgets import ( + wl_labels, + wl_layouts, + wl_tables, + wl_widgets +) _tr = QCoreApplication.translate diff --git a/wordless/wl_dependency_parser.py b/wordless/wl_dependency_parser.py index 70cf1876c..3cb8c54d4 100644 --- a/wordless/wl_dependency_parser.py +++ b/wordless/wl_dependency_parser.py @@ -29,9 +29,19 @@ from wordless.wl_checks import wl_checks_work_area from wordless.wl_dialogs import wl_dialogs_misc -from wordless.wl_nlp import wl_dependency_parsing, wl_matching, wl_texts, wl_token_processing +from wordless.wl_nlp import ( + wl_dependency_parsing, + wl_matching, + wl_texts, + wl_token_processing +) from wordless.wl_utils import wl_misc, wl_threading -from wordless.wl_widgets import wl_labels, wl_layouts, wl_tables, wl_widgets +from wordless.wl_widgets import ( + wl_labels, + wl_layouts, + wl_tables, + wl_widgets +) _tr = QCoreApplication.translate diff --git a/wordless/wl_dialogs/wl_dialogs.py b/wordless/wl_dialogs/wl_dialogs.py index 41ca92074..6a4419f95 100644 --- a/wordless/wl_dialogs/wl_dialogs.py +++ b/wordless/wl_dialogs/wl_dialogs.py @@ -18,18 +18,29 @@ from PyQt5.QtCore import QCoreApplication, Qt from PyQt5.QtGui import QIcon -from PyQt5.QtWidgets import QDialog, QPlainTextEdit, QPushButton, QTextEdit, QWidget - -from wordless.wl_utils import wl_paths +from PyQt5.QtWidgets import ( + QApplication, + QDialog, + QPlainTextEdit, + QPushButton, + QTextEdit, + QWidget +) + +from wordless.wl_dialogs import wl_msg_boxes +from wordless.wl_utils import wl_paths, wl_misc from wordless.wl_widgets import wl_buttons _tr = QCoreApplication.translate +is_windows, is_macos, is_linux = wl_misc.check_os() + class Wl_Dialog(QDialog): - def __init__(self, main, title, width = 0, height = 0, resizable = False): + def __init__(self, main, title, width = 0, height = 0, resizable = True): super().__init__(main) self.main = main + self.fixed_width = width # Dialog size if resizable: @@ -61,6 +72,7 @@ def __init__(self, main, title, width = 0, height = 0, resizable = False): color: #FFF; font-weight: bold; } + QHeaderView::section:horizontal { background-color: #5C88C5; } @@ -68,17 +80,25 @@ def __init__(self, main, title, width = 0, height = 0, resizable = False): background-color: #3265B2; } QHeaderView::section:horizontal:pressed { - background-color: #3265B2; + background-color: #264E8C; } ''') - def set_fixed_height(self): - self.setFixedHeight(self.heightForWidth(self.width())) + def adjust_size(self): + self.adjustSize() + + if self.fixed_width: + self.resize(self.fixed_width, self.heightForWidth(self.fixed_width)) + else: + self.resize(self.width(), self.heightForWidth(self.width())) + + if is_windows or is_linux: + self.move_to_center() def move_to_center(self): self.move( - int((self.main.width() - self.width()) / 2), - int((self.main.height() - self.height()) / 2) + int((QApplication.primaryScreen().size().width() - self.width()) / 2), + int((QApplication.primaryScreen().size().height() - self.height()) / 2) ) class Wl_Dialog_Frameless(Wl_Dialog): @@ -99,7 +119,7 @@ def __init__(self, main, width = 0, height = 0): ''') class Wl_Dialog_Info(Wl_Dialog): - def __init__(self, main, title, width = 0, height = 0, resizable = False, no_buttons = False): + def __init__(self, main, title, width = 0, height = 0, resizable = True, icon = True, no_buttons = False): # Avoid circular imports from wordless.wl_widgets import wl_layouts # pylint: disable=import-outside-toplevel @@ -115,13 +135,25 @@ def __init__(self, main, title, width = 0, height = 0, resizable = False, no_but } ''') - self.wrapper_info.setLayout(wl_layouts.Wl_Layout()) - self.wrapper_info.layout().setContentsMargins(20, 10, 20, 10) + if icon: + self.layout_info = wl_layouts.Wl_Layout() + self.wrapper_info.setLayout(wl_layouts.Wl_Layout()) + self.wrapper_info.layout().addWidget(wl_msg_boxes.get_msg_box_icon('information'), 0, 0, Qt.AlignTop) + self.wrapper_info.layout().addLayout(self.layout_info, 0, 1) + + self.wrapper_info.layout().setHorizontalSpacing(20) + self.wrapper_info.layout().setColumnStretch(1, 1) + else: + self.layout_info = wl_layouts.Wl_Layout() + self.wrapper_info.setLayout(self.layout_info) + + self.wrapper_info.layout().setContentsMargins(15, 15, 15, 15) self.wrapper_buttons = QWidget(self) - self.wrapper_buttons.setLayout(wl_layouts.Wl_Layout()) - self.wrapper_buttons.layout().setContentsMargins(11, 0, 11, 11) + self.layout_buttons = wl_layouts.Wl_Layout() + self.wrapper_buttons.setLayout(self.layout_buttons) + self.wrapper_buttons.layout().setContentsMargins(13, 1, 13, 13) if not no_buttons: self.button_ok = QPushButton(_tr('Wl_Dialog_Settings', 'OK'), self) @@ -137,11 +169,22 @@ def __init__(self, main, title, width = 0, height = 0, resizable = False, no_but self.layout().setRowStretch(0, 1) self.layout().setContentsMargins(0, 0, 0, 0) + def adjust_size(self): + self.wrapper_info.adjustSize() + self.wrapper_buttons.adjustSize() + + super().adjust_size() + class Wl_Dialog_Info_Copy(Wl_Dialog_Info): - def __init__(self, main, title, width = 0, height = 0, resizable = False, is_plain_text = False): + def __init__( + self, main, + title, + width = 0, height = 0, + resizable = True, is_plain_text = False + ): super().__init__( main, title, width, height, resizable, - no_buttons = True + no_buttons = True, icon = False ) self.is_plain_text = is_plain_text @@ -182,8 +225,8 @@ def set_info(self, text): self.text_edit_info.setHtml(text) class Wl_Dialog_Settings(Wl_Dialog_Info): - def __init__(self, main, title, width = 0, height = 0): - super().__init__(main, title, width, height, no_buttons = True) + def __init__(self, main, title, width = 0, height = 0, resizable = True): + super().__init__(main, title, width, height, resizable, no_buttons = True, icon = False) # Alias self.wrapper_settings = self.wrapper_info diff --git a/wordless/wl_dialogs/wl_dialogs_errs.py b/wordless/wl_dialogs/wl_dialogs_errs.py index fd498f3bf..472196213 100644 --- a/wordless/wl_dialogs/wl_dialogs_errs.py +++ b/wordless/wl_dialogs/wl_dialogs_errs.py @@ -25,6 +25,13 @@ _tr = QCoreApplication.translate class Wl_Dialog_Err(wl_dialogs.Wl_Dialog_Info): + def __init__(self, main, title, width = 0, height = 0, resizable = True, no_buttons = False): + super().__init__( + main, title, + width = width, height = height, + resizable = resizable, icon = False, no_buttons = no_buttons + ) + def exec_(self): super().exec_() @@ -35,16 +42,29 @@ def open(self): QApplication.beep() -class Wl_Dialog_Err_Info_Copy(Wl_Dialog_Err, wl_dialogs.Wl_Dialog_Info_Copy): - def __init__(self, main, title, width = 0, height = 0, resizable = False, help_info = '', err_msg = ''): - super().__init__(main, title, width = width, height = height, resizable = resizable) +class Wl_Dialog_Err_Info_Copy(wl_dialogs.Wl_Dialog_Info_Copy): + def __init__(self, main, title, width = 0, height = 0, resizable = True, help_info = '', err_msg = ''): + super().__init__( + main, title, + width = width, height = height, resizable = resizable + ) self.label_err_msg = wl_labels.Wl_Label_Dialog(help_info, self) self.text_edit_info.setPlainText(err_msg) - self.wrapper_info.layout().addWidget(self.label_err_msg, 0, 0) - self.wrapper_info.layout().addWidget(self.text_edit_info, 1, 0) + self.layout_info.addWidget(self.label_err_msg, 0, 0) + self.layout_info.addWidget(self.text_edit_info, 1, 0) + + def exec_(self): + super().exec_() + + QApplication.beep() + + def open(self): + super().open() + + QApplication.beep() class Wl_Dialog_Err_Fatal(Wl_Dialog_Err_Info_Copy): def __init__(self, main, err_msg): @@ -53,7 +73,6 @@ def __init__(self, main, err_msg): title = _tr('wl_dialogs_errs', 'Fatal Error'), width = 600, height = 300, - resizable = True, help_info = _tr('wl_dialogs_errs', '''
A fatal error has occurred, please send the following error messages to {} in order to contact the author for support!
''').format(main.email_html), @@ -67,7 +86,6 @@ def __init__(self, main, err_msg): title = _tr('wl_dialogs_errs', 'Network Error'), width = 600, height = 400, - resizable = True, help_info = _tr('wl_dialogs_errs', '''
A network error occurred while downloading the model, please check your internet connections and proxy settings in Menu β†’ Preferences β†’ General β†’ Proxy Settings if you are using a proxy.
If the network issue persists, please send the following error messages to {} in order to contact the author for support.
@@ -77,7 +95,7 @@ def __init__(self, main, err_msg): class Wl_Dialog_Err_Files(Wl_Dialog_Err): def __init__(self, main, title): - super().__init__(main, title, width = 580, height = 260, no_buttons = True) + super().__init__(main, title, width = 650, height = 350, no_buttons = True) self.label_err = wl_labels.Wl_Label_Dialog('', self) self.table_err_files = wl_tables.Wl_Table( @@ -98,8 +116,8 @@ def __init__(self, main, title): self.button_exp_table.clicked.connect(self.table_err_files.exp_all_cells) self.button_ok.clicked.connect(self.accept) - self.wrapper_info.layout().addWidget(self.label_err, 0, 0) - self.wrapper_info.layout().addWidget(self.table_err_files, 1, 0) + self.layout_info.addWidget(self.label_err, 0, 0) + self.layout_info.addWidget(self.table_err_files, 1, 0) - self.wrapper_buttons.layout().addWidget(self.button_exp_table, 0, 0, Qt.AlignLeft) - self.wrapper_buttons.layout().addWidget(self.button_ok, 0, 1, Qt.AlignRight) + self.layout_buttons.addWidget(self.button_exp_table, 0, 0, Qt.AlignLeft) + self.layout_buttons.addWidget(self.button_ok, 0, 1, Qt.AlignRight) diff --git a/wordless/wl_dialogs/wl_dialogs_misc.py b/wordless/wl_dialogs/wl_dialogs_misc.py index a266ebaec..4ccc3eb27 100644 --- a/wordless/wl_dialogs/wl_dialogs_misc.py +++ b/wordless/wl_dialogs/wl_dialogs_misc.py @@ -35,9 +35,13 @@ def __init__(self, main, text): self.timer_time_elapsed = QTimer(self) - self.label_progress = QLabel(text, self) - self.label_time_elapsed = QLabel(_tr('Wl_Dialog_Progress', 'Elapsed time: 0:00:00'), self) - self.label_processing = wl_labels.Wl_Label_Dialog(_tr('Wl_Dialog_Progress', 'Please wait. It may take a few seconds to several minutes for the operation to be completed.'), self) + self.label_progress = wl_labels.Wl_Label_Dialog(text, self, word_wrap = False) + self.label_time_elapsed = wl_labels.Wl_Label_Dialog(self.tr('
Elapsed time: 0:00:00
'), self, word_wrap = False) + self.label_processing = wl_labels.Wl_Label_Dialog(self.tr(''' +
Please wait. It may take a few seconds to several minutes for the operation to be completed.
+ '''), + self + ) self.timer_time_elapsed.timeout.connect(self.update_elapsed_time) self.timer_time_elapsed.start(1000) @@ -47,68 +51,23 @@ def __init__(self, main, text): self.layout().addWidget(self.label_time_elapsed, 0, 1, Qt.AlignRight) self.layout().addWidget(self.label_processing, 1, 0, 1, 2) - self.layout().setContentsMargins(20, 10, 20, 10) + self.layout().setContentsMargins(20, 20, 20, 20) def update_elapsed_time(self): - self.label_time_elapsed.setText( - _tr('Wl_Dialog_Progress', 'Elapsed time: ') - + str(datetime.timedelta(seconds = round(time.time() - self.time_start))) - ) + elapsed_time = datetime.timedelta(seconds = round(time.time() - self.time_start)) + + self.label_time_elapsed.set_text(self.tr('
Elapsed time: {}
').format(elapsed_time)) def update_progress(self, text): - self.label_progress.setText(text) + self.label_progress.set_text(text) class Wl_Dialog_Progress_Process_Data(Wl_Dialog_Progress): def __init__(self, main): - super().__init__(main, text = _tr('Wl_Dialog_Progress_Process_Data', 'Processing data...')) + super().__init__(main, text = _tr('wl_dialogs_misc', 'Processing data...')) class Wl_Dialog_Progress_Download_Model(Wl_Dialog_Progress): def __init__(self, main): - super().__init__(main, text = _tr('Wl_Dialog_Progress_Process_Data', 'Downloading model...')) - -class Wl_Dialog_Clr_Table(wl_dialogs.Wl_Dialog_Info): - def __init__(self, main): - super().__init__( - main, - title = _tr('WL_Dialog_Clear_Table', 'Clear Table'), - width = 450, - no_buttons = True - ) - - self.label_confirm_clr = wl_labels.Wl_Label_Dialog( - self.tr(''' -
- The results in the table have yet been exported. Do you really want to clear the table? -
- '''), - self - ) - - self.button_yes = QPushButton(self.tr('Yes'), self) - self.button_no = QPushButton(self.tr('No'), self) - - self.button_yes.clicked.connect(self.accept) - self.button_no.clicked.connect(self.reject) - - self.wrapper_info.layout().addWidget(self.label_confirm_clr, 0, 0) - - self.wrapper_buttons.layout().addWidget(self.button_yes, 0, 1) - self.wrapper_buttons.layout().addWidget(self.button_no, 0, 2) - - self.wrapper_buttons.layout().setColumnStretch(0, 1) - - self.set_fixed_height() - -class Wl_Dialog_Clr_All_Tables(Wl_Dialog_Clr_Table): - def __init__(self, main): - super().__init__(main) - - self.setWindowTitle(self.tr('Clear All Tables')) - self.label_confirm_clr.setText(self.tr(''' -
- The results in some of the tables have yet been exported. Do you really want to clear all tables? -
- ''')) + super().__init__(main, text = _tr('wl_dialogs_misc', 'Downloading model...')) class Wl_Dialog_Restart_Required(wl_dialogs.Wl_Dialog_Info): def __init__(self, main): @@ -124,10 +83,8 @@ def __init__(self, main):
Restart is required for the settings to take effect. Do you want to restart Wordless now?
- -
- Note: All unsaved data and figures will be lost. -
+
+
Note: All unsaved data and figures will be lost.
'''), self ) @@ -138,11 +95,9 @@ def __init__(self, main): self.button_restart.clicked.connect(self.accept) self.button_cancel.clicked.connect(self.reject) - self.wrapper_info.layout().addWidget(self.label_restart_exit, 0, 0) - - self.wrapper_buttons.layout().addWidget(self.button_restart, 0, 1) - self.wrapper_buttons.layout().addWidget(self.button_cancel, 0, 2) + self.layout_info.addWidget(self.label_restart_exit, 0, 0) - self.wrapper_buttons.layout().setColumnStretch(0, 1) + self.layout_buttons.addWidget(self.button_restart, 0, 1) + self.layout_buttons.addWidget(self.button_cancel, 0, 2) - self.set_fixed_height() + self.layout_buttons.setColumnStretch(0, 1) diff --git a/wordless/wl_dialogs/wl_msg_boxes.py b/wordless/wl_dialogs/wl_msg_boxes.py index bcec3ca25..7fed6bea1 100644 --- a/wordless/wl_dialogs/wl_msg_boxes.py +++ b/wordless/wl_dialogs/wl_msg_boxes.py @@ -16,10 +16,12 @@ # along with this program. If not, see . # ---------------------------------------------------------------------- -from PyQt5.QtCore import QCoreApplication -from PyQt5.QtWidgets import QMessageBox - -_tr = QCoreApplication.translate +from PyQt5.QtWidgets import ( + QApplication, + QLabel, + QMessageBox, + QStyle +) class Wl_Msg_Box(QMessageBox): def __init__(self, main, icon, title, text): @@ -28,7 +30,7 @@ def __init__(self, main, icon, title, text): title, f''' {main.settings_global['styles']['style_dialog']} - + {text} ''', @@ -64,12 +66,29 @@ def wl_msg_box_question(main, title, text, default_to_yes = False): title, f''' {main.settings_global['styles']['style_dialog']} - - {text} - + {text} ''', buttons = QMessageBox.Yes | QMessageBox.No, defaultButton = default_button ) return bool(reply == QMessageBox.Yes) + +def get_msg_box_icon(icon_type = 'information'): + style = QApplication.style() + size = style.pixelMetric(QStyle.PM_MessageBoxIconSize, None, None) + + match icon_type: + case 'information': + icon = style.standardIcon(QStyle.SP_MessageBoxInformation, None, None) + case 'warning': + icon = style.standardIcon(QStyle.SP_MessageBoxWarning, None, None) + case 'critical': + icon = style.standardIcon(QStyle.SP_MessageBoxCritical, None, None) + case 'question': + icon = style.standardIcon(QStyle.SP_MessageBoxQuestion, None, None) + + label = QLabel('') + label.setPixmap(icon.pixmap(size, size)) + + return label diff --git a/wordless/wl_file_area.py b/wordless/wl_file_area.py index 6b1a9283e..25a7ceb83 100644 --- a/wordless/wl_file_area.py +++ b/wordless/wl_file_area.py @@ -29,18 +29,48 @@ import openpyxl import pptx import pypdf -from PyQt5.QtCore import pyqtSignal, QCoreApplication, QItemSelection, QRect, Qt +from PyQt5.QtCore import ( + pyqtSignal, + QCoreApplication, + QItemSelection, + QRect, + Qt +) from PyQt5.QtGui import QStandardItem from PyQt5.QtWidgets import ( - QAbstractItemDelegate, QCheckBox, QFileDialog, QHeaderView, QLineEdit, - QPushButton, QStyle, QStyleOptionButton + QAbstractItemDelegate, + QCheckBox, + QFileDialog, + QHeaderView, + QLineEdit, + QPushButton, + QStyle, + QStyleOptionButton ) from wordless.wl_checks import wl_checks_files, wl_checks_misc -from wordless.wl_dialogs import wl_dialogs, wl_dialogs_errs, wl_dialogs_misc, wl_msg_boxes +from wordless.wl_dialogs import ( + wl_dialogs, + wl_dialogs_errs, + wl_dialogs_misc, + wl_msg_boxes +) from wordless.wl_nlp import wl_matching, wl_nlp_utils, wl_texts -from wordless.wl_utils import wl_conversion, wl_detection, wl_misc, wl_paths, wl_threading -from wordless.wl_widgets import wl_boxes, wl_buttons, wl_item_delegates, wl_layouts, wl_tables +from wordless.wl_utils import ( + wl_conversion, + wl_detection, + wl_misc, + wl_paths, + wl_threading +) +from wordless.wl_widgets import ( + wl_boxes, + wl_buttons, + wl_item_delegates, + wl_labels, + wl_layouts, + wl_tables +) _tr = QCoreApplication.translate @@ -208,7 +238,6 @@ def __init__(self, parent): # Menu self.main.action_file_open_files.triggered.connect(lambda: self.check_file_area(self.open_files)) - self.main.action_file_open_dir.triggered.connect(lambda: self.check_file_area(self.open_dir)) self.main.action_file_reopen.triggered.connect(lambda: self.check_file_area(self.reopen)) self.main.action_file_select_all.triggered.connect(lambda: self.check_file_area(self.horizontalHeader().select_all)) @@ -401,21 +430,13 @@ def update_gui(self, err_msg, new_files): self.main.statusBar().showMessage(self.tr('{} {} has been successfully opened.').format(len_files_opened, msg_file)) def open_files(self): - self.dialog_open_files = Dialog_Open_Files(self.main) + self.dialog_open_files = Wl_Dialog_Open_Files(self.main) self.dialog_open_files.open() - self.dialog_open_files.table_files.button_add_files.click() - - def open_dir(self): - self.dialog_open_files = Dialog_Open_Files(self.main) - self.dialog_open_files.open() - - self.dialog_open_files.table_files.button_add_folder.click() - def reopen(self): files = self.main.settings_custom['file_area'][f'files_closed{self.settings_suffix}'].pop() - dialog_open_files = Dialog_Open_Files(self.main) + dialog_open_files = Wl_Dialog_Open_Files(self.main) dialog_open_files._add_files(list(dict.fromkeys([file['path_original'] for file in files]))) self._open_files(files_to_open = dialog_open_files.table_files.files_to_open) @@ -440,14 +461,13 @@ def close_selected(self): def close_all(self): self._close_files(list(range(len(self.main.settings_custom['file_area'][f'files_open{self.settings_suffix}'])))) -class Dialog_Open_Files(wl_dialogs.Wl_Dialog): +class Wl_Dialog_Open_Files(wl_dialogs.Wl_Dialog): def __init__(self, main): super().__init__( main, - title = _tr('Dialog_Open_Files', 'Open Files'), + title = _tr('Wl_Dialog_Open_Files', 'Open Files'), width = 800, - height = 320, - resizable = True + height = 320 ) self.table_files = Table_Open_Files(self) @@ -572,13 +592,26 @@ def _add_files(self, file_paths): ] ) - wl_threading.Wl_Thread(Wl_Worker_Add_Files( - self.main, - dialog_progress = dialog_progress, - update_gui = self.update_gui, - file_paths = file_paths, - table = self.table_files - )).start_worker() + # Display warning when opening non-text files + if ( + any(( + os.path.splitext(file_path)[1].lower() not in ['.csv', '.lrc', '.txt', '.tmx', '.xml'] + for file_path in file_paths + )) + and self.main.settings_custom['files']['misc_settings']['display_warning_when_opening_nontext_files'] + ): + non_text_files_ok = Wl_Dialog_Opening_Nontext_Files(self.main).exec_() + else: + non_text_files_ok = True + + if non_text_files_ok: + wl_threading.Wl_Thread(Wl_Worker_Add_Files( + self.main, + dialog_progress = dialog_progress, + update_gui = self.update_gui, + file_paths = file_paths, + table = self.table_files + )).start_worker() def update_gui(self, err_msg, new_files): if wl_checks_files.check_err_file_area(self.main, err_msg): @@ -748,6 +781,52 @@ def update_table(self): else: self.clr_table() +class Wl_Dialog_Opening_Nontext_Files(wl_dialogs.Wl_Dialog_Info): + def __init__(self, main): + super().__init__( + main, + title = _tr('Wl_Dialog_Opening_Nontext_Files', 'Opening Non-text Files'), + width = 550, + no_buttons = True + ) + + self.label_opening_non_text_files = wl_labels.Wl_Label_Dialog( + self.tr(''' +
It is not recommended to directly import non-text files into Wordless and the support for doing so is provided only for convenience, since accuracy of text extraction could never be guaranteed and unintended data loss might occur, for which reason users are encouraged to convert their files using specialized tools and make their own choices on which part of the data should be kept or discarded.
+
+
Do you want to proceed to open non-text files anyway?
+ '''), + self + ) + + self.checkbox_dont_show_this_again = QCheckBox(self.tr('Do not show this again'), self) + self.button_proceed = QPushButton(self.tr('Proceed'), self) + self.button_abort = QPushButton(self.tr('Abort'), self) + + self.checkbox_dont_show_this_again.stateChanged.connect(self.dont_show_this_again_changed) + self.button_proceed.clicked.connect(self.accept) + self.button_abort.clicked.connect(self.reject) + + self.layout_info.addWidget(self.label_opening_non_text_files, 0, 0) + + self.layout_buttons.addWidget(self.checkbox_dont_show_this_again, 0, 0) + self.layout_buttons.addWidget(self.button_proceed, 0, 2) + self.layout_buttons.addWidget(self.button_abort, 0, 3) + + self.layout_buttons.setColumnStretch(1, 1) + + self.load_settings() + + def load_settings(self): + settings = copy.deepcopy(self.main.settings_custom['files']['misc_settings']) + + self.checkbox_dont_show_this_again.setChecked(settings['display_warning_when_opening_nontext_files']) + + def dont_show_this_again_changed(self): + settings = self.main.settings_custom['files']['misc_settings'] + + settings['display_warning_when_opening_nontext_files'] = self.checkbox_dont_show_this_again.isChecked() + class Wl_Worker_Add_Files(wl_threading.Wl_Worker): worker_done = pyqtSignal(str, list) diff --git a/wordless/wl_keyword_extractor.py b/wordless/wl_keyword_extractor.py index 003d6f202..6a84c3dde 100644 --- a/wordless/wl_keyword_extractor.py +++ b/wordless/wl_keyword_extractor.py @@ -32,7 +32,12 @@ from wordless.wl_measures import wl_measure_utils from wordless.wl_nlp import wl_texts, wl_token_processing from wordless.wl_utils import wl_misc, wl_sorting, wl_threading -from wordless.wl_widgets import wl_boxes, wl_layouts, wl_tables, wl_widgets +from wordless.wl_widgets import ( + wl_boxes, + wl_layouts, + wl_tables, + wl_widgets +) _tr = QCoreApplication.translate diff --git a/wordless/wl_main.py b/wordless/wl_main.py index 25b8c5462..9b7e008b7 100644 --- a/wordless/wl_main.py +++ b/wordless/wl_main.py @@ -43,11 +43,31 @@ import matplotlib import nltk import packaging.version -from PyQt5.QtCore import pyqtSignal, QCoreApplication, QObject, Qt, QTranslator -from PyQt5.QtGui import QFont, QIcon, QKeySequence, QPixmap, QStandardItem +from PyQt5.QtCore import ( + pyqtSignal, + QCoreApplication, + QObject, + Qt, + QTranslator +) +from PyQt5.QtGui import ( + QFont, + QIcon, + QKeySequence, + QPixmap, + QStandardItem +) from PyQt5.QtWidgets import ( - QActionGroup, QApplication, QCheckBox, QDialog, QLabel, - QMainWindow, QPushButton, QSplashScreen, QTabWidget, QWidget + QActionGroup, + QApplication, + QCheckBox, + QDialog, + QLabel, + QMainWindow, + QPushButton, + QSplashScreen, + QTabWidget, + QWidget ) import pythainlp import spacy_pkuseg @@ -72,7 +92,13 @@ from wordless.wl_dialogs import wl_dialogs, wl_dialogs_misc, wl_msg_boxes from wordless.wl_settings import wl_settings, wl_settings_default, wl_settings_global from wordless.wl_utils import wl_misc, wl_paths, wl_threading -from wordless.wl_widgets import wl_boxes, wl_editors, wl_labels, wl_layouts, wl_tables +from wordless.wl_widgets import ( + wl_boxes, + wl_editors, + wl_labels, + wl_layouts, + wl_tables +) # Modify paths of data files when frozen if getattr(sys, '_MEIPASS', False): @@ -151,43 +177,40 @@ def __init__(self, main): self.label_confirm_exit = wl_labels.Wl_Label_Dialog( self.tr(''' -
- Are you sure you want to exit Wordless? -
-
- Note: All unsaved data and figures will be lost. -
+
Are you sure you want to exit Wordless?
+
+
Note: All unsaved data and figures will be lost.
'''), self ) - self.checkbox_confirm_on_exit = QCheckBox(self.tr('Always confirm on exit'), self) + self.checkbox_always_confirm_on_exit = QCheckBox(self.tr('Always confirm on exit'), self) self.button_exit = QPushButton(self.tr('Exit'), self) self.button_cancel = QPushButton(self.tr('Cancel'), self) - self.checkbox_confirm_on_exit.stateChanged.connect(self.confirm_on_exit_changed) + self.checkbox_always_confirm_on_exit.stateChanged.connect(self.always_confirm_on_exit_changed) self.button_exit.clicked.connect(self.accept) self.button_cancel.clicked.connect(self.reject) - self.wrapper_info.layout().addWidget(self.label_confirm_exit, 0, 0) + self.layout_info.addWidget(self.label_confirm_exit, 0, 0) - self.wrapper_buttons.layout().addWidget(self.checkbox_confirm_on_exit, 0, 0) - self.wrapper_buttons.layout().addWidget(self.button_exit, 0, 2) - self.wrapper_buttons.layout().addWidget(self.button_cancel, 0, 3) + self.layout_buttons.addWidget(self.checkbox_always_confirm_on_exit, 0, 0) + self.layout_buttons.addWidget(self.button_exit, 0, 2) + self.layout_buttons.addWidget(self.button_cancel, 0, 3) - self.wrapper_buttons.layout().setColumnStretch(1, 1) + self.layout_buttons.setColumnStretch(1, 1) self.load_settings() def load_settings(self): settings = copy.deepcopy(self.main.settings_custom['general']['misc_settings']) - self.checkbox_confirm_on_exit.setChecked(settings['confirm_on_exit']) + self.checkbox_always_confirm_on_exit.setChecked(settings['always_confirm_on_exit']) - def confirm_on_exit_changed(self): + def always_confirm_on_exit_changed(self): settings = self.main.settings_custom['general']['misc_settings'] - settings['confirm_on_exit'] = self.checkbox_confirm_on_exit.isChecked() + settings['always_confirm_on_exit'] = self.checkbox_always_confirm_on_exit.isChecked() class Wl_Main(QMainWindow): def __init__(self, loading_window): @@ -273,7 +296,7 @@ def fix_macos_layout(self, parent): widget.setAttribute(Qt.WA_LayoutUsesWidgetRect) def closeEvent(self, event): - if self.settings_custom['general']['misc_settings']['confirm_on_exit']: + if self.settings_custom['general']['misc_settings']['always_confirm_on_exit']: result = Wl_Dialog_Confirm_Exit(self).exec_() if result == QDialog.Accepted: @@ -295,9 +318,7 @@ def init_menu(self): # File self.action_file_open_files = self.menu_file.addAction(self.tr('&Open Files...')) self.action_file_open_files.setShortcut(QKeySequence('Ctrl+O')) - self.action_file_open_files.setStatusTip(self.tr('Open files')) - self.action_file_open_dir = self.menu_file.addAction(self.tr('Open &Folder...')) - self.action_file_open_dir.setStatusTip(self.tr('Open all files in the folder')) + self.action_file_open_files.setStatusTip(self.tr('Open file(s)')) self.action_file_reopen = self.menu_file.addAction(self.tr('&Reopen Closed Files')) self.action_file_reopen.setStatusTip(self.tr('Reopen closed files')) @@ -634,14 +655,13 @@ def __init__(self, main): main, title = _tr('Wl_Dialog_Need_Help', 'Need Help?'), width = 600, - height = 550 + height = 600, + icon = False ) self.label_need_help = wl_labels.Wl_Label_Dialog( self.tr(''' -
- If you have any questions, find software bugs, need to provide feedback, or want to submit feature requests, you may seek support from the open-source community or contact me directly via any of the support channels listed below. -
+
If you have any questions, find software bugs, need to provide feedback, or want to submit feature requests, you may seek support from the open-source community or contact me directly via any of the support channels listed below.
'''), self ) @@ -665,9 +685,12 @@ def __init__(self, main): ) self.table_need_help.setIndexWidget( self.table_need_help.model().index(0, 1), - wl_labels.Wl_Label_Html(self.tr( - f'Stable Version | Development Version' - ), self) + wl_labels.Wl_Label_Html( + self.tr( + f'Stable Version | Development Version' + ), + self + ) ) self.table_need_help.setIndexWidget( @@ -676,9 +699,12 @@ def __init__(self, main): ) self.table_need_help.setIndexWidget( self.table_need_help.model().index(1, 1), - wl_labels.Wl_Label_Html(self.tr( - 'YouTube | bilibili' - ), self) + wl_labels.Wl_Label_Html( + self.tr( + 'YouTube | bilibili' + ), + self + ) ) self.table_need_help.setIndexWidget( @@ -687,9 +713,10 @@ def __init__(self, main): ) self.table_need_help.setIndexWidget( self.table_need_help.model().index(2, 1), - wl_labels.Wl_Label_Html(self.tr( - 'Gihub Issues' - ), self) + wl_labels.Wl_Label_Html( + 'GitHub Issues', + self + ) ) self.table_need_help.setIndexWidget( @@ -698,9 +725,10 @@ def __init__(self, main): ) self.table_need_help.setIndexWidget( self.table_need_help.model().index(3, 1), - wl_labels.Wl_Label_Html(self.tr( - 'Gihub Discussions' - ), self) + wl_labels.Wl_Label_Html( + 'GitHub Discussions', + self + ) ) self.table_need_help.setIndexWidget( @@ -714,9 +742,12 @@ def __init__(self, main): self.table_need_help.setIndexWidget( self.table_need_help.model().index(5, 0), - wl_labels.Wl_Label_Html(self.tr( - 'WeChat official account' - ), self) + wl_labels.Wl_Label_Html( + self.tr( + 'WeChat official account' + ), + self + ) ) self.table_need_help.setIndexWidget( self.table_need_help.model().index(5, 1), @@ -728,24 +759,20 @@ def __init__(self, main): self.table_need_help.enable_updates() - self.wrapper_info.layout().addWidget(self.label_need_help, 0, 0) - self.wrapper_info.layout().addWidget(self.table_need_help, 1, 0) + self.layout_info.addWidget(self.label_need_help, 0, 0) + self.layout_info.addWidget(self.table_need_help, 1, 0) class Wl_Dialog_Citing(wl_dialogs.Wl_Dialog_Info_Copy): def __init__(self, main): super().__init__( main, title = _tr('Wl_Dialog_Citing', 'Citing'), - width = 500, - height = 300, - resizable = True + width = 500 ) self.label_citing = wl_labels.Wl_Label_Dialog( self.tr(''' -
- If you are going to publish a work that uses Wordless, please cite as follows. -
+
If you are going to publish a work that uses Wordless, please cite as follows.
'''), self ) @@ -760,12 +787,13 @@ def __init__(self, main): self.combo_box_select_citation_sys.currentTextChanged.connect(self.select_citation_sys_changed) - self.wrapper_info.layout().addWidget(self.label_citing, 0, 0, 1, 2) - self.wrapper_info.layout().addWidget(self.label_select_citation_sys, 1, 0) - self.wrapper_info.layout().addWidget(self.combo_box_select_citation_sys, 1, 1) - self.wrapper_info.layout().addWidget(self.text_edit_info, 2, 0, 1, 2) + self.layout_info.addWidget(self.label_citing, 0, 0, 1, 2) + self.layout_info.addWidget(self.label_select_citation_sys, 1, 0) + self.layout_info.addWidget(self.combo_box_select_citation_sys, 1, 1) + self.layout_info.addWidget(self.text_edit_info, 2, 0, 1, 2) - self.wrapper_info.layout().setColumnStretch(1, 1) + self.layout_info.setRowStretch(2, 1) + self.layout_info.setColumnStretch(1, 1) self.load_settings() @@ -795,14 +823,13 @@ def __init__(self, main): super().__init__( main, title = _tr('Wl_Dialog_Donating', 'Donating'), - width = 450 + width = 450, + icon = False ) self.label_donating = wl_labels.Wl_Label_Dialog( self.tr(''' -
- If you would like to support the development of Wordless, you may donate via PayPal, Alipay, or WeChat Pay. -
+
If you would like to support the development of Wordless, you may donate via PayPal, Alipay, or WeChat Pay.
'''), self ) @@ -824,9 +851,11 @@ def __init__(self, main): layout_donating_via.setColumnStretch(2, 1) - self.wrapper_info.layout().addWidget(self.label_donating, 0, 0) - self.wrapper_info.layout().addLayout(layout_donating_via, 1, 0) - self.wrapper_info.layout().addWidget(self.label_donating_via_img, 2, 0, Qt.AlignHCenter | Qt.AlignVCenter) + self.layout_info.addWidget(self.label_donating, 0, 0) + self.layout_info.addLayout(layout_donating_via, 1, 0) + self.layout_info.addWidget(self.label_donating_via_img, 2, 0, Qt.AlignHCenter | Qt.AlignVCenter) + + self.layout_info.setRowStretch(2, 1) self.load_settings() @@ -856,11 +885,7 @@ def donating_via_changed(self): ) self.label_donating_via_img.adjustSize() - self.wrapper_info.adjustSize() - self.adjustSize() - - if is_windows or is_linux: - self.move_to_center() + self.adjust_size() class Wl_Dialog_Acks(wl_dialogs.Wl_Dialog_Info): def __init__(self, main): @@ -869,7 +894,7 @@ def __init__(self, main): title = _tr('Wl_Dialog_Acks', 'Acknowledgments'), width = 700, height = 600, - resizable = True + icon = False ) # Load acknowledgments @@ -889,9 +914,7 @@ def __init__(self, main): self.label_acks = wl_labels.Wl_Label_Dialog( self.tr(''' -
- As Wordless stands on the shoulders of giants, I hereby extend my sincere gratitude to the following open-source projects without which this project would not have been possible: -
+
As Wordless stands on the shoulders of giants, I hereby extend my sincere gratitude to the following open-source projects without which this project would not have been possible:
'''), self ) @@ -916,8 +939,8 @@ def __init__(self, main): self.table_acks.enable_updates() - self.wrapper_info.layout().addWidget(self.label_acks, 0, 0) - self.wrapper_info.layout().addWidget(self.table_acks, 1, 0) + self.layout_info.addWidget(self.label_acks, 0, 0) + self.layout_info.addWidget(self.table_acks, 1, 0) class Wl_Dialog_Check_Updates(wl_dialogs.Wl_Dialog_Info): def __init__(self, main, on_startup = False): @@ -925,14 +948,15 @@ def __init__(self, main, on_startup = False): main, title = _tr('Wl_Dialog_Check_Updates', 'Check for Updates'), width = 550, - no_buttons = True + no_buttons = True, + icon = False ) self.on_startup = on_startup - self.label_checking_status = wl_labels.Wl_Label_Dialog('', self) - self.label_cur_ver = wl_labels.Wl_Label_Dialog(self.tr('Current version: ') + str(self.main.ver), self) - self.label_latest_ver = wl_labels.Wl_Label_Dialog('', self) + self.label_checking_status = wl_labels.Wl_Label_Dialog('
', self) + self.label_cur_ver = wl_labels.Wl_Label_Dialog(self.tr('
Current version:
') + str(self.main.ver), self) + self.label_latest_ver = wl_labels.Wl_Label_Dialog('
', self) self.checkbox_check_updates_on_startup = QCheckBox(self.tr('Check for updates on startup'), self) self.button_try_again = QPushButton(self.tr('Try again'), self) @@ -941,15 +965,15 @@ def __init__(self, main, on_startup = False): self.checkbox_check_updates_on_startup.stateChanged.connect(self.check_updates_on_startup_changed) self.button_try_again.clicked.connect(self.check_updates) - self.wrapper_info.layout().addWidget(self.label_checking_status, 0, 0, 2, 1) - self.wrapper_info.layout().addWidget(self.label_cur_ver, 2, 0) - self.wrapper_info.layout().addWidget(self.label_latest_ver, 3, 0) + self.layout_info.addWidget(self.label_checking_status, 0, 0, 2, 1) + self.layout_info.addWidget(self.label_cur_ver, 2, 0) + self.layout_info.addWidget(self.label_latest_ver, 3, 0) - self.wrapper_buttons.layout().addWidget(self.checkbox_check_updates_on_startup, 0, 0) - self.wrapper_buttons.layout().addWidget(self.button_try_again, 0, 2) - self.wrapper_buttons.layout().addWidget(self.button_cancel, 0, 3) + self.layout_buttons.addWidget(self.checkbox_check_updates_on_startup, 0, 0) + self.layout_buttons.addWidget(self.button_try_again, 0, 2) + self.layout_buttons.addWidget(self.button_cancel, 0, 3) - self.wrapper_buttons.layout().setColumnStretch(1, 1) + self.layout_buttons.setColumnStretch(1, 1) #self.set_fixed_height() self.load_settings() @@ -983,12 +1007,8 @@ def checking_status_changed(self, status, ver_new = ''): self.button_try_again.hide() if status == 'checking': - self.label_checking_status.set_text(self.tr(''' -
- Checking for updates... -
- ''')) - self.label_latest_ver.set_text(self.tr('Latest version: Checking...')) + self.label_checking_status.set_text(self.tr('
Checking for updates...
')) + self.label_latest_ver.set_text(self.tr('
Latest version: Checking...
')) self.button_cancel.setText(self.tr('Cancel')) self.button_cancel.disconnect() @@ -997,25 +1017,19 @@ def checking_status_changed(self, status, ver_new = ''): if status in ['updates_available', 'no_updates']: if status == 'updates_available': self.label_checking_status.set_text(self.tr(''' -
- Wordless {} is out, click HERE to download the latest version of Wordless. -
+
Wordless {} is out, click HERE to download the latest version of Wordless.
''').format(ver_new)) - self.label_latest_ver.set_text(self.tr('Latest version: ') + ver_new) + self.label_latest_ver.set_text(self.tr('
Latest version:
') + ver_new) elif status == 'no_updates': self.label_checking_status.set_text(self.tr(''' -
- Hooray, you are using the latest version of Wordless! -
+
Hooray, you are using the latest version of Wordless!
''')) - self.label_latest_ver.set_text(self.tr('Latest version: ') + str(self.main.ver)) + self.label_latest_ver.set_text(self.tr('
Latest version:
') + str(self.main.ver)) elif status == 'network_err': self.label_checking_status.set_text(self.tr(''' -
- A network error has occurred, please check your network settings and try again or check for updates manually. -
+
A network error has occurred, please check your network settings and try again or check for updates manually.
''')) - self.label_latest_ver.set_text(self.tr('Latest version: Network error')) + self.label_latest_ver.set_text(self.tr('
Latest version: Network error
')) self.button_cancel.setText(self.tr('OK')) self.button_cancel.disconnect() @@ -1081,7 +1095,8 @@ def __init__(self, main): main, title = _tr('Wl_Dialog_Changelog', 'Changelog'), width = 600, - height = 600 + height = 600, + icon = False ) changelog = [] @@ -1116,44 +1131,37 @@ def __init__(self, main): font_size_custom = main.settings_custom['general']['ui_settings']['font_size'] changelog_text = f''' - - - - + + ''' for release in changelog: @@ -1191,45 +1199,53 @@ def __init__(self, main): text_edit_changelog = wl_editors.Wl_Text_Browser(self) text_edit_changelog.setHtml(changelog_text) - self.wrapper_info.layout().addWidget(text_edit_changelog, 0, 0) + self.layout_info.addWidget(text_edit_changelog, 0, 0) class Wl_Dialog_About(wl_dialogs.Wl_Dialog_Info): def __init__(self, main): - super().__init__(main, title = _tr('Wl_Dialog_About', 'About Wordless')) + super().__init__( + main, + title = _tr('Wl_Dialog_About', 'About Wordless'), + icon = False + ) img_wl_icon = QPixmap(wl_paths.get_path_img('wl_icon_about.png')) label_about_icon = QLabel('', self) label_about_icon.setPixmap(img_wl_icon) - label_about_title = wl_labels.Wl_Label_Dialog_No_Wrap(self.tr(''' -
-

         Wordless

-
             Version {}
-
- ''').format(main.ver), self) - - label_about_info = wl_labels.Wl_Label_Dialog_No_Wrap(self.tr(''' -
- An Integrated Corpus Tool with Multilingual Support
- for the Study of Language, Literature, and Translation -
- -
- -
- Copyright (C) 2018-2023  Ye Lei (叢磊)
- Licensed Under GNU GPLv3
- All Other Rights Reserved -
- '''), self) - - self.wrapper_info.layout().addWidget(label_about_icon, 0, 0, Qt.AlignHCenter) - self.wrapper_info.layout().addWidget(label_about_title, 0, 0, 1, 2) - self.wrapper_info.layout().addWidget(label_about_info, 1, 0, 1, 2) - - self.wrapper_info.layout().setColumnStretch(0, 9) - self.wrapper_info.layout().setColumnStretch(1, 5) + label_about_title = wl_labels.Wl_Label_Dialog_No_Wrap( + self.tr(''' +
+

         Wordless

+
             Version {}
+
+ ''').format(main.ver), + self + ) + + label_about_info = wl_labels.Wl_Label_Dialog_No_Wrap( + self.tr(''' +
+ An Integrated Corpus Tool with Multilingual Support
+ for the Study of Language, Literature, and Translation +
+
+
+ Copyright (C) 2018-2023  Ye Lei (叢磊)
+ Licensed Under GNU GPLv3
+ All Other Rights Reserved +
+ '''), + self + ) + + self.layout_info.addWidget(label_about_icon, 0, 0, Qt.AlignHCenter) + self.layout_info.addWidget(label_about_title, 0, 0, 1, 2) + self.layout_info.addWidget(label_about_info, 1, 0, 1, 2) + + self.layout_info.setColumnStretch(0, 9) + self.layout_info.setColumnStretch(1, 5) if __name__ == '__main__': # Environment variables for QT should be set before QApplication is created diff --git a/wordless/wl_measures/wl_measures_readability.py b/wordless/wl_measures/wl_measures_readability.py index b546f565c..1fce24838 100644 --- a/wordless/wl_measures/wl_measures_readability.py +++ b/wordless/wl_measures/wl_measures_readability.py @@ -26,7 +26,13 @@ from PyQt5.QtCore import QCoreApplication from wordless.wl_checks import wl_checks_tokens -from wordless.wl_nlp import wl_lemmatization, wl_pos_tagging, wl_sentence_tokenization, wl_syl_tokenization, wl_texts +from wordless.wl_nlp import ( + wl_lemmatization, + wl_pos_tagging, + wl_sentence_tokenization, + wl_syl_tokenization, + wl_texts +) from wordless.wl_utils import wl_misc, wl_paths _tr = QCoreApplication.translate diff --git a/wordless/wl_ngram_generator.py b/wordless/wl_ngram_generator.py index 87ed70392..d81f12738 100644 --- a/wordless/wl_ngram_generator.py +++ b/wordless/wl_ngram_generator.py @@ -30,9 +30,19 @@ from wordless.wl_dialogs import wl_dialogs_misc from wordless.wl_figs import wl_figs, wl_figs_freqs, wl_figs_stats from wordless.wl_measures import wl_measure_utils -from wordless.wl_nlp import wl_matching, wl_nlp_utils, wl_texts, wl_token_processing +from wordless.wl_nlp import ( + wl_matching, + wl_nlp_utils, + wl_texts, + wl_token_processing +) from wordless.wl_utils import wl_misc, wl_sorting, wl_threading -from wordless.wl_widgets import wl_boxes, wl_layouts, wl_tables, wl_widgets +from wordless.wl_widgets import ( + wl_boxes, + wl_layouts, + wl_tables, + wl_widgets +) _tr = QCoreApplication.translate diff --git a/wordless/wl_nlp/wl_lemmatization.py b/wordless/wl_nlp/wl_lemmatization.py index 002b8a903..c921ffb48 100644 --- a/wordless/wl_nlp/wl_lemmatization.py +++ b/wordless/wl_nlp/wl_lemmatization.py @@ -21,7 +21,12 @@ import simplemma import spacy -from wordless.wl_nlp import wl_nlp_utils, wl_pos_tagging, wl_texts, wl_word_tokenization +from wordless.wl_nlp import ( + wl_nlp_utils, + wl_pos_tagging, + wl_texts, + wl_word_tokenization +) from wordless.wl_utils import wl_conversion _tr = QCoreApplication.translate diff --git a/wordless/wl_nlp/wl_nlp_utils.py b/wordless/wl_nlp/wl_nlp_utils.py index 1d8eec62a..e1f16a700 100644 --- a/wordless/wl_nlp/wl_nlp_utils.py +++ b/wordless/wl_nlp/wl_nlp_utils.py @@ -45,7 +45,12 @@ from wordless.wl_checks import wl_checks_work_area from wordless.wl_dialogs import wl_dialogs_misc from wordless.wl_nlp import wl_sentence_tokenization -from wordless.wl_utils import wl_conversion, wl_misc, wl_paths, wl_threading +from wordless.wl_utils import ( + wl_conversion, + wl_misc, + wl_paths, + wl_threading +) LANGS_WITHOUT_SPACES = ['mya', 'zho_cn', 'zho_tw', 'khm', 'lao', 'jpn', 'tha', 'bod'] diff --git a/wordless/wl_nlp/wl_token_processing.py b/wordless/wl_nlp/wl_token_processing.py index b0adc0ccd..41fe6f4b1 100644 --- a/wordless/wl_nlp/wl_token_processing.py +++ b/wordless/wl_nlp/wl_token_processing.py @@ -20,8 +20,13 @@ from wordless.wl_checks import wl_checks_tokens from wordless.wl_nlp import ( - wl_dependency_parsing, wl_lemmatization, wl_pos_tagging, wl_stop_word_lists, wl_syl_tokenization, - wl_texts, wl_word_detokenization + wl_dependency_parsing, + wl_lemmatization, + wl_pos_tagging, + wl_stop_word_lists, + wl_syl_tokenization, + wl_texts, + wl_word_detokenization ) from wordless.wl_utils import wl_misc diff --git a/wordless/wl_profiler.py b/wordless/wl_profiler.py index 1b72ce903..e2f631d78 100644 --- a/wordless/wl_profiler.py +++ b/wordless/wl_profiler.py @@ -25,10 +25,15 @@ import numpy import scipy from PyQt5.QtCore import pyqtSignal, QCoreApplication, Qt -from PyQt5.QtWidgets import QDialog, QGroupBox, QPushButton, QStackedWidget, QTabWidget +from PyQt5.QtWidgets import ( + QGroupBox, + QPushButton, + QStackedWidget, + QTabWidget +) from wordless.wl_checks import wl_checks_tokens, wl_checks_work_area -from wordless.wl_dialogs import wl_dialogs_misc +from wordless.wl_dialogs import wl_dialogs_misc, wl_msg_boxes from wordless.wl_measures import wl_measures_lexical_density_diversity, wl_measures_misc, wl_measures_readability from wordless.wl_nlp import wl_texts, wl_token_processing from wordless.wl_utils import wl_misc, wl_threading @@ -301,10 +306,15 @@ def clr_all_tables(self): # Ask for confirmation if results have not been exported if needs_confirm: - dialog_clr_table = wl_dialogs_misc.Wl_Dialog_Clr_All_Tables(self.main) - result = dialog_clr_table.exec_() - - confirmed = bool(result == QDialog.Accepted) + confirmed = wl_msg_boxes.wl_msg_box_question( + self.main, + title = self.tr('Clear All Tables'), + text = self.tr(''' +
+ The results in some of the tables have yet been exported. Do you really want to clear all tables? +
+ ''') + ) if confirmed: for table in self.tables: diff --git a/wordless/wl_results/wl_results_search.py b/wordless/wl_results/wl_results_search.py index a0f26e594..4bd09f931 100644 --- a/wordless/wl_results/wl_results_search.py +++ b/wordless/wl_results/wl_results_search.py @@ -74,7 +74,7 @@ def __init__(self, main, table): self.button_restore_defaults = wl_buttons.Wl_Button_Restore_Defaults(self, load_settings = self.load_settings) self.button_close = QPushButton(self.tr('Close'), self) - self.checkbox_multi_search_mode.stateChanged.connect(self.search_settings_changed) + self.checkbox_multi_search_mode.stateChanged.connect(self.multi_search_mode_changed) self.line_edit_search_term.textChanged.connect(self.search_settings_changed) self.line_edit_search_term.returnPressed.connect(self.button_find_next.click) self.list_search_terms.model().dataChanged.connect(self.search_settings_changed) @@ -126,6 +126,8 @@ def __init__(self, main, table): self.layout().addWidget(wl_layouts.Wl_Separator(self), 9, 0, 1, 4) self.layout().addLayout(layout_buttons_bottom, 10, 0, 1, 4) + self.layout().setColumnStretch(0, 1) + for table_to_search in self.tables: table_to_search.model().itemChanged.connect(self.table_item_changed) @@ -165,9 +167,6 @@ def search_settings_changed(self): self.settings['match_without_tags'] = self.checkbox_match_without_tags.isChecked() self.settings['match_tags'] = self.checkbox_match_tags.isChecked() - # Multi-search mode - self.adjustSize() - if wl_checks_work_area.check_search_terms( self.main, search_settings = self.settings, @@ -181,6 +180,11 @@ def search_settings_changed(self): self.button_find_prev.setEnabled(False) self.button_find_all.setEnabled(False) + def multi_search_mode_changed(self): + self.adjust_size() + + self.search_settings_changed() + def table_item_changed(self): self.checkbox_match_tags.token_settings_changed( token_settings = self.tables[0].settings[self.tab]['token_settings'] diff --git a/wordless/wl_results/wl_results_sort.py b/wordless/wl_results/wl_results_sort.py index b5f2d7dba..128b1b847 100644 --- a/wordless/wl_results/wl_results_sort.py +++ b/wordless/wl_results/wl_results_sort.py @@ -21,17 +21,33 @@ from PyQt5.QtCore import pyqtSignal, QCoreApplication from PyQt5.QtGui import QStandardItem -from PyQt5.QtWidgets import QAbstractItemDelegate, QComboBox, QMessageBox, QPushButton +from PyQt5.QtWidgets import ( + QAbstractItemDelegate, + QComboBox, + QMessageBox, + QPushButton +) from wordless.wl_dialogs import wl_dialogs, wl_dialogs_misc from wordless.wl_utils import wl_misc, wl_threading -from wordless.wl_widgets import wl_buttons, wl_item_delegates, wl_labels, wl_layouts, wl_tables +from wordless.wl_widgets import ( + wl_buttons, + wl_item_delegates, + wl_labels, + wl_layouts, + wl_tables +) _tr = QCoreApplication.translate class Wl_Dialog_Results_Sort_Concordancer(wl_dialogs.Wl_Dialog): def __init__(self, main, table): - super().__init__(main, _tr('Wl_Dialog_Results_Sort_Concordancer', 'Sort Results')) + super().__init__( + main, + title = _tr('Wl_Dialog_Results_Sort_Concordancer', 'Sort Results'), + width = 550, + height = 300 + ) self.tab = table.tab self.table = table @@ -39,7 +55,7 @@ def __init__(self, main, table): self.main.wl_work_area.currentChanged.connect(self.reject) - self.table_sort = Wl_Table_Results_Sort_Conordancer(self, table = self.table) + self.table_sort = Table_Results_Sort_Conordancer(self, table = self.table) self.button_restore_defaults = wl_buttons.Wl_Button_Restore_Defaults(self, load_settings = self.load_settings) self.button_sort = QPushButton(self.tr('Sort'), self) @@ -49,13 +65,14 @@ def __init__(self, main, table): self.button_close.clicked.connect(self.reject) layout_table_sort = wl_layouts.Wl_Layout() - layout_table_sort.addWidget(self.table_sort, 0, 0, 5, 1) + layout_table_sort.addWidget(self.table_sort, 0, 0, 4, 1) layout_table_sort.addWidget(self.table_sort.button_add, 0, 1) layout_table_sort.addWidget(self.table_sort.button_ins, 1, 1) layout_table_sort.addWidget(self.table_sort.button_del, 2, 1) layout_table_sort.addWidget(self.table_sort.button_clr, 3, 1) - layout_table_sort.setRowStretch(4, 1) + layout_table_sort.setRowStretch(0, 1) + layout_table_sort.setColumnStretch(0, 1) layout_buttons = wl_layouts.Wl_Layout() layout_buttons.addWidget(self.button_restore_defaults, 0, 0) @@ -68,6 +85,8 @@ def __init__(self, main, table): self.layout().addLayout(layout_table_sort, 0, 0) self.layout().addLayout(layout_buttons, 1, 0) + self.layout().setRowStretch(0, 1) + self.load_settings() # To be called by Restore defaults @@ -224,13 +243,13 @@ def update_gui(self, results): self.table.enable_updates(emit_signals = False) -class Wl_Table_Results_Sort_Conordancer(wl_tables.Wl_Table_Add_Ins_Del_Clr): +class Table_Results_Sort_Conordancer(wl_tables.Wl_Table_Add_Ins_Del_Clr): def __init__(self, parent, table): super().__init__( parent = parent, headers = [ - _tr('Wl_Table_Results_Sort_Conordancer', 'Column'), - _tr('Wl_Table_Results_Sort_Conordancer', 'Order') + _tr('Table_Results_Sort_Conordancer', 'Column'), + _tr('Table_Results_Sort_Conordancer', 'Order') ], col_edit = 0 ) @@ -247,8 +266,6 @@ def __init__(self, parent, table): self.cols_to_sort = self.cols_to_sort_default.copy() - self.setFixedSize(400, 200) - self.setItemDelegateForColumn(1, wl_item_delegates.Wl_Item_Delegate_Combo_Box( parent = self, items = [ diff --git a/wordless/wl_settings/wl_settings.py b/wordless/wl_settings/wl_settings.py index 001a349c8..9ea671b4e 100644 --- a/wordless/wl_settings/wl_settings.py +++ b/wordless/wl_settings/wl_settings.py @@ -22,8 +22,13 @@ from PyQt5.QtCore import pyqtSignal, QCoreApplication, Qt from PyQt5.QtGui import QStandardItem, QStandardItemModel from PyQt5.QtWidgets import ( - QAbstractItemView, QHeaderView, QMessageBox, QPushButton, - QStackedWidget, QTreeView, QWidget + QAbstractItemView, + QHeaderView, + QMessageBox, + QPushButton, + QStackedWidget, + QTreeView, + QWidget ) from wordless.wl_checks import wl_checks_misc, wl_checks_work_area @@ -40,8 +45,7 @@ def __init__(self, main): main, title = _tr('Wl_Settings', 'Settings'), width = 1024, - height = 768, - resizable = True + height = 768 ) # Avoid circular imports diff --git a/wordless/wl_settings/wl_settings_default.py b/wordless/wl_settings/wl_settings_default.py index fbe9bdd3b..340102729 100644 --- a/wordless/wl_settings/wl_settings_default.py +++ b/wordless/wl_settings/wl_settings_default.py @@ -1203,7 +1203,7 @@ def init_settings_default(main): }, 'misc_settings': { - 'confirm_on_exit': True + 'always_confirm_on_exit': True }, # Settings - General - Import @@ -1264,6 +1264,7 @@ def init_settings_default(main): }, 'misc_settings': { + 'display_warning_when_opening_nontext_files': True, 'read_files_in_chunks': 10 }, diff --git a/wordless/wl_settings/wl_settings_dependency_parsing.py b/wordless/wl_settings/wl_settings_dependency_parsing.py index f0e296596..409b62c38 100644 --- a/wordless/wl_settings/wl_settings_dependency_parsing.py +++ b/wordless/wl_settings/wl_settings_dependency_parsing.py @@ -26,7 +26,13 @@ from wordless.wl_nlp import wl_dependency_parsing, wl_nlp_utils from wordless.wl_settings import wl_settings from wordless.wl_utils import wl_conversion, wl_threading -from wordless.wl_widgets import wl_boxes, wl_item_delegates, wl_layouts, wl_tables, wl_widgets +from wordless.wl_widgets import ( + wl_boxes, + wl_item_delegates, + wl_layouts, + wl_tables, + wl_widgets +) class Wl_Settings_Dependency_Parsing(wl_settings.Wl_Settings_Node): def __init__(self, main): diff --git a/wordless/wl_settings/wl_settings_figs.py b/wordless/wl_settings/wl_settings_figs.py index 36e6ff82a..9be848fe9 100644 --- a/wordless/wl_settings/wl_settings_figs.py +++ b/wordless/wl_settings/wl_settings_figs.py @@ -25,7 +25,13 @@ import networkx from PyQt5.QtCore import QCoreApplication from PyQt5.QtGui import QFont, QPixmap -from PyQt5.QtWidgets import QCheckBox, QDesktopWidget, QGroupBox, QLabel, QLineEdit +from PyQt5.QtWidgets import ( + QCheckBox, + QDesktopWidget, + QGroupBox, + QLabel, + QLineEdit +) from wordless.wl_settings import wl_settings from wordless.wl_utils import wl_paths diff --git a/wordless/wl_settings/wl_settings_files.py b/wordless/wl_settings/wl_settings_files.py index 854236034..1b8b724f5 100644 --- a/wordless/wl_settings/wl_settings_files.py +++ b/wordless/wl_settings/wl_settings_files.py @@ -21,14 +21,27 @@ from PyQt5.QtCore import QCoreApplication from PyQt5.QtGui import QStandardItem -from PyQt5.QtWidgets import QAbstractItemDelegate, QGroupBox, QLabel, QLineEdit, QPushButton +from PyQt5.QtWidgets import ( + QAbstractItemDelegate, + QCheckBox, + QGroupBox, + QLabel, + QLineEdit, + QPushButton +) from wordless.wl_checks import wl_checks_misc from wordless.wl_dialogs import wl_msg_boxes from wordless.wl_nlp import wl_matching from wordless.wl_settings import wl_settings from wordless.wl_utils import wl_conversion -from wordless.wl_widgets import wl_boxes, wl_item_delegates, wl_labels, wl_layouts, wl_tables +from wordless.wl_widgets import ( + wl_boxes, + wl_item_delegates, + wl_labels, + wl_layouts, + wl_tables +) _tr = QCoreApplication.translate @@ -85,6 +98,7 @@ def __init__(self, main): # Miscellaneous Settings self.group_box_misc_settings = QGroupBox(self.tr('Miscellaneous Settings'), self) + self.checkbox_display_warning_when_opening_nontext_files = QCheckBox(self.tr('Display warning when opening non-text files'), self) self.label_read_files_in_chunks = QLabel(self.tr('Read files in chunks of'), self) self.spin_box_read_files_in_chunks = wl_boxes.Wl_Spin_Box(self) self.label_read_files_in_chunks_lines = QLabel(self.tr('lines'), self) @@ -92,9 +106,10 @@ def __init__(self, main): self.spin_box_read_files_in_chunks.setRange(1, 10000) self.group_box_misc_settings.setLayout(wl_layouts.Wl_Layout()) - self.group_box_misc_settings.layout().addWidget(self.label_read_files_in_chunks, 0, 0) - self.group_box_misc_settings.layout().addWidget(self.spin_box_read_files_in_chunks, 0, 1) - self.group_box_misc_settings.layout().addWidget(self.label_read_files_in_chunks_lines, 0, 2) + self.group_box_misc_settings.layout().addWidget(self.checkbox_display_warning_when_opening_nontext_files, 0, 0, 1, 3) + self.group_box_misc_settings.layout().addWidget(self.label_read_files_in_chunks, 1, 0) + self.group_box_misc_settings.layout().addWidget(self.spin_box_read_files_in_chunks, 1, 1) + self.group_box_misc_settings.layout().addWidget(self.label_read_files_in_chunks_lines, 1, 2) self.group_box_misc_settings.layout().setColumnStretch(3, 1) @@ -123,6 +138,7 @@ def load_settings(self, defaults = False): self.checkbox_num_lines_no_limit.setChecked(settings['auto_detection_settings']['num_lines_no_limit']) # Miscellaneous Settings + self.checkbox_display_warning_when_opening_nontext_files.setChecked(settings['misc_settings']['display_warning_when_opening_nontext_files']) self.spin_box_read_files_in_chunks.setValue(settings['misc_settings']['read_files_in_chunks']) def apply_settings(self): @@ -137,6 +153,7 @@ def apply_settings(self): self.settings_custom['auto_detection_settings']['num_lines_no_limit'] = self.checkbox_num_lines_no_limit.isChecked() # Miscellaneous Settings + self.settings_custom['misc_settings']['display_warning_when_opening_nontext_files'] = self.checkbox_display_warning_when_opening_nontext_files.isChecked() self.settings_custom['misc_settings']['read_files_in_chunks'] = self.spin_box_read_files_in_chunks.value() return True diff --git a/wordless/wl_settings/wl_settings_general.py b/wordless/wl_settings/wl_settings_general.py index ca9adf6ca..11ded445a 100644 --- a/wordless/wl_settings/wl_settings_general.py +++ b/wordless/wl_settings/wl_settings_general.py @@ -21,8 +21,13 @@ from PyQt5.QtGui import QFont from PyQt5.QtWidgets import ( - QCheckBox, QDialog, QFileDialog, QGroupBox, QLabel, - QLineEdit, QPushButton + QCheckBox, + QDialog, + QFileDialog, + QGroupBox, + QLabel, + QLineEdit, + QPushButton ) from wordless.wl_dialogs import wl_dialogs_misc @@ -104,10 +109,10 @@ def __init__(self, main): # Miscellaneous Settings self.group_box_misc_settings = QGroupBox(self.tr('Miscellaneous Settings'), self) - self.checkbox_confirm_on_exit = QCheckBox(self.tr('Always confirm on exit'), self) + self.checkbox_always_confirm_on_exit = QCheckBox(self.tr('Always confirm on exit'), self) self.group_box_misc_settings.setLayout(wl_layouts.Wl_Layout()) - self.group_box_misc_settings.layout().addWidget(self.checkbox_confirm_on_exit, 0, 0) + self.group_box_misc_settings.layout().addWidget(self.checkbox_always_confirm_on_exit, 0, 0) self.setLayout(wl_layouts.Wl_Layout()) self.layout().addWidget(self.group_box_ui_settings, 0, 0) @@ -152,7 +157,7 @@ def load_settings(self, defaults = False): self.checkbox_check_updates_on_startup.setChecked(settings['update_settings']['check_updates_on_startup']) # Miscellaneous Settings - self.checkbox_confirm_on_exit.setChecked(settings['misc_settings']['confirm_on_exit']) + self.checkbox_always_confirm_on_exit.setChecked(settings['misc_settings']['always_confirm_on_exit']) self.proxy_settings_changed() @@ -195,7 +200,7 @@ def apply_settings(self): self.settings_custom['update_settings']['check_updates_on_startup'] = self.checkbox_check_updates_on_startup.isChecked() # Miscellaneous Settings - self.settings_custom['misc_settings']['confirm_on_exit'] = self.checkbox_confirm_on_exit.isChecked() + self.settings_custom['misc_settings']['always_confirm_on_exit'] = self.checkbox_always_confirm_on_exit.isChecked() if result == 'restart': self.main.restart() diff --git a/wordless/wl_settings/wl_settings_global.py b/wordless/wl_settings/wl_settings_global.py index 591efb943..3bccd1dea 100644 --- a/wordless/wl_settings/wl_settings_global.py +++ b/wordless/wl_settings/wl_settings_global.py @@ -3892,37 +3892,12 @@ 'styles': { 'style_dialog': ''' - - - + ''' } } diff --git a/wordless/wl_settings/wl_settings_lemmatization.py b/wordless/wl_settings/wl_settings_lemmatization.py index d53d76b3e..1d73b62db 100644 --- a/wordless/wl_settings/wl_settings_lemmatization.py +++ b/wordless/wl_settings/wl_settings_lemmatization.py @@ -20,12 +20,27 @@ from PyQt5.QtCore import pyqtSignal from PyQt5.QtGui import QStandardItem -from PyQt5.QtWidgets import QGroupBox, QLabel, QPushButton, QTextEdit - -from wordless.wl_nlp import wl_lemmatization, wl_nlp_utils, wl_texts, wl_word_detokenization +from PyQt5.QtWidgets import ( + QGroupBox, + QLabel, + QPushButton, + QTextEdit +) + +from wordless.wl_nlp import ( + wl_lemmatization, + wl_nlp_utils, + wl_texts, + wl_word_detokenization +) from wordless.wl_settings import wl_settings from wordless.wl_utils import wl_conversion, wl_threading -from wordless.wl_widgets import wl_boxes, wl_item_delegates, wl_layouts, wl_tables +from wordless.wl_widgets import ( + wl_boxes, + wl_item_delegates, + wl_layouts, + wl_tables +) class Wl_Settings_Lemmatization(wl_settings.Wl_Settings_Node): def __init__(self, main): diff --git a/wordless/wl_settings/wl_settings_pos_tagging.py b/wordless/wl_settings/wl_settings_pos_tagging.py index c2930f833..03f8a00ff 100644 --- a/wordless/wl_settings/wl_settings_pos_tagging.py +++ b/wordless/wl_settings/wl_settings_pos_tagging.py @@ -21,15 +21,26 @@ from PyQt5.QtCore import pyqtSignal, Qt from PyQt5.QtGui import QStandardItem from PyQt5.QtWidgets import ( - QCheckBox, QGroupBox, QLabel, QPlainTextEdit, QPushButton, - QStackedWidget, QTextEdit + QCheckBox, + QGroupBox, + QLabel, + QPlainTextEdit, + QPushButton, + QStackedWidget, + QTextEdit ) from wordless.wl_dialogs import wl_dialogs_misc, wl_msg_boxes from wordless.wl_nlp import wl_nlp_utils, wl_pos_tagging from wordless.wl_settings import wl_settings from wordless.wl_utils import wl_conversion, wl_threading -from wordless.wl_widgets import wl_boxes, wl_item_delegates, wl_labels, wl_layouts, wl_tables +from wordless.wl_widgets import ( + wl_boxes, + wl_item_delegates, + wl_labels, + wl_layouts, + wl_tables +) # Part-of-speech Tagging class Wl_Settings_Pos_Tagging(wl_settings.Wl_Settings_Node): diff --git a/wordless/wl_settings/wl_settings_sentence_tokenization.py b/wordless/wl_settings/wl_settings_sentence_tokenization.py index 66b4726e4..ef7acace4 100644 --- a/wordless/wl_settings/wl_settings_sentence_tokenization.py +++ b/wordless/wl_settings/wl_settings_sentence_tokenization.py @@ -20,12 +20,22 @@ from PyQt5.QtCore import pyqtSignal from PyQt5.QtGui import QStandardItem -from PyQt5.QtWidgets import QGroupBox, QLabel, QPushButton, QTextEdit +from PyQt5.QtWidgets import ( + QGroupBox, + QLabel, + QPushButton, + QTextEdit +) from wordless.wl_nlp import wl_nlp_utils, wl_sentence_tokenization from wordless.wl_settings import wl_settings from wordless.wl_utils import wl_conversion, wl_threading -from wordless.wl_widgets import wl_boxes, wl_item_delegates, wl_layouts, wl_tables +from wordless.wl_widgets import ( + wl_boxes, + wl_item_delegates, + wl_layouts, + wl_tables +) class Wl_Settings_Sentence_Tokenization(wl_settings.Wl_Settings_Node): def __init__(self, main): diff --git a/wordless/wl_settings/wl_settings_sentiment_analysis.py b/wordless/wl_settings/wl_settings_sentiment_analysis.py index 2cb6dff38..6117f0a02 100644 --- a/wordless/wl_settings/wl_settings_sentiment_analysis.py +++ b/wordless/wl_settings/wl_settings_sentiment_analysis.py @@ -20,12 +20,22 @@ from PyQt5.QtCore import pyqtSignal from PyQt5.QtGui import QStandardItem -from PyQt5.QtWidgets import QGroupBox, QLabel, QPushButton, QTextEdit +from PyQt5.QtWidgets import ( + QGroupBox, + QLabel, + QPushButton, + QTextEdit +) from wordless.wl_nlp import wl_nlp_utils, wl_sentiment_analysis from wordless.wl_settings import wl_settings from wordless.wl_utils import wl_conversion, wl_threading -from wordless.wl_widgets import wl_boxes, wl_item_delegates, wl_layouts, wl_tables +from wordless.wl_widgets import ( + wl_boxes, + wl_item_delegates, + wl_layouts, + wl_tables +) class Wl_Settings_Sentiment_Analysis(wl_settings.Wl_Settings_Node): def __init__(self, main): diff --git a/wordless/wl_settings/wl_settings_stop_word_lists.py b/wordless/wl_settings/wl_settings_stop_word_lists.py index faa36db17..af1fc315c 100644 --- a/wordless/wl_settings/wl_settings_stop_word_lists.py +++ b/wordless/wl_settings/wl_settings_stop_word_lists.py @@ -24,7 +24,13 @@ from wordless.wl_nlp import wl_nlp_utils, wl_stop_word_lists from wordless.wl_settings import wl_settings from wordless.wl_utils import wl_conversion -from wordless.wl_widgets import wl_boxes, wl_item_delegates, wl_layouts, wl_lists, wl_tables +from wordless.wl_widgets import ( + wl_boxes, + wl_item_delegates, + wl_layouts, + wl_lists, + wl_tables +) class Wl_Settings_Stop_Word_Lists(wl_settings.Wl_Settings_Node): def __init__(self, main): diff --git a/wordless/wl_settings/wl_settings_syl_tokenization.py b/wordless/wl_settings/wl_settings_syl_tokenization.py index f1d781318..123f12c9e 100644 --- a/wordless/wl_settings/wl_settings_syl_tokenization.py +++ b/wordless/wl_settings/wl_settings_syl_tokenization.py @@ -20,12 +20,27 @@ from PyQt5.QtCore import pyqtSignal from PyQt5.QtGui import QStandardItem -from PyQt5.QtWidgets import QGroupBox, QLabel, QPushButton, QTextEdit - -from wordless.wl_nlp import wl_nlp_utils, wl_syl_tokenization, wl_texts, wl_word_detokenization +from PyQt5.QtWidgets import ( + QGroupBox, + QLabel, + QPushButton, + QTextEdit +) + +from wordless.wl_nlp import ( + wl_nlp_utils, + wl_syl_tokenization, + wl_texts, + wl_word_detokenization +) from wordless.wl_settings import wl_settings from wordless.wl_utils import wl_conversion, wl_threading -from wordless.wl_widgets import wl_boxes, wl_item_delegates, wl_layouts, wl_tables +from wordless.wl_widgets import ( + wl_boxes, + wl_item_delegates, + wl_layouts, + wl_tables +) class Wl_Settings_Syl_Tokenization(wl_settings.Wl_Settings_Node): def __init__(self, main): diff --git a/wordless/wl_settings/wl_settings_word_tokenization.py b/wordless/wl_settings/wl_settings_word_tokenization.py index 3d8555272..06529e7b1 100644 --- a/wordless/wl_settings/wl_settings_word_tokenization.py +++ b/wordless/wl_settings/wl_settings_word_tokenization.py @@ -21,12 +21,22 @@ from PyQt5.QtCore import pyqtSignal from PyQt5.QtGui import QStandardItem -from PyQt5.QtWidgets import QGroupBox, QLabel, QPushButton, QTextEdit +from PyQt5.QtWidgets import ( + QGroupBox, + QLabel, + QPushButton, + QTextEdit +) from wordless.wl_nlp import wl_nlp_utils, wl_word_tokenization from wordless.wl_settings import wl_settings from wordless.wl_utils import wl_conversion, wl_misc, wl_threading -from wordless.wl_widgets import wl_boxes, wl_item_delegates, wl_layouts, wl_tables +from wordless.wl_widgets import ( + wl_boxes, + wl_item_delegates, + wl_layouts, + wl_tables +) class Wl_Settings_Word_Tokenization(wl_settings.Wl_Settings_Node): def __init__(self, main): diff --git a/wordless/wl_utils/wl_threading.py b/wordless/wl_utils/wl_threading.py index 3cb5698b1..ad834a9e5 100644 --- a/wordless/wl_utils/wl_threading.py +++ b/wordless/wl_utils/wl_threading.py @@ -18,7 +18,12 @@ import time -from PyQt5.QtCore import pyqtSignal, QObject, Qt, QThread +from PyQt5.QtCore import ( + pyqtSignal, + QObject, + Qt, + QThread +) # Workers class Wl_Worker(QObject): diff --git a/wordless/wl_widgets/wl_boxes.py b/wordless/wl_widgets/wl_boxes.py index 7159d025d..cbf3407cf 100644 --- a/wordless/wl_widgets/wl_boxes.py +++ b/wordless/wl_widgets/wl_boxes.py @@ -18,7 +18,11 @@ from PyQt5.QtCore import QCoreApplication, Qt from PyQt5.QtWidgets import ( - QCheckBox, QComboBox, QDoubleSpinBox, QFontComboBox, QLabel, + QCheckBox, + QComboBox, + QDoubleSpinBox, + QFontComboBox, + QLabel, QSpinBox ) diff --git a/wordless/wl_widgets/wl_buttons.py b/wordless/wl_widgets/wl_buttons.py index 6d3b49f14..b8624270b 100644 --- a/wordless/wl_widgets/wl_buttons.py +++ b/wordless/wl_widgets/wl_buttons.py @@ -20,7 +20,12 @@ from PyQt5.QtCore import QCoreApplication from PyQt5.QtGui import QBrush, QColor, QPainter -from PyQt5.QtWidgets import QCheckBox, QColorDialog, QFileDialog, QPushButton +from PyQt5.QtWidgets import ( + QCheckBox, + QColorDialog, + QFileDialog, + QPushButton +) from wordless.wl_checks import wl_checks_misc from wordless.wl_dialogs import wl_msg_boxes diff --git a/wordless/wl_widgets/wl_labels.py b/wordless/wl_widgets/wl_labels.py index d8ced0ac8..d24590b45 100644 --- a/wordless/wl_widgets/wl_labels.py +++ b/wordless/wl_widgets/wl_labels.py @@ -58,27 +58,23 @@ def __init__(self, html, parent): self.setAlignment(Qt.AlignCenter) class Wl_Label_Dialog(Wl_Label_Html): - def __init__(self, text, parent): + def __init__(self, text, parent, word_wrap = True): main = wl_misc.find_wl_main(parent) super().__init__( f''' {main.settings_global['styles']['style_dialog']} - - {text} - + {text} ''', parent ) - self.setWordWrap(True) + self.setWordWrap(word_wrap) def set_text(self, text): super().setText(f''' {self.main.settings_global['styles']['style_dialog']} - - {text} - + {text} ''') class Wl_Label_Dialog_No_Wrap(Wl_Label_Dialog): diff --git a/wordless/wl_widgets/wl_layouts.py b/wordless/wl_widgets/wl_layouts.py index 7c8fec785..70cbfa475 100644 --- a/wordless/wl_widgets/wl_layouts.py +++ b/wordless/wl_widgets/wl_layouts.py @@ -20,8 +20,15 @@ from PyQt5.QtGui import QPainter, QPalette from PyQt5.QtWidgets import ( - QFrame, QGridLayout, QScrollArea, QSizePolicy, QSplitter, - QStackedWidget, QStyle, QStyleOption, QWidget + QFrame, + QGridLayout, + QScrollArea, + QSizePolicy, + QSplitter, + QStackedWidget, + QStyle, + QStyleOption, + QWidget ) from wordless.wl_utils import wl_misc diff --git a/wordless/wl_widgets/wl_lists.py b/wordless/wl_widgets/wl_lists.py index 9d30187fc..c0365f744 100644 --- a/wordless/wl_widgets/wl_lists.py +++ b/wordless/wl_widgets/wl_lists.py @@ -19,10 +19,19 @@ import os import re -from PyQt5.QtCore import QCoreApplication, QItemSelection, QModelIndex, QStringListModel +from PyQt5.QtCore import ( + QCoreApplication, + QItemSelection, + QModelIndex, + QStringListModel +) from PyQt5.QtGui import QStandardItem from PyQt5.QtWidgets import ( - QAbstractItemDelegate, QAbstractItemView, QFileDialog, QLineEdit, QListView, + QAbstractItemDelegate, + QAbstractItemView, + QFileDialog, + QLineEdit, + QListView, QPushButton ) diff --git a/wordless/wl_widgets/wl_tables.py b/wordless/wl_widgets/wl_tables.py index 61319a24b..2e20e1c95 100644 --- a/wordless/wl_widgets/wl_tables.py +++ b/wordless/wl_widgets/wl_tables.py @@ -25,15 +25,25 @@ import bs4 import docx import openpyxl -from PyQt5.QtCore import pyqtSignal, QCoreApplication, QItemSelection, Qt +from PyQt5.QtCore import ( + pyqtSignal, + QCoreApplication, + QItemSelection, + Qt +) from PyQt5.QtGui import QFont, QStandardItem, QStandardItemModel from PyQt5.QtWidgets import ( - QAbstractItemView, QApplication, QDialog, QFileDialog, QHeaderView, - QLabel, QPushButton, QTableView + QAbstractItemView, + QApplication, + QFileDialog, + QHeaderView, + QLabel, + QPushButton, + QTableView ) from wordless.wl_checks import wl_checks_misc, wl_checks_work_area -from wordless.wl_dialogs import wl_dialogs_misc +from wordless.wl_dialogs import wl_dialogs_misc, wl_msg_boxes from wordless.wl_nlp import wl_nlp_utils from wordless.wl_utils import wl_misc, wl_paths, wl_threading from wordless.wl_widgets import wl_buttons @@ -95,6 +105,7 @@ def __init__( self.default_foreground = '#292929' self.default_background = '#FFF' + stylesheet_items = f''' QTableView::item:hover {{ background-color: #E5E5E5; @@ -1736,11 +1747,15 @@ def clr_table(self, num_headers = 1, confirm = False): # Ask for confirmation if results have not been exported if confirm: if not self.is_empty() and not self.results_saved: - dialog_clr_table = wl_dialogs_misc.Wl_Dialog_Clr_Table(self.main) - result = dialog_clr_table.exec_() - - if result == QDialog.Rejected: - confirmed = False + confirmed = wl_msg_boxes.wl_msg_box_question( + self.main, + title = self.tr('Clear Table'), + text = self.tr(''' +
+ The results in the table have yet been exported. Do you really want to clear the table? +
+ ''') + ) if confirmed: self.model().clear() diff --git a/wordless/wl_widgets/wl_widgets.py b/wordless/wl_widgets/wl_widgets.py index 307a035a1..a78e81112 100644 --- a/wordless/wl_widgets/wl_widgets.py +++ b/wordless/wl_widgets/wl_widgets.py @@ -20,20 +20,29 @@ from PyQt5.QtCore import QCoreApplication, Qt from PyQt5.QtWidgets import ( - QCheckBox, QGroupBox, QLabel, QLineEdit, QPushButton, + QCheckBox, + QGroupBox, + QLabel, + QLineEdit, + QPushButton, QWidget ) from wordless.wl_dialogs import wl_dialogs from wordless.wl_measures import wl_measure_utils from wordless.wl_utils import wl_misc -from wordless.wl_widgets import wl_boxes, wl_labels, wl_layouts, wl_lists +from wordless.wl_widgets import ( + wl_boxes, + wl_labels, + wl_layouts, + wl_lists +) _tr = QCoreApplication.translate class Wl_Dialog_Context_Settings(wl_dialogs.Wl_Dialog_Settings): def __init__(self, main, tab): - super().__init__(main, title = _tr('wl_widgets', 'Context Settings')) + super().__init__(main, title = _tr('Wl_Dialog_Context_Settings', 'Context Settings')) self.tab = tab self.settings_custom = self.main.settings_custom[self.tab]['search_settings']['context_settings'] @@ -511,7 +520,7 @@ def match_tags_changed(): wrapper_search_terms = QWidget(parent) wrapper_search_terms.setLayout(wl_layouts.Wl_Layout()) - wrapper_search_terms.layout().addWidget(list_search_terms, 0, 0, 6, 1) + wrapper_search_terms.layout().addWidget(list_search_terms, 0, 0, 7, 1) wrapper_search_terms.layout().addWidget(list_search_terms.button_add, 0, 1) wrapper_search_terms.layout().addWidget(list_search_terms.button_ins, 1, 1) wrapper_search_terms.layout().addWidget(list_search_terms.button_del, 2, 1) @@ -519,6 +528,7 @@ def match_tags_changed(): wrapper_search_terms.layout().addWidget(list_search_terms.button_imp, 4, 1) wrapper_search_terms.layout().addWidget(list_search_terms.button_exp, 5, 1) + wrapper_search_terms.layout().setRowStretch(6, 1) wrapper_search_terms.layout().setContentsMargins(0, 0, 0, 0) stacked_widget_search_term = wl_layouts.Wl_Stacked_Widget_Resizable(parent) diff --git a/wordless/wl_wordlist_generator.py b/wordless/wl_wordlist_generator.py index 32b9f483b..e2b9ff53f 100644 --- a/wordless/wl_wordlist_generator.py +++ b/wordless/wl_wordlist_generator.py @@ -31,8 +31,18 @@ from wordless.wl_figs import wl_figs, wl_figs_freqs, wl_figs_stats from wordless.wl_measures import wl_measure_utils from wordless.wl_nlp import wl_texts, wl_token_processing -from wordless.wl_utils import wl_conversion, wl_misc, wl_sorting, wl_threading -from wordless.wl_widgets import wl_boxes, wl_layouts, wl_tables, wl_widgets +from wordless.wl_utils import ( + wl_conversion, + wl_misc, + wl_sorting, + wl_threading +) +from wordless.wl_widgets import ( + wl_boxes, + wl_layouts, + wl_tables, + wl_widgets +) _tr = QCoreApplication.translate