initial commit
This commit is contained in:
commit
e575f95965
665
main.py
Normal file
665
main.py
Normal file
@ -0,0 +1,665 @@
|
||||
"""
|
||||
Markdown Editor Application using PyQt6.
|
||||
|
||||
Key Features:
|
||||
- Create, edit, and preview Markdown files.
|
||||
- Live HTML preview of Markdown content.
|
||||
- Configurable settings stored in YAML.
|
||||
|
||||
Dependencies: PyQt6, yaml, markdown2.
|
||||
|
||||
Author: Isaak Buslovich
|
||||
Date: 2024-01-13
|
||||
Version: 0.1
|
||||
"""
|
||||
import pathlib
|
||||
import platform
|
||||
import sys
|
||||
import webbrowser
|
||||
import yaml
|
||||
from PyQt6.QtCore import QFileSystemWatcher, Qt
|
||||
from PyQt6.QtGui import QAction, QStandardItem, QStandardItemModel, QIcon, QPalette
|
||||
from PyQt6.QtWebEngineWidgets import QWebEngineView
|
||||
from PyQt6.QtWebEngineCore import QWebEngineSettings
|
||||
from PyQt6.QtWidgets import QApplication, QMainWindow, QTextEdit, QHBoxLayout, QWidget, QToolBar, QStatusBar, \
|
||||
QMessageBox, QTreeView, QFileDialog, QAbstractItemView, QLineEdit, QVBoxLayout
|
||||
from markdown2 import markdown
|
||||
from fuzzywuzzy import process, fuzz
|
||||
|
||||
|
||||
class MarkdownEditor(QMainWindow):
|
||||
"""
|
||||
A PyQt6-based Markdown editor with file navigation, text editing, and live HTML preview.
|
||||
|
||||
Attributes:
|
||||
config_path (pathlib.Path): Configuration file path.
|
||||
config (dict): Application configuration.
|
||||
fileSystemWatcher (QFileSystemWatcher): Monitors file system changes.
|
||||
dataDirectory (pathlib.Path): Data directory path.
|
||||
directoryTree (QTreeView): File navigation widget.
|
||||
editor (QTextEdit): Markdown text editor.
|
||||
preview (QWebEngineView): Live HTML preview widget.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
""" Initializes the Markdown editor application with UI setup and configuration management. """
|
||||
super().__init__()
|
||||
self.dataDirectory = None
|
||||
self.preview = None
|
||||
self.editor = None
|
||||
self.directoryTree = None
|
||||
self.config_path = pathlib.Path(__file__).parent / 'config.yaml'
|
||||
self.toolbar = None
|
||||
if not self.config_path.exists():
|
||||
self.config = {'window': {'width': 800, 'height': 600},
|
||||
'dataDirectory': str(pathlib.Path(__file__).parent.resolve())}
|
||||
self.save_config()
|
||||
else:
|
||||
self.config = self.load_config()
|
||||
self.fileSystemWatcher = QFileSystemWatcher()
|
||||
self.initialize_data_directory()
|
||||
self.initialize_ui()
|
||||
self.update_directory_tree_view()
|
||||
self.fileSystemWatcher.directoryChanged.connect(self.update_directory_tree_view)
|
||||
self.load_initial_file()
|
||||
|
||||
def initialize_ui(self):
|
||||
""" Sets up the application's user interface components. """
|
||||
self.setWindowTitle("Markdown Editor")
|
||||
width = self.config['window']['width']
|
||||
height = self.config['window']['height']
|
||||
self.setGeometry(100, 100, width, height)
|
||||
icon_path = self.dataDirectory / 'data' / 'smile.png'
|
||||
self.setWindowIcon(QIcon(str(icon_path)))
|
||||
|
||||
# Create the directory tree view
|
||||
self.directoryTree = QTreeView()
|
||||
self.directoryTree.setHeaderHidden(True)
|
||||
self.directoryTree.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
|
||||
self.directoryTree.doubleClicked.connect(self.on_file_double_clicked)
|
||||
|
||||
# Create the QLineEdit widget for the search box
|
||||
self.searchBox = QLineEdit()
|
||||
self.searchBox.setPlaceholderText("Search...") # Placeholder text for the search box
|
||||
self.searchBox.textChanged.connect(self.on_search_text_changed) # Connect to search handler
|
||||
|
||||
# Create a QVBoxLayout for the directory tree and search box
|
||||
directory_layout = QVBoxLayout()
|
||||
directory_layout.addWidget(self.searchBox) # Add the search box to the layout
|
||||
directory_layout.addWidget(self.directoryTree) # Add the directory tree below the search box
|
||||
|
||||
# Text editor setup
|
||||
self.editor = QTextEdit()
|
||||
self.editor.textChanged.connect(self.update_preview)
|
||||
|
||||
# Preview setup
|
||||
self.preview = QWebEngineView()
|
||||
|
||||
web_engine_settings = self.preview.page().settings()
|
||||
web_engine_settings.setAttribute(QWebEngineSettings.WebAttribute.LocalContentCanAccessFileUrls, True)
|
||||
web_engine_settings.setAttribute(QWebEngineSettings.WebAttribute.LocalContentCanAccessRemoteUrls, True)
|
||||
|
||||
# Main layout setup
|
||||
layout = QHBoxLayout()
|
||||
layout.addLayout(directory_layout, 0) # Add the QVBoxLayout with the search box and directory tree
|
||||
layout.addWidget(self.editor, 1)
|
||||
layout.addWidget(self.preview, 1)
|
||||
|
||||
# Set the main layout
|
||||
container = QWidget()
|
||||
container.setLayout(layout)
|
||||
self.setCentralWidget(container)
|
||||
|
||||
# Toolbar, menu bar, and status bar setup
|
||||
self.create_tool_bar()
|
||||
self.create_menu_bar()
|
||||
self.setStatusBar(QStatusBar(self))
|
||||
|
||||
|
||||
def initialize_data_directory(self):
|
||||
""" Initializes the data directory and sets up the file system watcher. """
|
||||
data_directory = self.config['dataDirectory']
|
||||
self.dataDirectory = pathlib.Path(data_directory).resolve()
|
||||
if not self.dataDirectory.is_dir():
|
||||
QMessageBox.critical(self, "Directory Error", "The application's directory is not valid.")
|
||||
sys.exit(1)
|
||||
self.fileSystemWatcher.addPath(str(self.dataDirectory))
|
||||
|
||||
|
||||
def on_search_text_changed(self, text):
|
||||
"""
|
||||
Handles the search functionality.
|
||||
First, tries an exact match. If no results are found, uses fuzzy matching and gradually increases fuzziness.
|
||||
"""
|
||||
if not self.dataDirectory or not self.dataDirectory.is_dir():
|
||||
return
|
||||
model = QStandardItemModel()
|
||||
self.directoryTree.setModel(model)
|
||||
try:
|
||||
entries = list(self.dataDirectory.rglob('*'))
|
||||
filtered_entries = []
|
||||
if text:
|
||||
filtered_entries = [entry for entry in entries if text.lower() in entry.name.lower()]
|
||||
fuzziness = 100
|
||||
while not filtered_entries and fuzziness > 0:
|
||||
matches = [(entry, fuzz.partial_ratio(text.lower(), entry.name.lower()))
|
||||
for entry in entries]
|
||||
filtered_entries = [entry for entry, score in matches if score >= fuzziness]
|
||||
if len(filtered_entries) >= 2:
|
||||
break
|
||||
fuzziness -= 5
|
||||
for entry in filtered_entries:
|
||||
self.add_item_to_model(model, entry, is_dir=entry.is_dir())
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Search Error", f"An error occurred during the search: {e}")
|
||||
|
||||
|
||||
|
||||
def add_item_to_model(self, model, entry, is_dir=False):
|
||||
"""
|
||||
Adds an item to the directory tree model.
|
||||
|
||||
Args:
|
||||
model (QStandardItemModel): The model of the directory tree.
|
||||
entry (Path): The file or directory path.
|
||||
is_dir (bool): Indicates if the entry is a directory.
|
||||
"""
|
||||
item = QStandardItem(entry.name)
|
||||
item.setData(str(entry))
|
||||
item.setFlags(item.flags() | Qt.ItemFlag.ItemIsEnabled)
|
||||
if is_dir:
|
||||
item.setIcon(QIcon('icons/folder_icon.png'))
|
||||
model.appendRow(item)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def get_config_path():
|
||||
"""
|
||||
Get the path to the configuration file based on the operating system.
|
||||
|
||||
Returns:
|
||||
Path: The path to the configuration file.
|
||||
"""
|
||||
app_name = "YourApp"
|
||||
home = pathlib.Path.home()
|
||||
if platform.system() == "Windows":
|
||||
config_path = pathlib.Path(home, "AppData", "Roaming", app_name, "config.yaml")
|
||||
elif platform.system() == "Darwin":
|
||||
config_path = pathlib.Path(home, "Library", "Application Support", app_name, "config.yaml")
|
||||
else:
|
||||
config_path = pathlib.Path(home, ".config", app_name, "config.yaml")
|
||||
config_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
return config_path
|
||||
|
||||
config_file = get_config_path()
|
||||
|
||||
def closeEvent(self, event):
|
||||
"""
|
||||
Handle the close event of the application. Save the configuration before closing.
|
||||
|
||||
Args:
|
||||
event (QCloseEvent): The close event.
|
||||
"""
|
||||
self.config['window']['width'] = self.size().width()
|
||||
self.config['window']['height'] = self.size().height()
|
||||
self.config['dataDirectory'] = str(self.dataDirectory)
|
||||
self.save_config()
|
||||
super().closeEvent(event)
|
||||
|
||||
def load_config(self):
|
||||
"""
|
||||
Load the configuration from the configuration file.
|
||||
|
||||
Returns:
|
||||
dict: The loaded configuration.
|
||||
"""
|
||||
with open(self.config_path) as file:
|
||||
return yaml.safe_load(file)
|
||||
|
||||
def save_config(self):
|
||||
"""
|
||||
Save the configuration to the configuration file.
|
||||
"""
|
||||
with open(self.config_path, 'w') as file:
|
||||
yaml.dump(self.config, file)
|
||||
|
||||
def load_initial_file(self):
|
||||
"""
|
||||
Load an initial Markdown file for editing. This method looks for a 'cheatsheet.md' file in the
|
||||
application's data directory and loads it into the editor if it exists.
|
||||
"""
|
||||
cheatsheet_path = self.dataDirectory / 'cheatsheet.md'
|
||||
if cheatsheet_path.is_file():
|
||||
with open(cheatsheet_path, encoding='utf-8') as file:
|
||||
markdown_content = file.read()
|
||||
self.editor.setText(markdown_content)
|
||||
self.update_preview()
|
||||
else:
|
||||
QMessageBox.warning(self, "File Not Found", f"Cheatsheet file not found: {cheatsheet_path}")
|
||||
|
||||
def on_file_double_clicked(self, index):
|
||||
"""
|
||||
Loads a Markdown file into the editor on double-click in the directory tree.
|
||||
|
||||
Args:
|
||||
index (QModelIndex): Index of the clicked item in the directory tree.
|
||||
"""
|
||||
try:
|
||||
file_path = pathlib.Path(index.model().itemFromIndex(index).data())
|
||||
if not file_path.exists():
|
||||
QMessageBox.critical(self, "Error", f"The item does not exist: {file_path}")
|
||||
return
|
||||
if not file_path.is_file():
|
||||
QMessageBox.critical(self, "Error", "The selected item is not a file.")
|
||||
return
|
||||
with open(file_path, encoding='utf-8') as file:
|
||||
markdown_content = file.read()
|
||||
self.editor.setText(markdown_content)
|
||||
self.update_preview()
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "File Open Error", f"An error occurred: {e}")
|
||||
|
||||
def create_menu_bar(self):
|
||||
"""
|
||||
Create the menu bar for the application. It adds various menus like File, Edit, View, Window, and Help
|
||||
with their respective actions.
|
||||
"""
|
||||
menu_bar = self.menuBar()
|
||||
file_menu = menu_bar.addMenu("&File")
|
||||
self.add_actions_to_menu(file_menu, [
|
||||
("&New", self.new_file),
|
||||
("&Open", self.open_file),
|
||||
("&Save", self.save_entry),
|
||||
("Open Data Directory", self.open_data_directory),
|
||||
("Change Data Directory", self.change_data_directory),
|
||||
("Theme", self.change_theme),
|
||||
("Settings", self.open_settings),
|
||||
("E&xit", self.close)
|
||||
])
|
||||
|
||||
edit_menu = menu_bar.addMenu("&Edit")
|
||||
self.add_actions_to_menu(edit_menu, [
|
||||
("Undo", self.undo_action),
|
||||
("Redo", self.redo_action)
|
||||
])
|
||||
|
||||
view_menu = menu_bar.addMenu("&View")
|
||||
self.add_actions_to_menu(view_menu, [
|
||||
("Toggle Split View Mode", self.toggle_split_view),
|
||||
("Toggle Toolbar", self.toggle_toolbar)
|
||||
])
|
||||
|
||||
window_menu = menu_bar.addMenu("&Window")
|
||||
self.add_actions_to_menu(window_menu, [
|
||||
("Close", self.close),
|
||||
("Minimize", self.minimize),
|
||||
("Search", self.search)
|
||||
])
|
||||
|
||||
help_menu = menu_bar.addMenu("&Help")
|
||||
self.add_actions_to_menu(help_menu, [
|
||||
("&About", self.about),
|
||||
("View Cheatsheet", self.view_cheatsheet),
|
||||
("GitHub", self.open_git_hub)
|
||||
])
|
||||
|
||||
def add_actions_to_menu(self, menu, actions):
|
||||
"""
|
||||
Add actions to a given menu.
|
||||
|
||||
Args:
|
||||
menu (QMenu): The menu to which actions will be added.
|
||||
actions (list of tuples): Each tuple contains the text for the menu item and the handler function.
|
||||
"""
|
||||
for action_text, action_handler in actions:
|
||||
action = QAction(action_text, self)
|
||||
action.triggered.connect(action_handler)
|
||||
menu.addAction(action)
|
||||
|
||||
def create_tool_bar(self):
|
||||
"""
|
||||
Create the main toolbar for the application. It adds actions like New, Open, and Save with icons.
|
||||
"""
|
||||
self.toolbar = QToolBar("Main Toolbar")
|
||||
self.addToolBar(self.toolbar)
|
||||
self.add_actions_to_toolbar(self.toolbar, [
|
||||
("New", "📄", self.new_file),
|
||||
("Open", "📂", self.open_file),
|
||||
("Save", "💾", self.save_entry)
|
||||
])
|
||||
|
||||
def add_actions_to_toolbar(self, toolbar, actions):
|
||||
"""
|
||||
Add actions to a given toolbar.
|
||||
"""
|
||||
for name, emoji, handler in actions:
|
||||
action = QAction(emoji + ' ' + name, self)
|
||||
action.triggered.connect(handler)
|
||||
toolbar.addAction(action)
|
||||
|
||||
@staticmethod
|
||||
def markdown_table_to_html(markdown_content):
|
||||
"""
|
||||
Convert Markdown tables in the given content to HTML table format.
|
||||
|
||||
Args:
|
||||
markdown_content (str): The Markdown content containing tables.
|
||||
|
||||
Returns:
|
||||
str: The HTML content with Markdown tables converted to HTML tables.
|
||||
"""
|
||||
lines = markdown_content.split('\n')
|
||||
html_lines = []
|
||||
in_table = False
|
||||
|
||||
for line in lines:
|
||||
if '|' in line:
|
||||
if not in_table:
|
||||
html_lines.append('<table>')
|
||||
in_table = True
|
||||
columns = line.split('|')[1:-1]
|
||||
if '---' in columns[0]:
|
||||
html_lines.append('<tr>' + ''.join('<th>{}</th>'.format(col.strip()) for col in columns) + '</tr>')
|
||||
else:
|
||||
html_lines.append('<tr>' + ''.join('<td>{}</td>'.format(col.strip()) for col in columns) + '</tr>')
|
||||
else:
|
||||
if in_table:
|
||||
html_lines.append('</table>')
|
||||
in_table = False
|
||||
html_lines.append(line)
|
||||
|
||||
if in_table:
|
||||
html_lines.append('</table>')
|
||||
|
||||
return '\n'.join(html_lines)
|
||||
|
||||
@staticmethod
|
||||
def convert_image_paths(html_content, base_directory):
|
||||
"""
|
||||
Converts relative image paths in the HTML content to absolute file URLs.
|
||||
|
||||
Args:
|
||||
html_content (str): The HTML content with Markdown images.
|
||||
base_directory (Path): The base directory where the 'data' folder is located.
|
||||
|
||||
Returns:
|
||||
str: Modified HTML content with absolute image paths.
|
||||
"""
|
||||
image_folder = base_directory / 'data'
|
||||
|
||||
def replace_path(match):
|
||||
"""
|
||||
Replaces a matched image path with an absolute URL.
|
||||
|
||||
Args:
|
||||
match (MatchObject): Regex match object containing the image path.
|
||||
|
||||
Returns:
|
||||
str: The image URL with the absolute path.
|
||||
"""
|
||||
image_relative_path = match.group(1)
|
||||
image_absolute_path = image_folder / image_relative_path
|
||||
return f'src="{image_absolute_path.as_uri()}"'
|
||||
|
||||
import re
|
||||
html_content = re.sub(r'src="([^"]+)"', replace_path, html_content)
|
||||
return html_content
|
||||
|
||||
def update_preview(self):
|
||||
""" Updates the HTML preview based on the current Markdown content. """
|
||||
markdown_content = self.editor.toPlainText()
|
||||
html_content = markdown(markdown_content)
|
||||
html_content = self.markdown_table_to_html(html_content)
|
||||
data_directory = pathlib.Path(__file__).parent.resolve()
|
||||
html_content = self.convert_image_paths(html_content, data_directory)
|
||||
html_content = html_content.replace('[x]', '✅')
|
||||
html_content = html_content.replace('[ ]', '❌')
|
||||
palette = QApplication.instance().palette()
|
||||
background_color = palette.color(QPalette.ColorRole.Window).name()
|
||||
foreground_color = palette.color(QPalette.ColorRole.WindowText).name()
|
||||
accent_color = "#9f74b3"
|
||||
css = f"""
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Lora&display=swap');
|
||||
/* Main body styling */
|
||||
body {{
|
||||
font-family: 'Lora', serif;
|
||||
background-color: {background_color};
|
||||
color: {foreground_color};
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}}
|
||||
/* Container for the content */
|
||||
.content-container {{
|
||||
max-width: 600px;
|
||||
width: 95%;
|
||||
margin: 20px;
|
||||
}}
|
||||
/* Headers styling */
|
||||
h1, h2, h3, h4, h5, h6 {{
|
||||
color: {accent_color};
|
||||
}}
|
||||
/* Style for scrollbar */
|
||||
::-webkit-scrollbar {{
|
||||
width: 12px;
|
||||
}}
|
||||
::-webkit-scrollbar-track {{
|
||||
background: {background_color};
|
||||
}}
|
||||
::-webkit-scrollbar-thumb {{
|
||||
background: #555;
|
||||
border-radius: 6px;
|
||||
}}
|
||||
::-webkit-scrollbar-thumb:hover {{
|
||||
background: {accent_color};
|
||||
}}
|
||||
/* Table styling */
|
||||
table {{
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}}
|
||||
th, td {{
|
||||
border: 1px solid {foreground_color};
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
background-color: {background_color};
|
||||
color: {foreground_color};
|
||||
}}
|
||||
th {{
|
||||
background-color: {accent_color};
|
||||
color: white;
|
||||
}}
|
||||
tr:nth-child(even) {{
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}}
|
||||
tr:hover {{
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
}}
|
||||
</style>
|
||||
"""
|
||||
html_with_css = css + '<div class="content-container">' + html_content + '</div>'
|
||||
self.preview.setHtml(html_with_css)
|
||||
|
||||
def update_directory_tree_view(self):
|
||||
""" Refreshes the directory tree view with files and directories. """
|
||||
if not self.dataDirectory or not self.dataDirectory.is_dir():
|
||||
return
|
||||
model = QStandardItemModel()
|
||||
self.directoryTree.setModel(model)
|
||||
self.populate_model(model, self.dataDirectory)
|
||||
|
||||
def populate_model(self, parent_item, directory, depth=0):
|
||||
"""
|
||||
Populates the directory tree model with directory contents.
|
||||
|
||||
Args:
|
||||
parent_item (QStandardItem): Item to populate.
|
||||
directory (pathlib.Path): Directory to scan.
|
||||
depth (int): Depth level for recursive scanning.
|
||||
"""
|
||||
if depth > 5:
|
||||
return
|
||||
try:
|
||||
for entry in sorted(directory.iterdir(), key=lambda e: (e.is_file(), e.name.lower())):
|
||||
if entry.is_dir():
|
||||
if any(file.suffix == '.md' for file in entry.glob('*.md')):
|
||||
dir_item = QStandardItem(entry.name)
|
||||
dir_item.setData(str(entry))
|
||||
dir_item.setFlags(dir_item.flags() | Qt.ItemFlag.ItemIsEnabled)
|
||||
dir_item.setIcon(QIcon('icons/folder_icon.png'))
|
||||
parent_item.appendRow(dir_item)
|
||||
self.populate_model(dir_item, entry, depth + 1)
|
||||
elif entry.suffix == '.md':
|
||||
file_item = QStandardItem(entry.name)
|
||||
file_item.setData(str(entry))
|
||||
parent_item.appendRow(file_item)
|
||||
except PermissionError:
|
||||
pass
|
||||
|
||||
def on_directory_tree_clicked(self, index):
|
||||
"""
|
||||
Handle clicks on the directory tree. It loads the selected file into the editor if it is a Markdown file.
|
||||
|
||||
Args:
|
||||
index (QModelIndex): The model index of the clicked item in the directory tree.
|
||||
"""
|
||||
file_path = pathlib.Path(index.data())
|
||||
if file_path.is_file():
|
||||
self.load_file(file_path)
|
||||
|
||||
def load_file(self, file_path):
|
||||
"""
|
||||
Loads the content of a file into the text editor.
|
||||
|
||||
Args:
|
||||
file_path (pathlib.Path): The path of the file to load.
|
||||
"""
|
||||
if not file_path.is_file():
|
||||
QMessageBox.critical(self, "File Open Error", "The selected file does not exist.")
|
||||
return
|
||||
|
||||
try:
|
||||
with open(file_path, encoding='utf-8') as file:
|
||||
markdown_content = file.read()
|
||||
except OSError as e:
|
||||
QMessageBox.critical(self, "File Open Error", f"Error opening file: {e}")
|
||||
return
|
||||
|
||||
self.editor.setText(markdown_content)
|
||||
self.update_preview()
|
||||
|
||||
def new_file(self):
|
||||
"""
|
||||
Clear the editor to create a new file. This action empties the text editor for writing new content.
|
||||
"""
|
||||
self.editor.clear()
|
||||
|
||||
def open_file(self):
|
||||
"""
|
||||
Open a file dialog to select a Markdown file. This method allows the user to select a Markdown file from
|
||||
the file system and loads its content into the editor.
|
||||
"""
|
||||
filename, _ = QFileDialog.getOpenFileName(
|
||||
self,
|
||||
"Open Markdown File",
|
||||
str(self.dataDirectory),
|
||||
'Markdown Files (*.md)')
|
||||
if not filename:
|
||||
return
|
||||
try:
|
||||
with open(filename, encoding='utf-8') as file:
|
||||
markdown_content = file.read()
|
||||
except OSError as e:
|
||||
QMessageBox.critical(self, "File Open Error", f"Error opening file: {e}")
|
||||
return
|
||||
self.editor.setText(markdown_content)
|
||||
self.update_preview()
|
||||
|
||||
def save_entry(self):
|
||||
"""
|
||||
Save the current entry. This method is intended to save the content of the text editor to a file.
|
||||
"""
|
||||
pass
|
||||
|
||||
def about(self):
|
||||
"""
|
||||
Displays an 'About' dialog with information about the Markdown Editor.
|
||||
"""
|
||||
QMessageBox.about(self, "About Markdown Editor", "Markdown Editor\nVersion 1.0")
|
||||
|
||||
def open_data_directory(self):
|
||||
""" Opens the application's data directory in the system's default file explorer. """
|
||||
try:
|
||||
directory_url = pathlib.Path(self.dataDirectory).resolve().as_uri()
|
||||
webbrowser.open(directory_url)
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Error", f"Failed to open directory: {e}")
|
||||
|
||||
def change_data_directory(self):
|
||||
"""
|
||||
Opens a dialog to select a new data directory for the application.
|
||||
"""
|
||||
new_data_directory = QFileDialog.getExistingDirectory(self, "Select Data Directory", str(self.dataDirectory))
|
||||
if new_data_directory:
|
||||
new_path = pathlib.Path(new_data_directory)
|
||||
if new_path.is_dir():
|
||||
self.dataDirectory = new_path
|
||||
self.update_directory_tree_view()
|
||||
else:
|
||||
QMessageBox.critical(self, "Directory Selection Error", "Invalid directory path")
|
||||
|
||||
def change_theme(self):
|
||||
pass
|
||||
|
||||
def open_settings(self):
|
||||
pass
|
||||
|
||||
def undo_action(self):
|
||||
pass
|
||||
|
||||
def redo_action(self):
|
||||
pass
|
||||
|
||||
def toggle_split_view(self):
|
||||
""" Toggles the visibility of the preview pane. """
|
||||
self.preview.setVisible(not self.preview.isVisible())
|
||||
self.centralWidget().layout().activate()
|
||||
|
||||
def toggle_toolbar(self):
|
||||
""" Toggle the visibility of the toolbar. """
|
||||
if self.toolbar:
|
||||
self.toolbar.setVisible(not self.toolbar.isVisible())
|
||||
|
||||
def minimize(self):
|
||||
""" Minimize the main window. """
|
||||
self.showMinimized()
|
||||
|
||||
def search(self):
|
||||
pass
|
||||
|
||||
def view_cheatsheet(self):
|
||||
""" Load and display the cheatsheet markdown file. """
|
||||
cheatsheet_path = self.dataDirectory / 'cheatsheet.md'
|
||||
if cheatsheet_path.is_file():
|
||||
self.load_file(cheatsheet_path)
|
||||
else:
|
||||
QMessageBox.warning(self, "Cheatsheet Not Found", f"Cheatsheet file not found: {cheatsheet_path}")
|
||||
|
||||
def open_git_hub(self):
|
||||
pass
|
||||
|
||||
|
||||
def main():
|
||||
""" Main function to run the Markdown Editor application. """
|
||||
app = QApplication(sys.argv)
|
||||
editor = MarkdownEditor()
|
||||
editor.show()
|
||||
sys.exit(app.exec())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Loading…
Reference in New Issue
Block a user