From 566d3fc321d14819594f22218384e82486992888 Mon Sep 17 00:00:00 2001
From: refrigerador67 <96502023+refrigerador67@users.noreply.github.com>
Date: Wed, 18 Dec 2024 22:42:43 -0300
Subject: [PATCH 01/31] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index f05b870..a9d9ca6 100644
--- a/README.md
+++ b/README.md
@@ -41,4 +41,4 @@
### About the name
-The name "Picodulce" comes from a popular Argentine candy. This reflects the enjoyable and user-friendly experience that the launcher aims to provide, making game management straightforward and pleasant.
+The name "Picodulce" comes from a popular Argentinian candy. This reflects the enjoyable and user-friendly experience that the launcher aims to provide, making game management straightforward and pleasant.
From fd4c23ed3bad91a8ed8a31e5d79ad72ca7500c05 Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Fri, 20 Dec 2024 09:43:15 -0300
Subject: [PATCH 02/31] added stylesheet support
---
picodulce.py | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/picodulce.py b/picodulce.py
index 341a7e7..a357a2b 100644
--- a/picodulce.py
+++ b/picodulce.py
@@ -94,6 +94,14 @@ class PicomcVersionSelector(QWidget):
# Apply the palette to the application
app.setPalette(palette)
+ # Apply style sheet if present
+ if "stylesheet" in self.theme:
+ stylesheet = self.theme["stylesheet"]
+ app.setStyleSheet(stylesheet)
+ else:
+ print("No 'stylesheet' section found in the theme file.")
+
+
def themes_integrity(self):
# Define folder and file paths
themes_folder = "themes"
From afac01f3dceab859f49ddd72602bf818732362d6 Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Fri, 20 Dec 2024 09:52:18 -0300
Subject: [PATCH 03/31] Update version.json
---
version.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/version.json b/version.json
index 13bde8c..2496313 100644
--- a/version.json
+++ b/version.json
@@ -1,5 +1,5 @@
{
- "version": "0.11",
+ "version": "0.11.1",
"links": [
"https://raw.githubusercontent.com/nixietab/picodulce/main/version.json",
"https://raw.githubusercontent.com/nixietab/picodulce/main/picodulce.py",
From 17ccedfefc0cc3e822af0088eba22ec7ee63cabe Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Sat, 21 Dec 2024 06:21:28 -0300
Subject: [PATCH 04/31] added native theme compatibility and generation
also added some fallbacks
---
picodulce.py | 34 +++++++++++++++++++++++++++++++---
1 file changed, 31 insertions(+), 3 deletions(-)
diff --git a/picodulce.py b/picodulce.py
index a357a2b..f7aec50 100644
--- a/picodulce.py
+++ b/picodulce.py
@@ -50,8 +50,15 @@ class PicomcVersionSelector(QWidget):
discord_rcp_thread.start()
def load_theme_from_file(self, file_path, app):
+ self.theme = {}
+ # Check if the file exists, else load 'Dark.json'
if not os.path.exists(file_path):
- raise FileNotFoundError(f"Theme file '{file_path}' not found.")
+ print(f"Theme file '{file_path}' not found. Loading default 'Dark.json' instead.")
+ file_path = "themes/Dark.json"
+
+ # Ensure the fallback file exists
+ if not os.path.exists(file_path):
+ raise FileNotFoundError(f"Default theme file '{file_path}' not found.")
# Open and parse the JSON file
with open(file_path, "r") as file:
@@ -81,7 +88,7 @@ class PicomcVersionSelector(QWidget):
"BrightText": QPalette.BrightText,
"Link": QPalette.Link,
"Highlight": QPalette.Highlight,
- "HighlightedText": QPalette.HighlightedText
+ "HighlightedText": QPalette.HighlightedText,
}
# Apply colors from the palette config
@@ -101,11 +108,11 @@ class PicomcVersionSelector(QWidget):
else:
print("No 'stylesheet' section found in the theme file.")
-
def themes_integrity(self):
# Define folder and file paths
themes_folder = "themes"
dark_theme_file = os.path.join(themes_folder, "Dark.json")
+ native_theme_file = os.path.join(themes_folder, "Native.json")
# Define the default content for Dark.json
dark_theme_content = {
@@ -133,6 +140,17 @@ class PicomcVersionSelector(QWidget):
"background_image_base64": ""
}
+ # Define the default content for Native.json
+ native_theme_content = {
+ "manifest": {
+ "name": "Native",
+ "description": "The native looks of your OS",
+ "author": "Your Qt Style",
+ "license": "Any"
+ },
+ "palette": {}
+ }
+
# Step 1: Ensure the themes folder exists
if not os.path.exists(themes_folder):
print(f"Creating folder: {themes_folder}")
@@ -149,6 +167,16 @@ class PicomcVersionSelector(QWidget):
else:
print(f"File already exists: {dark_theme_file}")
+ # Step 3: Ensure Native.json exists
+ if not os.path.isfile(native_theme_file):
+ print(f"Creating file: {native_theme_file}")
+ with open(native_theme_file, "w", encoding="utf-8") as file:
+ json.dump(native_theme_content, file, indent=2)
+ print("Native.json has been created successfully.")
+ else:
+ print(f"File already exists: {native_theme_file}")
+
+
def init_ui(self):
self.setWindowTitle('PicoDulce Launcher') # Change window title
current_date = datetime.now()
From b7c099c0d95f5a47ac9fa67629610a64de0454d3 Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Sat, 21 Dec 2024 06:21:40 -0300
Subject: [PATCH 05/31] Update version.json
---
version.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/version.json b/version.json
index 2496313..6185f05 100644
--- a/version.json
+++ b/version.json
@@ -1,5 +1,5 @@
{
- "version": "0.11.1",
+ "version": "0.11.2",
"links": [
"https://raw.githubusercontent.com/nixietab/picodulce/main/version.json",
"https://raw.githubusercontent.com/nixietab/picodulce/main/picodulce.py",
From 3ee4348dfe9ed19d2045ca950b7761419ed76889 Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Sat, 21 Dec 2024 21:15:35 -0300
Subject: [PATCH 06/31] Update README.md
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index a9d9ca6..a26be5f 100644
--- a/README.md
+++ b/README.md
@@ -36,8 +36,8 @@
- **Version Management**: Picodulce is designed to download and launch all available game versions, ensuring users have easy access to the latest updates as well as older versions.
- **Offline and Online Support**: Whether you're connected to Microsoft or not, Picodulce ensures you can still enjoy your game by supporting both offline and online modes.
-- **Integrated Mod Manager**: The latest update includes the [Marroc Mod Manager](https://github.com/nixietab/marroc), enabling users to effortlessly manage and customize their game with mods.
-
+- **Integrated Mod Manager**: Includes the [Marroc Mod Manager](https://github.com/nixietab/marroc), enabling users to effortlessly manage and customize their game with mods and texturepacks.
+- **Custom Theme Support**: Create and apply personalized themes with ease. A dedicated repository and guide are [available to help you get started.](https://github.com/nixietab/picodulce-themes)
### About the name
From 58e2b018d8727c6af58aad620d05cee7d4737f4f Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Sat, 21 Dec 2024 21:21:44 -0300
Subject: [PATCH 07/31] Update picodulce.py
---
picodulce.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/picodulce.py b/picodulce.py
index f7aec50..07f4276 100644
--- a/picodulce.py
+++ b/picodulce.py
@@ -352,7 +352,7 @@ class PicomcVersionSelector(QWidget):
title_label.setFont(QFont("Arial", 14))
# Create checkboxes for settings tab
- discord_rcp_checkbox = QCheckBox('Discord RPC')
+ discord_rcp_checkbox = QCheckBox('Discord Rich Presence')
discord_rcp_checkbox.setChecked(self.config.get("IsRCPenabled", False))
check_updates_checkbox = QCheckBox('Check Updates on Start')
@@ -391,7 +391,7 @@ class PicomcVersionSelector(QWidget):
current_theme_label = QLabel(f"Current Theme: {theme_filename}")
# QListWidget to display available themes
- json_files_label = QLabel('Available Themes:')
+ json_files_label = QLabel('Installed Themes:')
json_files_list_widget = QListWidget()
# Track selected theme
@@ -440,7 +440,7 @@ class PicomcVersionSelector(QWidget):
customization_layout.addWidget(json_files_list_widget)
# Button to download themes
- download_themes_button = QPushButton("Download Themes")
+ download_themes_button = QPushButton("Download More Themes")
download_themes_button.clicked.connect(self.download_themes_window)
customization_layout.addWidget(download_themes_button)
From b60b5179bca8cbea6bde51c9502d28ed4cd96d74 Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Mon, 23 Dec 2024 22:22:15 -0300
Subject: [PATCH 08/31] Update picodulce.py
---
picodulce.py | 87 +++++++++++++++++++++++++++++++---------------------
1 file changed, 52 insertions(+), 35 deletions(-)
diff --git a/picodulce.py b/picodulce.py
index 07f4276..347720e 100644
--- a/picodulce.py
+++ b/picodulce.py
@@ -387,7 +387,7 @@ class PicomcVersionSelector(QWidget):
theme_background_checkbox.setChecked(self.config.get("ThemeBackground", False))
# Label to show currently selected theme
- theme_filename = self.config.get('Theme', 'Default.json')
+ theme_filename = self.config.get('Theme', 'Dark.json')
current_theme_label = QLabel(f"Current Theme: {theme_filename}")
# QListWidget to display available themes
@@ -397,41 +397,13 @@ class PicomcVersionSelector(QWidget):
# Track selected theme
self.selected_theme = theme_filename # Default to current theme
- # Path to themes folder
- themes_folder = os.path.join(os.getcwd(), "themes")
-
- def populate_themes():
- json_files_list_widget.clear()
- if os.path.exists(themes_folder):
- json_files = [f for f in os.listdir(themes_folder) if f.endswith('.json')]
- for json_file in json_files:
- json_path = os.path.join(themes_folder, json_file)
- with open(json_path, 'r') as file:
- theme_data = json.load(file)
-
- # Get manifest details
- manifest = theme_data.get("manifest", {})
- name = manifest.get("name", "Unnamed")
- description = manifest.get("description", "No description available")
- author = manifest.get("author", "Unknown")
-
- # Create display text and list item
- display_text = f"#{name}\n{description}\nBy: {author}"
- list_item = QListWidgetItem(display_text)
- list_item.setData(Qt.UserRole, json_file) # Store the JSON filename as metadata
- json_files_list_widget.addItem(list_item)
-
- # Initially populate themes
- populate_themes()
+ # Populate themes initially
+ self.populate_themes(json_files_list_widget)
# Update current theme label when a theme is selected
- def on_theme_selected():
- selected_item = json_files_list_widget.currentItem()
- if selected_item:
- self.selected_theme = selected_item.data(Qt.UserRole)
- current_theme_label.setText(f"Current Theme: {self.selected_theme}")
-
- json_files_list_widget.itemClicked.connect(on_theme_selected)
+ json_files_list_widget.itemClicked.connect(
+ lambda: self.on_theme_selected(json_files_list_widget, current_theme_label)
+ )
# Add widgets to the layout
customization_layout.addWidget(theme_background_checkbox)
@@ -470,6 +442,51 @@ class PicomcVersionSelector(QWidget):
dialog.setLayout(main_layout)
dialog.exec_()
+ def populate_themes(self, json_files_list_widget):
+ themes_folder = os.path.join(os.getcwd(), "themes")
+ json_files_list_widget.clear()
+ if os.path.exists(themes_folder):
+ json_files = [f for f in os.listdir(themes_folder) if f.endswith('.json')]
+ for json_file in json_files:
+ json_path = os.path.join(themes_folder, json_file)
+ with open(json_path, 'r') as file:
+ theme_data = json.load(file)
+
+ # Get manifest details
+ manifest = theme_data.get("manifest", {})
+ name = manifest.get("name", "Unnamed")
+ description = manifest.get("description", "No description available")
+ author = manifest.get("author", "Unknown")
+
+ # Create display text and list item
+ display_text = f"{name}\n{description}\nBy: {author}"
+ list_item = QListWidgetItem(display_text)
+ list_item.setData(Qt.UserRole, json_file) # Store the JSON filename as metadata
+
+ # Style the name in bold
+ font = QFont()
+ font.setBold(False)
+ list_item.setFont(font)
+
+ json_files_list_widget.addItem(list_item)
+
+ # Apply spacing and styling to the list
+ json_files_list_widget.setStyleSheet("""
+ QListWidget {
+ padding: 1px;
+ }
+ QListWidget::item {
+ margin: 3px 0;
+ padding: 3px;
+ }
+ """)
+
+ def on_theme_selected(self, json_files_list_widget, current_theme_label):
+ selected_item = json_files_list_widget.currentItem()
+ if selected_item:
+ self.selected_theme = selected_item.data(Qt.UserRole)
+ current_theme_label.setText(f"Current Theme: {self.selected_theme}")
+
## REPOSITORY BLOCK BEGGINS
def download_themes_window(self):
@@ -500,7 +517,7 @@ class PicomcVersionSelector(QWidget):
# Initially load themes into the list
self.load_themes()
-
+ dialog.finished.connect(self.populate_themes)
dialog.exec_() # Open the dialog as a modal window
def fetch_themes(self):
From b824c2c4fb96fc09eb32c09a7edcef48b309fe76 Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Mon, 23 Dec 2024 22:28:50 -0300
Subject: [PATCH 09/31] minor changes
---
picodulce.py | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/picodulce.py b/picodulce.py
index 347720e..0dcef87 100644
--- a/picodulce.py
+++ b/picodulce.py
@@ -517,7 +517,6 @@ class PicomcVersionSelector(QWidget):
# Initially load themes into the list
self.load_themes()
- dialog.finished.connect(self.populate_themes)
dialog.exec_() # Open the dialog as a modal window
def fetch_themes(self):
@@ -577,13 +576,22 @@ class PicomcVersionSelector(QWidget):
themes_data = self.fetch_themes()
themes = themes_data.get("themes", [])
- theme_list.clear()
+ # Separate themes into installed and uninstalled
+ installed_themes = []
+ uninstalled_themes = []
+
for theme in themes:
- # Add "[I]" if the theme is installed
theme_display_name = f"{theme['name']} by {theme['author']}"
if self.is_theme_installed(theme['name']):
theme_display_name += " [I]" # Mark installed themes
- theme_list.addItem(theme_display_name)
+ installed_themes.append(theme_display_name)
+ else:
+ uninstalled_themes.append(theme_display_name)
+
+ # Clear the list and add uninstalled themes first, then installed ones
+ theme_list.clear()
+ theme_list.addItems(uninstalled_themes)
+ theme_list.addItems(installed_themes)
def on_theme_click(self):
selected_item = self.theme_list.currentItem()
@@ -618,9 +626,6 @@ class PicomcVersionSelector(QWidget):
self.download_theme_json(theme_url, theme_name)
self.load_themes() # Reload the list to show the "[I]" marker
-
-
-
## REPOSITORY BLOCK ENDS
From 70f6b2b59cc9871cf01fb792b8a6da377665e790 Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Thu, 26 Dec 2024 08:36:39 -0300
Subject: [PATCH 10/31] Update README.md
---
README.md | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/README.md b/README.md
index a26be5f..9afb9e0 100644
--- a/README.md
+++ b/README.md
@@ -39,6 +39,40 @@
- **Integrated Mod Manager**: Includes the [Marroc Mod Manager](https://github.com/nixietab/marroc), enabling users to effortlessly manage and customize their game with mods and texturepacks.
- **Custom Theme Support**: Create and apply personalized themes with ease. A dedicated repository and guide are [available to help you get started.](https://github.com/nixietab/picodulce-themes)
+# Installation
+If you are on windows you may be more interested in a [installer](https://github.com/nixietab/2hsu/releases/download/release/2hsu.exe)
+
+### 1. Clone the repository
+
+``` git clone https://github.com/nixietab/picodulce ```
+
+### 2. (Optional) Set Up a Virtual Environment
+Setting up a virtual environment is recommended to avoid dependency conflicts. Picodulce relies on the path of the `picomc` project, and using a virtual environment helps prevent errors.
+
+Create the virtual environment:
+
+``` python -m venv venv ```
+
+- **Linux/Mac:**
+ `source venv/bin/activate`
+- **Windows:**
+ `.\\venv\\Scripts\\activate`
+
+
+
+### Install requirements
+
+Now on the venv you can install the requirements safely
+
+```pip install -r requirements.txt ```
+
+### Running the launcher
+
+On the venv run it as a normal python script
+
+```python picodulce.py```
+
+Just make sure you have Java installed for running the actual game
### About the name
The name "Picodulce" comes from a popular Argentinian candy. This reflects the enjoyable and user-friendly experience that the launcher aims to provide, making game management straightforward and pleasant.
From 17f5f2a8d0aff2a9a871bdc70a3d7385fc9d8a12 Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Tue, 31 Dec 2024 10:50:11 -0300
Subject: [PATCH 11/31] Update version.json
---
version.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/version.json b/version.json
index 6185f05..bab01c9 100644
--- a/version.json
+++ b/version.json
@@ -1,5 +1,5 @@
{
- "version": "0.11.2",
+ "version": "0.11.3",
"links": [
"https://raw.githubusercontent.com/nixietab/picodulce/main/version.json",
"https://raw.githubusercontent.com/nixietab/picodulce/main/picodulce.py",
From 0028de0d698092adf92b3b65e3e94153dfa80e0f Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Tue, 31 Dec 2024 10:51:46 -0300
Subject: [PATCH 12/31] Update version.json
---
version.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/version.json b/version.json
index bab01c9..23fa914 100644
--- a/version.json
+++ b/version.json
@@ -1,5 +1,5 @@
{
- "version": "0.11.3",
+ "version": "0.11.32",
"links": [
"https://raw.githubusercontent.com/nixietab/picodulce/main/version.json",
"https://raw.githubusercontent.com/nixietab/picodulce/main/picodulce.py",
From b0ec76ebc36bdc497eef1cb600f86c76c7520c96 Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Tue, 31 Dec 2024 10:55:07 -0300
Subject: [PATCH 13/31] Update version.json
---
version.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/version.json b/version.json
index 23fa914..bab01c9 100644
--- a/version.json
+++ b/version.json
@@ -1,5 +1,5 @@
{
- "version": "0.11.32",
+ "version": "0.11.3",
"links": [
"https://raw.githubusercontent.com/nixietab/picodulce/main/version.json",
"https://raw.githubusercontent.com/nixietab/picodulce/main/picodulce.py",
From dbca6721bef0e0c2bf763743c35a4b31078197d2 Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Tue, 31 Dec 2024 11:27:29 -0300
Subject: [PATCH 14/31] Update version.json
---
version.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/version.json b/version.json
index bab01c9..cb9c5ab 100644
--- a/version.json
+++ b/version.json
@@ -1,5 +1,5 @@
{
- "version": "0.11.3",
+ "version": "0.11.4",
"links": [
"https://raw.githubusercontent.com/nixietab/picodulce/main/version.json",
"https://raw.githubusercontent.com/nixietab/picodulce/main/picodulce.py",
From f4eac0f4120b1d75f7b15e7c069cd2c5e7b23a2a Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Tue, 31 Dec 2024 15:38:24 -0300
Subject: [PATCH 15/31] Create Build.yml
---
.github/workflows/Build.yml | 67 +++++++++++++++++++++++++++++++++++++
1 file changed, 67 insertions(+)
create mode 100644 .github/workflows/Build.yml
diff --git a/.github/workflows/Build.yml b/.github/workflows/Build.yml
new file mode 100644
index 0000000..e92b90b
--- /dev/null
+++ b/.github/workflows/Build.yml
@@ -0,0 +1,67 @@
+name: Version Change Action
+
+on:
+ push:
+ paths:
+ - version.json # Trigger on changes to version.json
+
+jobs:
+ version-release:
+ runs-on: windows-latest # Use Windows 10 runner
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ - name: Set up Python
+ uses: actions/setup-python@v4
+ with:
+ python-version: '3.x' # Specify the Python version you need
+
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install pyqt5 requests pywin32 pyinstaller pillow # Install specific dependencies
+
+ - name: Create actions-temp folder
+ run: mkdir actions-temp # Create the folder called actions-temp
+
+ - name: Download picoBuild.py script
+ run: curl -L -o actions-temp/picoBuild.py https://raw.githubusercontent.com/nixietab/picodulce-build-script/refs/heads/main/picoBuild.py
+
+ - name: Run picoBuild.py script
+ run: python actions-temp/picoBuild.py
+
+ - name: Show directory structure
+ run: |
+ dir actions-temp
+ dir
+
+ - name: Get version and name from version.json
+ id: version_info
+ run: |
+ $versionJson = Get-Content version.json | ConvertFrom-Json
+ echo "RELEASE_NAME=Release $($versionJson.version)" >> $env:GITHUB_ENV
+ echo "RELEASE_TAG=$($versionJson.version)" >> $env:GITHUB_ENV
+
+ - name: Create GitHub Release
+ id: create_release
+ uses: actions/create-release@v1
+ with:
+ tag_name: ${{ env.RELEASE_TAG }}
+ release_name: ${{ env.RELEASE_NAME }}
+ body: "This release was created automatically by a GitHub Action."
+ draft: false
+ prerelease: false
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Upload Release Asset
+ uses: actions/upload-release-asset@v1
+ with:
+ upload_url: ${{ steps.create_release.outputs.upload_url }}
+ asset_path: build/2hsu.exe
+ asset_name: 2hsu.exe
+ asset_content_type: application/octet-stream
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
From b55ac71db1bbecc79dc06cdb3df289225c815d0d Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Tue, 31 Dec 2024 15:38:48 -0300
Subject: [PATCH 16/31] Update version.json
---
version.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/version.json b/version.json
index cb9c5ab..bab01c9 100644
--- a/version.json
+++ b/version.json
@@ -1,5 +1,5 @@
{
- "version": "0.11.4",
+ "version": "0.11.3",
"links": [
"https://raw.githubusercontent.com/nixietab/picodulce/main/version.json",
"https://raw.githubusercontent.com/nixietab/picodulce/main/picodulce.py",
From e02ca8ab7627bbbde817eb0fb2a181accf8be36d Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Wed, 1 Jan 2025 13:27:15 -0300
Subject: [PATCH 17/31] Added a rudimentary Instance manager
---
picodulce.py | 249 ++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 246 insertions(+), 3 deletions(-)
diff --git a/picodulce.py b/picodulce.py
index 0dcef87..503806d 100644
--- a/picodulce.py
+++ b/picodulce.py
@@ -299,6 +299,7 @@ class PicomcVersionSelector(QWidget):
"IsRCPenabled": False,
"CheckUpdate": False,
"LastPlayed": "",
+ "Instance": "default",
"Theme": "Dark.json",
"ThemeBackground": True,
"ThemeRepository": "https://raw.githubusercontent.com/nixietab/picodulce-themes/main/repo.json"
@@ -825,12 +826,18 @@ class PicomcVersionSelector(QWidget):
try:
# Set current_state to the selected instance
self.current_state = selected_instance
+
+ # Read the config.json to get the "Instance" value
+ with open('config.json', 'r') as config_file:
+ config = json.load(config_file)
+ instance_value = config.get("Instance", "default") # Default to "default" if not found
+
# Update lastplayed field in config.json on a separate thread
update_thread = threading.Thread(target=self.update_last_played, args=(selected_instance,))
update_thread.start()
- # Run the game subprocess
- subprocess.run(['picomc', 'play', selected_instance], check=True)
+ # Run the game subprocess with the instance_value from config.json
+ subprocess.run(['picomc', 'instance', 'launch', '--version-override', selected_instance, instance_value], check=True)
except subprocess.CalledProcessError as e:
error_message = f"Error playing {selected_instance}: {e}"
@@ -843,7 +850,7 @@ class PicomcVersionSelector(QWidget):
finally:
# Reset current_state to "menu" after the game closes
self.current_state = "menu"
-
+
def update_last_played(self, selected_instance):
config_path = "config.json"
self.config["LastPlayed"] = selected_instance
@@ -1235,9 +1242,11 @@ class ModLoaderAndVersionMenu(QDialog):
# Create tabs
install_mod_tab = QWidget()
download_version_tab = QWidget()
+ instances_tab = QWidget() # New tab for instances
tab_widget.addTab(download_version_tab, "Download Version")
tab_widget.addTab(install_mod_tab, "Install Mod Loader")
+ tab_widget.addTab(instances_tab, "Instances") # Add the new tab
# Add content to "Install Mod Loader" tab
self.setup_install_mod_loader_tab(install_mod_tab)
@@ -1245,6 +1254,240 @@ class ModLoaderAndVersionMenu(QDialog):
# Add content to "Download Version" tab
self.setup_download_version_tab(download_version_tab)
+ # Add content to "Instances" tab
+ self.setup_instances_tab(instances_tab)
+
+ def setup_instances_tab(self, instances_tab):
+ layout = QVBoxLayout(instances_tab)
+
+ # Create title label
+ title_label = QLabel('Current Instance:')
+ title_label.setFont(QFont("Arial", 14))
+ layout.addWidget(title_label)
+
+ # Create a label to display the current instance
+ self.current_instance_label = QLabel('Loading...') # Placeholder text
+ layout.addWidget(self.current_instance_label)
+
+ # Create a QListWidget to display the instances
+ self.instances_list_widget = QListWidget()
+ layout.addWidget(self.instances_list_widget)
+
+ # Create input field and button to create a new instance
+ self.create_instance_input = QLineEdit()
+ self.create_instance_input.setPlaceholderText("Enter instance name")
+ layout.addWidget(self.create_instance_input)
+
+ create_instance_button = QPushButton("Create Instance")
+ create_instance_button.clicked.connect(self.create_instance)
+ layout.addWidget(create_instance_button)
+
+ # Create input field and button to rename an instance
+ self.rename_instance_input = QLineEdit()
+ self.rename_instance_input.setPlaceholderText("Enter new instance name")
+ layout.addWidget(self.rename_instance_input)
+
+ rename_instance_button = QPushButton("Rename Instance")
+ rename_instance_button.clicked.connect(self.rename_instance)
+ layout.addWidget(rename_instance_button)
+
+ # Create button to delete an instance
+ delete_instance_button = QPushButton("Delete Instance")
+ delete_instance_button.clicked.connect(self.delete_instance)
+ layout.addWidget(delete_instance_button)
+
+ # Fetch and display the current instances
+ self.load_instances()
+
+ # Connect the item selection to the instance selection method
+ self.instances_list_widget.itemClicked.connect(self.on_instance_selected)
+
+ # Update the label with the current instance from the config
+ self.update_instance_label()
+
+ def create_instance(self):
+ instance_name = self.create_instance_input.text().strip()
+
+ if instance_name:
+ try:
+ # Run the "picomc instance create" command
+ process = subprocess.Popen(['picomc', 'instance', 'create', instance_name], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
+ output, error = process.communicate()
+
+ if process.returncode != 0:
+ raise subprocess.CalledProcessError(process.returncode, process.args, error)
+
+ # Notify the user that the instance was created
+ QMessageBox.information(self, "Instance Created", f"Instance '{instance_name}' has been created successfully.")
+
+ # Reload the instances list
+ self.load_instances()
+
+ # Optionally select the newly created instance
+ self.on_instance_selected(self.instances_list_widget.item(self.instances_list_widget.count() - 1))
+
+ except FileNotFoundError:
+ logging.error("'picomc' command not found. Please make sure it's installed and in your PATH.")
+ except subprocess.CalledProcessError as e:
+ logging.error("Error creating instance: %s", e.stderr)
+ QMessageBox.critical(self, "Error", f"Failed to create instance: {e.stderr}")
+ else:
+ QMessageBox.warning(self, "Invalid Input", "Please enter a valid instance name.")
+
+ def rename_instance(self):
+ selected_item = self.instances_list_widget.currentItem()
+
+ if not selected_item:
+ QMessageBox.warning(self, "No Selection", "Please select an instance to rename.")
+ return
+
+ old_instance_name = selected_item.text()
+ new_instance_name = self.rename_instance_input.text().strip()
+
+ if not new_instance_name:
+ QMessageBox.warning(self, "Invalid Input", "Please enter a valid new instance name.")
+ return
+
+ if old_instance_name == "default":
+ QMessageBox.warning(self, "Cannot Rename Instance", "You cannot rename the 'default' instance.")
+ return
+
+ try:
+ # Run the "picomc instance rename" command
+ process = subprocess.Popen(
+ ['picomc', 'instance', 'rename', old_instance_name, new_instance_name],
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
+ )
+ output, error = process.communicate()
+
+ if process.returncode != 0:
+ raise subprocess.CalledProcessError(process.returncode, process.args, error)
+
+ QMessageBox.information(self, "Instance Renamed", f"Instance '{old_instance_name}' has been renamed to '{new_instance_name}' successfully.")
+
+ # Reload the instances list
+ self.load_instances()
+
+ # Optionally select the newly renamed instance
+ matching_items = self.instances_list_widget.findItems(new_instance_name, Qt.MatchExactly)
+ if matching_items:
+ self.instances_list_widget.setCurrentItem(matching_items[0])
+
+ except FileNotFoundError:
+ logging.error("'picomc' command not found. Please make sure it's installed and in your PATH.")
+ except subprocess.CalledProcessError as e:
+ logging.error("Error renaming instance: %s", e.stderr)
+ QMessageBox.critical(self, "Error", f"Failed to rename instance: {e.stderr}")
+
+
+
+
+ def delete_instance(self):
+ # Get the selected instance name
+ selected_instance = self.instances_list_widget.currentItem()
+
+ if selected_instance:
+ instance_name = selected_instance.text()
+
+ # Prevent deletion of the "default" instance
+ if instance_name == "default":
+ QMessageBox.warning(self, "Cannot Delete Instance", "You cannot delete the 'default' instance.")
+ return
+
+ confirm_delete = QMessageBox.question(
+ self, "Confirm Deletion", f"Are you sure you want to delete the instance '{instance_name}'?",
+ QMessageBox.Yes | QMessageBox.No, QMessageBox.No
+ )
+
+ if confirm_delete == QMessageBox.Yes:
+ try:
+ # Run the "picomc instance delete" command
+ process = subprocess.Popen(['picomc', 'instance', 'delete', instance_name], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
+ output, error = process.communicate()
+
+ if process.returncode != 0:
+ raise subprocess.CalledProcessError(process.returncode, process.args, error)
+
+ # Notify the user that the instance was deleted
+ QMessageBox.information(self, "Instance Deleted", f"Instance '{instance_name}' has been deleted successfully.")
+
+ # Reload the instances list
+ self.load_instances()
+
+ except FileNotFoundError:
+ logging.error("'picomc' command not found. Please make sure it's installed and in your PATH.")
+ except subprocess.CalledProcessError as e:
+ logging.error("Error deleting instance: %s", e.stderr)
+ QMessageBox.critical(self, "Error", f"Failed to delete instance: {e.stderr}")
+ else:
+ QMessageBox.warning(self, "No Selection", "Please select an instance to delete.")
+
+ def load_instances(self):
+ try:
+ process = subprocess.Popen(['picomc', 'instance', 'list'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
+ output, error = process.communicate()
+ if process.returncode != 0:
+ raise subprocess.CalledProcessError(process.returncode, process.args, error)
+
+ # Parse the output and add each instance to the list widget
+ instances = output.splitlines()
+ self.instances_list_widget.clear() # Clear the previous list
+ self.instances_list_widget.addItems(instances)
+
+ except FileNotFoundError:
+ logging.error("'picomc' command not found. Please make sure it's installed and in your PATH.")
+ except subprocess.CalledProcessError as e:
+ logging.error("Error fetching instances: %s", e.stderr)
+
+ def on_instance_selected(self, item):
+ # Get the selected instance name
+ selected_instance = item.text()
+
+ # Path to the config.json file
+ config_file = 'config.json'
+
+ # Check if the config file exists
+ if os.path.exists(config_file):
+ try:
+ # Load the current config.json file
+ with open(config_file, 'r') as file:
+ config_data = json.load(file)
+
+ # Update the "Instance" value with the selected instance name
+ config_data['Instance'] = selected_instance
+
+ # Save the updated config back to the file
+ with open(config_file, 'w') as file:
+ json.dump(config_data, file, indent=4)
+
+ logging.info(f"Config updated: Instance set to {selected_instance}")
+
+ # Update the label with the new instance
+ self.update_instance_label()
+
+ except (json.JSONDecodeError, FileNotFoundError) as e:
+ logging.error(f"Error reading config.json: {e}")
+ else:
+ logging.warning(f"{config_file} not found. Unable to update instance.")
+
+ def update_instance_label(self):
+ config_file = 'config.json'
+
+ if os.path.exists(config_file):
+ try:
+ # Load the config file
+ with open(config_file, 'r') as file:
+ config_data = json.load(file)
+
+ # Get the current instance from the config and update the label
+ current_instance = config_data.get('Instance', 'Not set')
+ self.current_instance_label.setText(f'Current instance: {current_instance}')
+
+ except (json.JSONDecodeError, FileNotFoundError) as e:
+ logging.error(f"Error reading config.json: {e}")
+ else:
+ self.current_instance_label.setText('Current instance: Not set')
+
def setup_install_mod_loader_tab(self, install_mod_tab):
layout = QVBoxLayout(install_mod_tab)
From f4b0b39090278c05cc84f1c43ae8b847e6c7636c Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Wed, 1 Jan 2025 13:27:48 -0300
Subject: [PATCH 18/31] Update version.json
---
version.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/version.json b/version.json
index bab01c9..cb9c5ab 100644
--- a/version.json
+++ b/version.json
@@ -1,5 +1,5 @@
{
- "version": "0.11.3",
+ "version": "0.11.4",
"links": [
"https://raw.githubusercontent.com/nixietab/picodulce/main/version.json",
"https://raw.githubusercontent.com/nixietab/picodulce/main/picodulce.py",
From bf30a6ecd62568eb9da273fe55fd760a7da4d50d Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Thu, 2 Jan 2025 01:28:37 -0300
Subject: [PATCH 19/31] added a better instance GUI
---
picodulce.py | 158 +++++++++++++++++++++++++--------------------------
1 file changed, 78 insertions(+), 80 deletions(-)
diff --git a/picodulce.py b/picodulce.py
index 503806d..469be47 100644
--- a/picodulce.py
+++ b/picodulce.py
@@ -9,7 +9,7 @@ import requests
import json
import os
import time
-from PyQt5.QtWidgets import QApplication, QComboBox, QWidget, QVBoxLayout, QListWidget, QPushButton, QMessageBox, QDialog, QHBoxLayout, QLabel, QLineEdit, QCheckBox, QTabWidget, QFrame, QSpacerItem, QSizePolicy, QMainWindow, QGridLayout, QTextEdit, QListWidget, QListWidgetItem
+from PyQt5.QtWidgets import QApplication, QComboBox, QWidget, QInputDialog, QVBoxLayout, QListWidget, QPushButton, QMessageBox, QDialog, QHBoxLayout, QLabel, QLineEdit, QCheckBox, QTabWidget, QFrame, QSpacerItem, QSizePolicy, QMainWindow, QGridLayout, QTextEdit, QListWidget, QListWidgetItem, QMenu
from PyQt5.QtGui import QFont, QIcon, QColor, QPalette, QMovie, QPixmap, QDesktopServices, QBrush
from PyQt5.QtCore import Qt, QObject, pyqtSignal, QThread, QUrl, QMetaObject, Q_ARG, QByteArray, QSize
from datetime import datetime
@@ -1257,11 +1257,12 @@ class ModLoaderAndVersionMenu(QDialog):
# Add content to "Instances" tab
self.setup_instances_tab(instances_tab)
+
def setup_instances_tab(self, instances_tab):
layout = QVBoxLayout(instances_tab)
# Create title label
- title_label = QLabel('Current Instance:')
+ title_label = QLabel('Manage Minecraft Instances')
title_label.setFont(QFont("Arial", 14))
layout.addWidget(title_label)
@@ -1282,20 +1283,6 @@ class ModLoaderAndVersionMenu(QDialog):
create_instance_button.clicked.connect(self.create_instance)
layout.addWidget(create_instance_button)
- # Create input field and button to rename an instance
- self.rename_instance_input = QLineEdit()
- self.rename_instance_input.setPlaceholderText("Enter new instance name")
- layout.addWidget(self.rename_instance_input)
-
- rename_instance_button = QPushButton("Rename Instance")
- rename_instance_button.clicked.connect(self.rename_instance)
- layout.addWidget(rename_instance_button)
-
- # Create button to delete an instance
- delete_instance_button = QPushButton("Delete Instance")
- delete_instance_button.clicked.connect(self.delete_instance)
- layout.addWidget(delete_instance_button)
-
# Fetch and display the current instances
self.load_instances()
@@ -1334,20 +1321,7 @@ class ModLoaderAndVersionMenu(QDialog):
else:
QMessageBox.warning(self, "Invalid Input", "Please enter a valid instance name.")
- def rename_instance(self):
- selected_item = self.instances_list_widget.currentItem()
-
- if not selected_item:
- QMessageBox.warning(self, "No Selection", "Please select an instance to rename.")
- return
-
- old_instance_name = selected_item.text()
- new_instance_name = self.rename_instance_input.text().strip()
-
- if not new_instance_name:
- QMessageBox.warning(self, "Invalid Input", "Please enter a valid new instance name.")
- return
-
+ def rename_instance(self, old_instance_name, new_instance_name):
if old_instance_name == "default":
QMessageBox.warning(self, "Cannot Rename Instance", "You cannot rename the 'default' instance.")
return
@@ -1379,48 +1353,36 @@ class ModLoaderAndVersionMenu(QDialog):
logging.error("Error renaming instance: %s", e.stderr)
QMessageBox.critical(self, "Error", f"Failed to rename instance: {e.stderr}")
+ def delete_instance(self, instance_name):
+ if instance_name == "default":
+ QMessageBox.warning(self, "Cannot Delete Instance", "You cannot delete the 'default' instance.")
+ return
+ confirm_delete = QMessageBox.question(
+ self, "Confirm Deletion", f"Are you sure you want to delete the instance '{instance_name}'?",
+ QMessageBox.Yes | QMessageBox.No, QMessageBox.No
+ )
+ if confirm_delete == QMessageBox.Yes:
+ try:
+ # Run the "picomc instance delete" command
+ process = subprocess.Popen(['picomc', 'instance', 'delete', instance_name], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
+ output, error = process.communicate()
- def delete_instance(self):
- # Get the selected instance name
- selected_instance = self.instances_list_widget.currentItem()
+ if process.returncode != 0:
+ raise subprocess.CalledProcessError(process.returncode, process.args, error)
- if selected_instance:
- instance_name = selected_instance.text()
+ # Notify the user that the instance was deleted
+ QMessageBox.information(self, "Instance Deleted", f"Instance '{instance_name}' has been deleted successfully.")
- # Prevent deletion of the "default" instance
- if instance_name == "default":
- QMessageBox.warning(self, "Cannot Delete Instance", "You cannot delete the 'default' instance.")
- return
+ # Reload the instances list
+ self.load_instances()
- confirm_delete = QMessageBox.question(
- self, "Confirm Deletion", f"Are you sure you want to delete the instance '{instance_name}'?",
- QMessageBox.Yes | QMessageBox.No, QMessageBox.No
- )
-
- if confirm_delete == QMessageBox.Yes:
- try:
- # Run the "picomc instance delete" command
- process = subprocess.Popen(['picomc', 'instance', 'delete', instance_name], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
- output, error = process.communicate()
-
- if process.returncode != 0:
- raise subprocess.CalledProcessError(process.returncode, process.args, error)
-
- # Notify the user that the instance was deleted
- QMessageBox.information(self, "Instance Deleted", f"Instance '{instance_name}' has been deleted successfully.")
-
- # Reload the instances list
- self.load_instances()
-
- except FileNotFoundError:
- logging.error("'picomc' command not found. Please make sure it's installed and in your PATH.")
- except subprocess.CalledProcessError as e:
- logging.error("Error deleting instance: %s", e.stderr)
- QMessageBox.critical(self, "Error", f"Failed to delete instance: {e.stderr}")
- else:
- QMessageBox.warning(self, "No Selection", "Please select an instance to delete.")
+ except FileNotFoundError:
+ logging.error("'picomc' command not found. Please make sure it's installed and in your PATH.")
+ except subprocess.CalledProcessError as e:
+ logging.error("Error deleting instance: %s", e.stderr)
+ QMessageBox.critical(self, "Error", f"Failed to delete instance: {e.stderr}")
def load_instances(self):
try:
@@ -1432,37 +1394,74 @@ class ModLoaderAndVersionMenu(QDialog):
# Parse the output and add each instance to the list widget
instances = output.splitlines()
self.instances_list_widget.clear() # Clear the previous list
- self.instances_list_widget.addItems(instances)
+ for instance in instances:
+ item = QListWidgetItem()
+ self.instances_list_widget.addItem(item)
+ self.add_instance_buttons(item, instance)
except FileNotFoundError:
logging.error("'picomc' command not found. Please make sure it's installed and in your PATH.")
except subprocess.CalledProcessError as e:
logging.error("Error fetching instances: %s", e.stderr)
- def on_instance_selected(self, item):
- # Get the selected instance name
- selected_instance = item.text()
+ def add_instance_buttons(self, list_item, instance_name):
+ widget = QWidget()
+ layout = QHBoxLayout(widget)
+ layout.setContentsMargins(0, 0, 0, 0)
+
+ label = QLabel(instance_name)
+ rename_button = QPushButton("Rename")
+ delete_button = QPushButton("Delete")
+
+ # Stylize the buttons
+ button_style = """
+ QPushButton {
+ padding: 2px 5px;
+ }
+ """
+ rename_button.setStyleSheet(button_style)
+ delete_button.setStyleSheet(button_style)
+
+ layout.addWidget(label)
+ layout.addStretch()
+ layout.addWidget(rename_button)
+ layout.addWidget(delete_button)
+
+ widget.setLayout(layout)
+ list_item.setSizeHint(widget.sizeHint())
+ self.instances_list_widget.setItemWidget(list_item, widget)
+
+ # Connect button signals
+ rename_button.clicked.connect(lambda: self.prompt_rename_instance(instance_name))
+ delete_button.clicked.connect(lambda: self.delete_instance(instance_name))
+
+ def prompt_rename_instance(self, old_instance_name):
+ new_instance_name, ok = QInputDialog.getText(
+ self, "Rename Instance",
+ f"Enter new name for instance '{old_instance_name}':"
+ )
+
+ if ok and new_instance_name:
+ self.rename_instance(old_instance_name, new_instance_name)
+
+ def on_instance_selected(self, item):
+ widget = self.instances_list_widget.itemWidget(item)
+ instance_name = widget.findChild(QLabel).text()
- # Path to the config.json file
config_file = 'config.json'
- # Check if the config file exists
if os.path.exists(config_file):
try:
- # Load the current config.json file
with open(config_file, 'r') as file:
config_data = json.load(file)
- # Update the "Instance" value with the selected instance name
- config_data['Instance'] = selected_instance
+ config_data['Instance'] = instance_name
- # Save the updated config back to the file
with open(config_file, 'w') as file:
json.dump(config_data, file, indent=4)
- logging.info(f"Config updated: Instance set to {selected_instance}")
+ logging.info(f"Config updated: Instance set to {instance_name}")
- # Update the label with the new instance
self.update_instance_label()
except (json.JSONDecodeError, FileNotFoundError) as e:
@@ -1475,11 +1474,9 @@ class ModLoaderAndVersionMenu(QDialog):
if os.path.exists(config_file):
try:
- # Load the config file
with open(config_file, 'r') as file:
config_data = json.load(file)
- # Get the current instance from the config and update the label
current_instance = config_data.get('Instance', 'Not set')
self.current_instance_label.setText(f'Current instance: {current_instance}')
@@ -1488,6 +1485,7 @@ class ModLoaderAndVersionMenu(QDialog):
else:
self.current_instance_label.setText('Current instance: Not set')
+
def setup_install_mod_loader_tab(self, install_mod_tab):
layout = QVBoxLayout(install_mod_tab)
From c4f662235e1415f6217a4f2e8643be9385b684d3 Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Thu, 2 Jan 2025 01:29:09 -0300
Subject: [PATCH 20/31] Update picodulce.py
---
picodulce.py | 2 --
1 file changed, 2 deletions(-)
diff --git a/picodulce.py b/picodulce.py
index 469be47..ceac38b 100644
--- a/picodulce.py
+++ b/picodulce.py
@@ -22,10 +22,8 @@ class PicomcVersionSelector(QWidget):
self.open_dialogs = []
self.check_config_file()
self.themes_integrity()
- # Specify the path to the themes directory
themes_folder = "themes"
- # Default theme file name (can be changed)
theme_file = self.config.get("Theme", "Dark.json")
# Ensure the theme file exists in the themes directory
From 18692c3abc1411b7ea394cbe40d548f2943b6b37 Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Thu, 2 Jan 2025 01:40:45 -0300
Subject: [PATCH 21/31] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 9afb9e0..ed1ef6b 100644
--- a/README.md
+++ b/README.md
@@ -40,7 +40,7 @@
- **Custom Theme Support**: Create and apply personalized themes with ease. A dedicated repository and guide are [available to help you get started.](https://github.com/nixietab/picodulce-themes)
# Installation
-If you are on windows you may be more interested in a [installer](https://github.com/nixietab/2hsu/releases/download/release/2hsu.exe)
+If you are on windows you may be more interested in a [installer](https://github.com/nixietab/picodulce/releases/latest)
### 1. Clone the repository
From 27c75d1ef26bb9d2fd9831e784d1c4084a6cb88b Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Thu, 2 Jan 2025 01:41:00 -0300
Subject: [PATCH 22/31] Update version.json
---
version.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/version.json b/version.json
index cb9c5ab..ccff04d 100644
--- a/version.json
+++ b/version.json
@@ -1,5 +1,5 @@
{
- "version": "0.11.4",
+ "version": "0.11.5",
"links": [
"https://raw.githubusercontent.com/nixietab/picodulce/main/version.json",
"https://raw.githubusercontent.com/nixietab/picodulce/main/picodulce.py",
From 9c66a3eeb860de6a8616629692a05fc7ac2ec98d Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Sun, 5 Jan 2025 06:49:40 -0300
Subject: [PATCH 23/31] theme previews
---
picodulce.py | 68 ++++++++++++++++++++++++++--------------------------
1 file changed, 34 insertions(+), 34 deletions(-)
diff --git a/picodulce.py b/picodulce.py
index ceac38b..83ccf77 100644
--- a/picodulce.py
+++ b/picodulce.py
@@ -489,49 +489,41 @@ class PicomcVersionSelector(QWidget):
## REPOSITORY BLOCK BEGGINS
def download_themes_window(self):
- # Create a QDialog to open the themes window
dialog = QDialog(self)
dialog.setWindowTitle("Themes Repository")
- dialog.setGeometry(100, 100, 400, 300)
+ dialog.setGeometry(100, 100, 400, 500)
- # Layout setup for the new window
layout = QVBoxLayout()
- # List widget to display themes
self.theme_list = QListWidget(dialog)
self.theme_list.setSelectionMode(QListWidget.SingleSelection)
self.theme_list.clicked.connect(self.on_theme_click)
layout.addWidget(self.theme_list)
- # Label to display the details of the selected theme
- self.details_label = QLabel(dialog) # Define the label here
+ self.details_label = QLabel(dialog)
layout.addWidget(self.details_label)
- # Download button to download the selected theme's JSON
+ self.image_label = QLabel(dialog)
+ self.image_label.setAlignment(Qt.AlignCenter)
+ layout.addWidget(self.image_label)
+
download_button = QPushButton("Download Theme", dialog)
download_button.clicked.connect(self.theme_download)
layout.addWidget(download_button)
dialog.setLayout(layout)
-
- # Initially load themes into the list
self.load_themes()
- dialog.exec_() # Open the dialog as a modal window
+ dialog.exec_()
def fetch_themes(self):
try:
- # Read the config.json file
with open("config.json", "r") as config_file:
config = json.load(config_file)
-
- # Get the ThemeRepository value
url = config.get("ThemeRepository")
if not url:
raise ValueError("ThemeRepository is not defined in config.json")
-
- # Fetch themes from the specified URL
response = requests.get(url)
- response.raise_for_status() # Raise an exception for HTTP errors
+ response.raise_for_status()
return response.json()
except (FileNotFoundError, json.JSONDecodeError) as config_error:
self.show_error_popup("Error reading configuration", f"An error occurred while reading config.json: {config_error}")
@@ -546,13 +538,9 @@ class PicomcVersionSelector(QWidget):
def download_theme_json(self, theme_url, theme_name):
try:
response = requests.get(theme_url)
- response.raise_for_status() # Raise an exception for HTTP errors
-
- # Create the themes directory if it doesn't exist
+ response.raise_for_status()
if not os.path.exists('themes'):
os.makedirs('themes')
-
- # Save the content of the theme JSON file to the 'themes' folder
theme_filename = os.path.join('themes', f'{theme_name}.json')
with open(theme_filename, 'wb') as f:
f.write(response.content)
@@ -571,26 +559,20 @@ class PicomcVersionSelector(QWidget):
return os.path.exists(os.path.join('themes', f'{theme_name}.json'))
def load_themes(self):
- theme_list = self.theme_list
themes_data = self.fetch_themes()
themes = themes_data.get("themes", [])
-
- # Separate themes into installed and uninstalled
installed_themes = []
uninstalled_themes = []
-
for theme in themes:
theme_display_name = f"{theme['name']} by {theme['author']}"
if self.is_theme_installed(theme['name']):
- theme_display_name += " [I]" # Mark installed themes
+ theme_display_name += " [I]"
installed_themes.append(theme_display_name)
else:
uninstalled_themes.append(theme_display_name)
-
- # Clear the list and add uninstalled themes first, then installed ones
- theme_list.clear()
- theme_list.addItems(uninstalled_themes)
- theme_list.addItems(installed_themes)
+ self.theme_list.clear()
+ self.theme_list.addItems(uninstalled_themes)
+ self.theme_list.addItems(installed_themes)
def on_theme_click(self):
selected_item = self.theme_list.currentItem()
@@ -598,14 +580,32 @@ class PicomcVersionSelector(QWidget):
theme_name = selected_item.text().split(" by ")[0]
theme = self.find_theme_by_name(theme_name)
if theme:
- # Display theme details in the QLabel (details_label)
self.details_label.setText(
f"Name: {theme['name']}\n"
f"Description: {theme['description']}\n"
f"Author: {theme['author']}\n"
f"License: {theme['license']}\n"
- f"Link: {theme['link']}"
+ f"Link: {theme['link']}\n"
+ f"Preview URL: {theme.get('preview', 'N/A')}"
)
+ preview = theme.get('preview')
+ if preview:
+ image_data = self.fetch_image(preview)
+ if image_data:
+ pixmap = QPixmap()
+ pixmap.loadFromData(image_data)
+ self.image_label.setPixmap(pixmap)
+ else:
+ self.image_label.clear()
+
+ def fetch_image(self, url):
+ try:
+ response = requests.get(url)
+ response.raise_for_status()
+ return response.content
+ except requests.exceptions.RequestException as e:
+ self.show_error_popup("Error fetching image", f"An error occurred while fetching the image: {e}")
+ return None
def find_theme_by_name(self, theme_name):
themes_data = self.fetch_themes()
@@ -623,7 +623,7 @@ class PicomcVersionSelector(QWidget):
if theme:
theme_url = theme["link"]
self.download_theme_json(theme_url, theme_name)
- self.load_themes() # Reload the list to show the "[I]" marker
+ self.load_themes()
## REPOSITORY BLOCK ENDS
From 372075132fc927e802d4cdcb634a013e94e98c73 Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Mon, 6 Jan 2025 00:54:35 -0300
Subject: [PATCH 24/31] Update picodulce.py
---
picodulce.py | 30 +++++++++++++++++++++++-------
1 file changed, 23 insertions(+), 7 deletions(-)
diff --git a/picodulce.py b/picodulce.py
index 83ccf77..affe7d8 100644
--- a/picodulce.py
+++ b/picodulce.py
@@ -491,27 +491,38 @@ class PicomcVersionSelector(QWidget):
def download_themes_window(self):
dialog = QDialog(self)
dialog.setWindowTitle("Themes Repository")
- dialog.setGeometry(100, 100, 400, 500)
+ dialog.setGeometry(100, 100, 600, 500) # Adjust width for side-by-side layout
- layout = QVBoxLayout()
+ main_layout = QHBoxLayout(dialog) # Horizontal layout for splitting left and right
+ # Left side: Theme list
self.theme_list = QListWidget(dialog)
self.theme_list.setSelectionMode(QListWidget.SingleSelection)
self.theme_list.clicked.connect(self.on_theme_click)
- layout.addWidget(self.theme_list)
+ main_layout.addWidget(self.theme_list)
+
+ # Right side: Theme details, image, and download button
+ right_layout = QVBoxLayout()
self.details_label = QLabel(dialog)
- layout.addWidget(self.details_label)
+ self.details_label.setWordWrap(True) # Enable word wrap for better formatting
+ right_layout.addWidget(self.details_label)
self.image_label = QLabel(dialog)
self.image_label.setAlignment(Qt.AlignCenter)
- layout.addWidget(self.image_label)
+ right_layout.addWidget(self.image_label)
download_button = QPushButton("Download Theme", dialog)
download_button.clicked.connect(self.theme_download)
- layout.addWidget(download_button)
+ right_layout.addWidget(download_button)
+
+ # Add a spacer to push the button to the bottom
+ spacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
+ right_layout.addItem(spacer)
+
+ main_layout.addLayout(right_layout)
+ dialog.setLayout(main_layout)
- dialog.setLayout(layout)
self.load_themes()
dialog.exec_()
@@ -574,6 +585,11 @@ class PicomcVersionSelector(QWidget):
self.theme_list.addItems(uninstalled_themes)
self.theme_list.addItems(installed_themes)
+ # Autoselect the first item in the list if it exists
+ if self.theme_list.count() > 0:
+ self.theme_list.setCurrentRow(0)
+ self.on_theme_click()
+
def on_theme_click(self):
selected_item = self.theme_list.currentItem()
if selected_item:
From fd2e57ffb328163a514ecd4e7e5735783742d234 Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Mon, 6 Jan 2025 06:38:11 -0300
Subject: [PATCH 25/31] Update picodulce.py
---
picodulce.py | 23 ++++++++++++-----------
1 file changed, 12 insertions(+), 11 deletions(-)
diff --git a/picodulce.py b/picodulce.py
index affe7d8..c8d4efb 100644
--- a/picodulce.py
+++ b/picodulce.py
@@ -491,25 +491,25 @@ class PicomcVersionSelector(QWidget):
def download_themes_window(self):
dialog = QDialog(self)
dialog.setWindowTitle("Themes Repository")
- dialog.setGeometry(100, 100, 600, 500) # Adjust width for side-by-side layout
+ dialog.setGeometry(100, 100, 800, 600)
- main_layout = QHBoxLayout(dialog) # Horizontal layout for splitting left and right
+ main_layout = QHBoxLayout(dialog)
- # Left side: Theme list
self.theme_list = QListWidget(dialog)
self.theme_list.setSelectionMode(QListWidget.SingleSelection)
self.theme_list.clicked.connect(self.on_theme_click)
main_layout.addWidget(self.theme_list)
- # Right side: Theme details, image, and download button
right_layout = QVBoxLayout()
self.details_label = QLabel(dialog)
- self.details_label.setWordWrap(True) # Enable word wrap for better formatting
+ self.details_label.setWordWrap(True)
+ self.details_label.setStyleSheet("padding: 10px;")
right_layout.addWidget(self.details_label)
self.image_label = QLabel(dialog)
self.image_label.setAlignment(Qt.AlignCenter)
+ self.image_label.setStyleSheet("padding: 10px;")
right_layout.addWidget(self.image_label)
download_button = QPushButton("Download Theme", dialog)
@@ -597,13 +597,14 @@ class PicomcVersionSelector(QWidget):
theme = self.find_theme_by_name(theme_name)
if theme:
self.details_label.setText(
- f"Name: {theme['name']}\n"
- f"Description: {theme['description']}\n"
- f"Author: {theme['author']}\n"
- f"License: {theme['license']}\n"
- f"Link: {theme['link']}\n"
- f"Preview URL: {theme.get('preview', 'N/A')}"
+ f"Name: {theme['name']}
"
+ f"Description: {theme['description']}
"
+ f"Author: {theme['author']}
"
+ f"License: {theme['license']}
"
+ f"Link: {theme['link']}
"
)
+ self.details_label.setTextFormat(Qt.RichText)
+ self.details_label.setOpenExternalLinks(True)
preview = theme.get('preview')
if preview:
image_data = self.fetch_image(preview)
From 82f44105b57c10f3d259f9d81a253fbafb70d332 Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Mon, 6 Jan 2025 06:40:03 -0300
Subject: [PATCH 26/31] Update version.json
---
version.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/version.json b/version.json
index ccff04d..1cbcc92 100644
--- a/version.json
+++ b/version.json
@@ -1,5 +1,5 @@
{
- "version": "0.11.5",
+ "version": "0.11.6",
"links": [
"https://raw.githubusercontent.com/nixietab/picodulce/main/version.json",
"https://raw.githubusercontent.com/nixietab/picodulce/main/picodulce.py",
From 46d053d9522f4ded210076bc123bb43f4f23b5bc Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Mon, 6 Jan 2025 16:09:57 -0300
Subject: [PATCH 27/31] minor changes
---
picodulce.py | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/picodulce.py b/picodulce.py
index c8d4efb..790a797 100644
--- a/picodulce.py
+++ b/picodulce.py
@@ -104,7 +104,7 @@ class PicomcVersionSelector(QWidget):
stylesheet = self.theme["stylesheet"]
app.setStyleSheet(stylesheet)
else:
- print("No 'stylesheet' section found in the theme file.")
+ print("Theme dosn't seem to have a stylesheet")
def themes_integrity(self):
# Define folder and file paths
@@ -153,8 +153,6 @@ class PicomcVersionSelector(QWidget):
if not os.path.exists(themes_folder):
print(f"Creating folder: {themes_folder}")
os.makedirs(themes_folder)
- else:
- print(f"Folder already exists: {themes_folder}")
# Step 2: Ensure Dark.json exists
if not os.path.isfile(dark_theme_file):
@@ -162,8 +160,6 @@ class PicomcVersionSelector(QWidget):
with open(dark_theme_file, "w", encoding="utf-8") as file:
json.dump(dark_theme_content, file, indent=2)
print("Dark.json has been created successfully.")
- else:
- print(f"File already exists: {dark_theme_file}")
# Step 3: Ensure Native.json exists
if not os.path.isfile(native_theme_file):
@@ -171,9 +167,10 @@ class PicomcVersionSelector(QWidget):
with open(native_theme_file, "w", encoding="utf-8") as file:
json.dump(native_theme_content, file, indent=2)
print("Native.json has been created successfully.")
- else:
- print(f"File already exists: {native_theme_file}")
+ # Check if both files exist and print OK message
+ if os.path.isfile(dark_theme_file) and os.path.isfile(native_theme_file):
+ print("Theme Integrity OK")
def init_ui(self):
self.setWindowTitle('PicoDulce Launcher') # Change window title
From 5202f8fb25d33cba27a95ead85016a7ae91ea5ad Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Wed, 8 Jan 2025 22:36:18 -0300
Subject: [PATCH 28/31] Add files via upload
---
picodulce.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/picodulce.py b/picodulce.py
index 790a797..9058f0a 100644
--- a/picodulce.py
+++ b/picodulce.py
@@ -225,7 +225,6 @@ class PicomcVersionSelector(QWidget):
# Create play button for installed versions
self.play_button = QPushButton('Play')
- self.play_button.setFocusPolicy(Qt.NoFocus) # Set focus policy to prevent highlighting
self.play_button.clicked.connect(self.play_instance)
highlight_color = self.palette().color(QPalette.Highlight)
self.play_button.setStyleSheet(f"background-color: {highlight_color.name()}; color: white;")
@@ -1704,4 +1703,4 @@ if __name__ == '__main__':
app.setWindowIcon(QIcon('launcher_icon.ico')) # Set regular icon
window = PicomcVersionSelector()
window.show()
- sys.exit(app.exec_())
+ sys.exit(app.exec_())
\ No newline at end of file
From 51dc126c8a32c9bd1fb3445daf521631349cc1e1 Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Thu, 9 Jan 2025 23:20:35 -0300
Subject: [PATCH 29/31] minnor fixes
---
picodulce.py | 68 ++++++++++++++++++++++++++++++++++++----------------
1 file changed, 48 insertions(+), 20 deletions(-)
diff --git a/picodulce.py b/picodulce.py
index 9058f0a..363f528 100644
--- a/picodulce.py
+++ b/picodulce.py
@@ -3,6 +3,7 @@ import subprocess
import threading
from threading import Thread
import logging
+import re
import shutil
import platform
import requests
@@ -775,7 +776,20 @@ class PicomcVersionSelector(QWidget):
self.installed_version_combo.addItems(versions)
def populate_installed_versions_normal_order(self):
- # Run the command and get the output
+ # Run the 'picomc instance create default' command at the start
+ try:
+ process = subprocess.Popen(['picomc', 'instance', 'create', 'default'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
+ output, error = process.communicate()
+ if process.returncode != 0:
+ raise subprocess.CalledProcessError(process.returncode, process.args, error)
+ except FileNotFoundError:
+ logging.error("'picomc' command not found. Please make sure it's installed and in your PATH.")
+ return
+ except subprocess.CalledProcessError as e:
+ logging.error("Error creating default instance: %s", e.stderr)
+ return
+
+ # Run the 'picomc version list' command and get the output
try:
process = subprocess.Popen(['picomc', 'version', 'list'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
output, error = process.communicate()
@@ -946,6 +960,38 @@ class PicomcVersionSelector(QWidget):
dialog.exec_()
self.open_dialogs.remove(dialog)
+ def create_account(self, dialog, username, is_microsoft):
+ # Remove leading and trailing spaces from the username
+ username = username.strip()
+
+ if not username:
+ QMessageBox.warning(dialog, "Warning", "Username cannot be blank.")
+ return
+
+ if not self.is_valid_username(username):
+ QMessageBox.warning(dialog, "Warning", "Invalid username. Usernames must be 3-16 characters long and can only contain letters, numbers, and underscores.")
+ return
+
+ try:
+ command = ['picomc', 'account', 'create', username]
+ if is_microsoft:
+ command.append('--ms')
+
+ subprocess.run(command, check=True)
+ QMessageBox.information(dialog, "Success", f"Account '{username}' created successfully!")
+ self.populate_accounts_for_all_dialogs()
+ dialog.accept()
+ except subprocess.CalledProcessError as e:
+ error_message = f"Error creating account: {e.stderr.decode()}"
+ logging.error(error_message)
+ QMessageBox.critical(dialog, "Error", error_message)
+
+ def is_valid_username(self, username):
+ # Validate the username according to Minecraft's rules
+ if 3 <= len(username) <= 16 and re.match(r'^[a-zA-Z0-9_]+$', username):
+ return True
+ return False
+
def authenticate_account(self, dialog, account_name):
# Authenticate a selected account
account_name = account_name.strip().lstrip(" * ")
@@ -980,24 +1026,6 @@ class PicomcVersionSelector(QWidget):
logging.error(error_message)
QMessageBox.critical(dialog, "Error", error_message)
- def create_account(self, dialog, username, is_microsoft):
- # Create a new account
- if not username.strip():
- QMessageBox.warning(dialog, "Warning", "Username cannot be blank.")
- return
-
- try:
- command = ['picomc', 'account', 'create', username]
- if is_microsoft:
- command.append('--ms')
-
- subprocess.run(command, check=True)
- QMessageBox.information(dialog, "Success", f"Account '{username}' created successfully!")
- self.populate_accounts_for_all_dialogs()
- except subprocess.CalledProcessError as e:
- error_message = f"Error creating account: {e.stderr.decode()}"
- logging.error(error_message)
- QMessageBox.critical(dialog, "Error", error_message)
def populate_accounts(self, account_combo):
# Populate the account dropdown
@@ -1703,4 +1731,4 @@ if __name__ == '__main__':
app.setWindowIcon(QIcon('launcher_icon.ico')) # Set regular icon
window = PicomcVersionSelector()
window.show()
- sys.exit(app.exec_())
\ No newline at end of file
+ sys.exit(app.exec_())
From 816746283807169b3194e20f571759addccedeb1 Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Mon, 13 Jan 2025 20:19:25 -0300
Subject: [PATCH 30/31] added animated themes support!
---
picodulce.py | 44 ++++++++++++++++++++++++++++++++++----------
1 file changed, 34 insertions(+), 10 deletions(-)
diff --git a/picodulce.py b/picodulce.py
index 363f528..bae6253 100644
--- a/picodulce.py
+++ b/picodulce.py
@@ -173,6 +173,13 @@ class PicomcVersionSelector(QWidget):
if os.path.isfile(dark_theme_file) and os.path.isfile(native_theme_file):
print("Theme Integrity OK")
+
+ def resize_event(self, event):
+ if hasattr(self, 'movie_label'):
+ self.movie_label.setGeometry(0, 0, self.width(), self.height())
+ event.accept() # Accept the resize event
+
+
def init_ui(self):
self.setWindowTitle('PicoDulce Launcher') # Change window title
current_date = datetime.now()
@@ -188,27 +195,44 @@ class PicomcVersionSelector(QWidget):
with open("config.json", "r") as config_file:
config = json.load(config_file)
- if config.get("ThemeBackground", False): # Default to False if ThemeBackground is missing
+ if self.config.get("ThemeBackground", False): # Default to False if ThemeBackground is missing
# Get the base64 string for the background image from the theme file
theme_background_base64 = self.theme.get("background_image_base64", "")
if theme_background_base64:
try:
- # Decode the base64 string and create a QPixmap
+ # Decode the base64 string to get the binary data
background_image_data = QByteArray.fromBase64(theme_background_base64.encode())
- pixmap = QPixmap()
- if pixmap.loadFromData(background_image_data):
+ temp_gif_path = "temp.gif" # Write the gif into a temp file because Qt stuff
+ with open(temp_gif_path, 'wb') as temp_gif_file:
+ temp_gif_file.write(background_image_data)
+
+ # Create a QMovie object from the temporary file
+ movie = QMovie(temp_gif_path)
+ if movie.isValid():
self.setAutoFillBackground(True)
palette = self.palette()
- palette.setBrush(QPalette.Window, QBrush(pixmap.scaled(
- self.size(), Qt.KeepAspectRatioByExpanding, Qt.SmoothTransformation
- )))
+
+ # Set the QMovie to a QLabel
+ self.movie_label = QLabel(self)
+ self.movie_label.setMovie(movie)
+ self.movie_label.setGeometry(0, 0, movie.frameRect().width(), movie.frameRect().height())
+ self.movie_label.setScaledContents(True) # Ensure the QLabel scales its contents
+ movie.start()
+
+ # Use the QLabel pixmap as the brush texture
+ brush = QBrush(QPixmap(movie.currentPixmap()))
+ brush.setStyle(Qt.TexturePattern)
+ palette.setBrush(QPalette.Window, brush)
self.setPalette(palette)
+
+ # Adjust the QLabel size when the window is resized
+ self.movie_label.resizeEvent = self.resize_event
else:
- print("Error: Failed to load background image from base64 string.")
+ print("Error: Failed to load background GIF from base64 string.")
except Exception as e:
- print(f"Error: Failed to decode and set background image. {e}")
+ print(f"Error: Failed to decode and set background GIF. {e}")
else:
- print("No background image base64 string found in the theme file.")
+ print("No background GIF base64 string found in the theme file.")
# Create title label
title_label = QLabel('PicoDulce Launcher') # Change label text
From d2f3aa6a4911f5c39aab0dee7431dfdcc71d19e3 Mon Sep 17 00:00:00 2001
From: Nix <75538775+nixietab@users.noreply.github.com>
Date: Tue, 14 Jan 2025 00:15:39 -0300
Subject: [PATCH 31/31] Update version.json
---
version.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/version.json b/version.json
index 1cbcc92..3a8dd18 100644
--- a/version.json
+++ b/version.json
@@ -1,5 +1,5 @@
{
- "version": "0.11.6",
+ "version": "0.11.7",
"links": [
"https://raw.githubusercontent.com/nixietab/picodulce/main/version.json",
"https://raw.githubusercontent.com/nixietab/picodulce/main/picodulce.py",