Compare commits
No commits in common. "cc8aaca2068d62c0f7ac7a182e884c7467ae94be" and "e8fdb820caa6d4bd3b3140dae4c29a1d29e5bbcb" have entirely different histories.
cc8aaca206
...
e8fdb820ca
276
main.py
276
main.py
@ -15,10 +15,9 @@ Version: 0.1
|
|||||||
import pathlib
|
import pathlib
|
||||||
import platform
|
import platform
|
||||||
import sys
|
import sys
|
||||||
import threading
|
|
||||||
import webbrowser
|
import webbrowser
|
||||||
import yaml
|
import yaml
|
||||||
from PyQt6.QtCore import QFileSystemWatcher, Qt, QTimer
|
from PyQt6.QtCore import QFileSystemWatcher, Qt
|
||||||
from PyQt6.QtGui import QAction, QStandardItem, QStandardItemModel, QIcon, QPalette
|
from PyQt6.QtGui import QAction, QStandardItem, QStandardItemModel, QIcon, QPalette
|
||||||
from PyQt6.QtWebEngineWidgets import QWebEngineView
|
from PyQt6.QtWebEngineWidgets import QWebEngineView
|
||||||
from PyQt6.QtWebEngineCore import QWebEngineSettings
|
from PyQt6.QtWebEngineCore import QWebEngineSettings
|
||||||
@ -26,9 +25,6 @@ from PyQt6.QtWidgets import QApplication, QMainWindow, QTextEdit, QHBoxLayout, Q
|
|||||||
QMessageBox, QTreeView, QFileDialog, QAbstractItemView, QLineEdit, QVBoxLayout
|
QMessageBox, QTreeView, QFileDialog, QAbstractItemView, QLineEdit, QVBoxLayout
|
||||||
from markdown2 import markdown
|
from markdown2 import markdown
|
||||||
from fuzzywuzzy import process, fuzz
|
from fuzzywuzzy import process, fuzz
|
||||||
import logging
|
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
|
||||||
|
|
||||||
|
|
||||||
class MarkdownEditor(QMainWindow):
|
class MarkdownEditor(QMainWindow):
|
||||||
@ -47,7 +43,6 @@ class MarkdownEditor(QMainWindow):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
""" Initializes the Markdown editor application with UI setup and configuration management. """
|
""" Initializes the Markdown editor application with UI setup and configuration management. """
|
||||||
logging.info("Initializing the Markdown Editor...")
|
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.dataDirectory = None
|
self.dataDirectory = None
|
||||||
self.preview = None
|
self.preview = None
|
||||||
@ -67,115 +62,100 @@ class MarkdownEditor(QMainWindow):
|
|||||||
self.update_directory_tree_view()
|
self.update_directory_tree_view()
|
||||||
self.fileSystemWatcher.directoryChanged.connect(self.update_directory_tree_view)
|
self.fileSystemWatcher.directoryChanged.connect(self.update_directory_tree_view)
|
||||||
self.load_initial_file()
|
self.load_initial_file()
|
||||||
self.searchTimer = QTimer()
|
|
||||||
self.searchTimer.setSingleShot(True)
|
|
||||||
self.searchTimer.timeout.connect(self.perform_search)
|
|
||||||
|
|
||||||
def initialize_ui(self):
|
def initialize_ui(self):
|
||||||
""" Sets up the application's user interface components. """
|
""" Sets up the application's user interface components. """
|
||||||
logging.info("Setting up the user interface...")
|
|
||||||
self.setWindowTitle("Markdown Editor")
|
self.setWindowTitle("Markdown Editor")
|
||||||
width = self.config['window']['width']
|
width = self.config['window']['width']
|
||||||
height = self.config['window']['height']
|
height = self.config['window']['height']
|
||||||
self.setGeometry(100, 100, width, height)
|
self.setGeometry(100, 100, width, height)
|
||||||
icon_path = self.dataDirectory / 'data' / 'smile.png'
|
icon_path = self.dataDirectory / 'data' / 'smile.png'
|
||||||
self.setWindowIcon(QIcon(str(icon_path)))
|
self.setWindowIcon(QIcon(str(icon_path)))
|
||||||
|
|
||||||
|
# Create the directory tree view
|
||||||
self.directoryTree = QTreeView()
|
self.directoryTree = QTreeView()
|
||||||
self.directoryTree.setHeaderHidden(True)
|
self.directoryTree.setHeaderHidden(True)
|
||||||
self.directoryTree.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
|
self.directoryTree.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
|
||||||
self.directoryTree.doubleClicked.connect(self.on_file_double_clicked)
|
self.directoryTree.doubleClicked.connect(self.on_file_double_clicked)
|
||||||
|
|
||||||
|
# Create the QLineEdit widget for the search box
|
||||||
self.searchBox = QLineEdit()
|
self.searchBox = QLineEdit()
|
||||||
self.searchBox.setPlaceholderText("Search...")
|
self.searchBox.setPlaceholderText("Search...") # Placeholder text for the search box
|
||||||
self.searchBox.textChanged.connect(self.on_search_text_changed)
|
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 = QVBoxLayout()
|
||||||
directory_layout.addWidget(self.searchBox)
|
directory_layout.addWidget(self.searchBox) # Add the search box to the layout
|
||||||
directory_layout.addWidget(self.directoryTree)
|
directory_layout.addWidget(self.directoryTree) # Add the directory tree below the search box
|
||||||
|
|
||||||
|
# Text editor setup
|
||||||
self.editor = QTextEdit()
|
self.editor = QTextEdit()
|
||||||
self.editor.textChanged.connect(self.update_preview)
|
self.editor.textChanged.connect(self.update_preview)
|
||||||
|
|
||||||
|
# Preview setup
|
||||||
self.preview = QWebEngineView()
|
self.preview = QWebEngineView()
|
||||||
|
|
||||||
web_engine_settings = self.preview.page().settings()
|
web_engine_settings = self.preview.page().settings()
|
||||||
web_engine_settings.setAttribute(QWebEngineSettings.WebAttribute.LocalContentCanAccessFileUrls, True)
|
web_engine_settings.setAttribute(QWebEngineSettings.WebAttribute.LocalContentCanAccessFileUrls, True)
|
||||||
web_engine_settings.setAttribute(QWebEngineSettings.WebAttribute.LocalContentCanAccessRemoteUrls, True)
|
web_engine_settings.setAttribute(QWebEngineSettings.WebAttribute.LocalContentCanAccessRemoteUrls, True)
|
||||||
|
|
||||||
|
# Main layout setup
|
||||||
layout = QHBoxLayout()
|
layout = QHBoxLayout()
|
||||||
layout.addLayout(directory_layout, 0)
|
layout.addLayout(directory_layout, 0) # Add the QVBoxLayout with the search box and directory tree
|
||||||
layout.addWidget(self.editor, 1)
|
layout.addWidget(self.editor, 1)
|
||||||
layout.addWidget(self.preview, 1)
|
layout.addWidget(self.preview, 1)
|
||||||
|
|
||||||
|
# Set the main layout
|
||||||
container = QWidget()
|
container = QWidget()
|
||||||
container.setLayout(layout)
|
container.setLayout(layout)
|
||||||
self.setCentralWidget(container)
|
self.setCentralWidget(container)
|
||||||
|
|
||||||
|
# Toolbar, menu bar, and status bar setup
|
||||||
self.create_tool_bar()
|
self.create_tool_bar()
|
||||||
self.create_menu_bar()
|
self.create_menu_bar()
|
||||||
self.setStatusBar(QStatusBar(self))
|
self.setStatusBar(QStatusBar(self))
|
||||||
|
|
||||||
|
|
||||||
def initialize_data_directory(self):
|
def initialize_data_directory(self):
|
||||||
"""
|
""" Initializes the data directory and sets up the file system watcher. """
|
||||||
Initializes the data directory and sets up the file system watcher.
|
data_directory = self.config['dataDirectory']
|
||||||
"""
|
|
||||||
logging.info("Initializing data directory.")
|
|
||||||
try:
|
|
||||||
data_directory = self.config.get('dataDirectory', str(pathlib.Path.home()))
|
|
||||||
self.dataDirectory = pathlib.Path(data_directory).resolve()
|
self.dataDirectory = pathlib.Path(data_directory).resolve()
|
||||||
if not self.dataDirectory.is_dir():
|
if not self.dataDirectory.is_dir():
|
||||||
raise FileNotFoundError(f"Data directory not found: {self.dataDirectory}")
|
QMessageBox.critical(self, "Directory Error", "The application's directory is not valid.")
|
||||||
self.fileSystemWatcher.addPath(str(self.dataDirectory))
|
|
||||||
logging.info(f"Data directory set to: {self.dataDirectory}")
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"Failed to initialize data directory: {e}")
|
|
||||||
QMessageBox.critical(self, "Directory Error", str(e))
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
self.fileSystemWatcher.addPath(str(self.dataDirectory))
|
||||||
|
|
||||||
|
|
||||||
def on_search_text_changed(self, text):
|
def on_search_text_changed(self, text):
|
||||||
self.searchText = text
|
"""
|
||||||
if not text:
|
Handles the search functionality.
|
||||||
self.update_directory_tree_view()
|
First, tries an exact match. If no results are found, uses fuzzy matching and gradually increases fuzziness.
|
||||||
else:
|
"""
|
||||||
self.searchTimer.start(60)
|
if not self.dataDirectory or not self.dataDirectory.is_dir():
|
||||||
|
return
|
||||||
def update_search_results(self, text):
|
|
||||||
model = QStandardItemModel()
|
model = QStandardItemModel()
|
||||||
self.populate_filtered_model(model, self.dataDirectory, text)
|
|
||||||
self.directoryTree.setModel(model)
|
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 perform_search(self):
|
|
||||||
threading.Thread(target=self.update_search_results, args=(self.searchText,)).start()
|
|
||||||
|
|
||||||
def populate_filtered_model(self, parent_item, directory, text, fuzziness=100, depth=0):
|
|
||||||
"""
|
|
||||||
Recursively populates the directory tree model with filtered contents based on the search text.
|
|
||||||
|
|
||||||
Args:
|
def add_item_to_model(self, model, entry, is_dir=False):
|
||||||
parent_item (QStandardItem): The parent item in the tree view model.
|
|
||||||
directory (pathlib.Path): The directory to scan.
|
|
||||||
text (str): The search text.
|
|
||||||
fuzziness (int): The fuzziness level for matching file names.
|
|
||||||
depth (int): Current depth of recursion.
|
|
||||||
"""
|
|
||||||
if depth > self.depth:
|
|
||||||
return False
|
|
||||||
dir_added = False
|
|
||||||
for entry in sorted(directory.iterdir(), key=lambda e: (e.is_file(), e.name.lower())):
|
|
||||||
if entry.is_dir():
|
|
||||||
dir_item = QStandardItem(entry.name)
|
|
||||||
if self.populate_filtered_model(dir_item, entry, text, fuzziness, depth + 1):
|
|
||||||
parent_item.appendRow(dir_item)
|
|
||||||
dir_added = True
|
|
||||||
elif entry.suffix == '.md' and self.is_match(entry.name, text, fuzziness):
|
|
||||||
file_item = QStandardItem(entry.name)
|
|
||||||
file_item.setData(str(entry))
|
|
||||||
parent_item.appendRow(file_item)
|
|
||||||
dir_added = True
|
|
||||||
return dir_added
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def is_match(filename, text, fuzziness):
|
|
||||||
if not text:
|
|
||||||
return False
|
|
||||||
if fuzziness == 100:
|
|
||||||
return text.lower() in filename.lower()
|
|
||||||
return fuzz.partial_ratio(text.lower(), filename.lower()) >= fuzziness
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def add_item_to_model(model, entry, is_dir=False):
|
|
||||||
"""
|
"""
|
||||||
Adds an item to the directory tree model.
|
Adds an item to the directory tree model.
|
||||||
|
|
||||||
@ -191,6 +171,7 @@ class MarkdownEditor(QMainWindow):
|
|||||||
item.setIcon(QIcon('icons/folder_icon.png'))
|
item.setIcon(QIcon('icons/folder_icon.png'))
|
||||||
model.appendRow(item)
|
model.appendRow(item)
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_config_path():
|
def get_config_path():
|
||||||
"""
|
"""
|
||||||
@ -230,22 +211,10 @@ class MarkdownEditor(QMainWindow):
|
|||||||
Load the configuration from the configuration file.
|
Load the configuration from the configuration file.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
dict: The loaded configuration with default values if keys are missing.
|
dict: The loaded configuration.
|
||||||
"""
|
"""
|
||||||
default_config = {
|
|
||||||
'window': {'width': 800, 'height': 600},
|
|
||||||
'dataDirectory': str(pathlib.Path(__file__).parent.resolve()),
|
|
||||||
'depth': 5
|
|
||||||
}
|
|
||||||
try:
|
|
||||||
with open(self.config_path) as file:
|
with open(self.config_path) as file:
|
||||||
config = yaml.safe_load(file) or {}
|
return yaml.safe_load(file)
|
||||||
except FileNotFoundError:
|
|
||||||
config = {}
|
|
||||||
for key, value in default_config.items():
|
|
||||||
config.setdefault(key, value)
|
|
||||||
self.depth = config['depth']
|
|
||||||
return config
|
|
||||||
|
|
||||||
def save_config(self):
|
def save_config(self):
|
||||||
"""
|
"""
|
||||||
@ -317,12 +286,7 @@ class MarkdownEditor(QMainWindow):
|
|||||||
view_menu = menu_bar.addMenu("&View")
|
view_menu = menu_bar.addMenu("&View")
|
||||||
self.add_actions_to_menu(view_menu, [
|
self.add_actions_to_menu(view_menu, [
|
||||||
("Toggle Split View Mode", self.toggle_split_view),
|
("Toggle Split View Mode", self.toggle_split_view),
|
||||||
("Toggle Toolbar", self.toggle_toolbar),
|
("Toggle Toolbar", self.toggle_toolbar)
|
||||||
("Theme", [
|
|
||||||
("Dark", lambda: self.change_theme("dark")),
|
|
||||||
("Light", lambda: self.change_theme("light")),
|
|
||||||
("System", lambda: self.change_theme("system"))
|
|
||||||
])
|
|
||||||
])
|
])
|
||||||
|
|
||||||
window_menu = menu_bar.addMenu("&Window")
|
window_menu = menu_bar.addMenu("&Window")
|
||||||
@ -340,16 +304,16 @@ class MarkdownEditor(QMainWindow):
|
|||||||
])
|
])
|
||||||
|
|
||||||
def add_actions_to_menu(self, menu, actions):
|
def add_actions_to_menu(self, menu, actions):
|
||||||
for action_text, action_func in actions:
|
"""
|
||||||
if isinstance(action_func, list): # Submenu
|
Add actions to a given menu.
|
||||||
submenu = menu.addMenu(action_text)
|
|
||||||
for sub_action_text, sub_action_func in action_func:
|
Args:
|
||||||
sub_action = QAction(sub_action_text, self)
|
menu (QMenu): The menu to which actions will be added.
|
||||||
sub_action.triggered.connect(sub_action_func)
|
actions (list of tuples): Each tuple contains the text for the menu item and the handler function.
|
||||||
submenu.addAction(sub_action)
|
"""
|
||||||
else:
|
for action_text, action_handler in actions:
|
||||||
action = QAction(action_text, self)
|
action = QAction(action_text, self)
|
||||||
action.triggered.connect(action_func)
|
action.triggered.connect(action_handler)
|
||||||
menu.addAction(action)
|
menu.addAction(action)
|
||||||
|
|
||||||
def create_tool_bar(self):
|
def create_tool_bar(self):
|
||||||
@ -531,53 +495,30 @@ class MarkdownEditor(QMainWindow):
|
|||||||
def populate_model(self, parent_item, directory, depth=0):
|
def populate_model(self, parent_item, directory, depth=0):
|
||||||
"""
|
"""
|
||||||
Populates the directory tree model with directory contents.
|
Populates the directory tree model with directory contents.
|
||||||
Shows all directories containing Markdown files and Markdown files themselves.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
parent_item (QStandardItem): Item to populate.
|
parent_item (QStandardItem): Item to populate.
|
||||||
directory (pathlib.Path): Directory to scan.
|
directory (pathlib.Path): Directory to scan.
|
||||||
depth (int): Depth level for recursive scanning.
|
depth (int): Depth level for recursive scanning.
|
||||||
"""
|
"""
|
||||||
if depth > self.depth:
|
if depth > 5:
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
for entry in sorted(directory.iterdir(), key=lambda e: (e.is_file(), e.name.lower())):
|
for entry in sorted(directory.iterdir(), key=lambda e: (e.is_file(), e.name.lower())):
|
||||||
if entry.is_dir():
|
if entry.is_dir():
|
||||||
self.process_directory(entry, parent_item, depth)
|
if any(file.suffix == '.md' for file in entry.glob('*.md')):
|
||||||
elif entry.suffix == '.md':
|
dir_item = QStandardItem(entry.name)
|
||||||
self.add_file_item(entry, parent_item)
|
dir_item.setData(str(entry))
|
||||||
except PermissionError as e:
|
dir_item.setFlags(dir_item.flags() | Qt.ItemFlag.ItemIsEnabled)
|
||||||
logging.error(f"Permission error accessing {directory}: {e}")
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def add_file_item(file_path, parent_item):
|
|
||||||
"""
|
|
||||||
Adds a file item to the model.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
file_path (pathlib.Path): The file path to add.
|
|
||||||
parent_item (QStandardItem): The parent item in the model.
|
|
||||||
"""
|
|
||||||
file_item = QStandardItem(file_path.name)
|
|
||||||
file_item.setData(str(file_path))
|
|
||||||
parent_item.appendRow(file_item)
|
|
||||||
|
|
||||||
def process_directory(self, directory, parent_item, depth):
|
|
||||||
"""
|
|
||||||
Processes a single directory, adding it to the model if it contains Markdown files.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
directory (pathlib.Path): The directory to process.
|
|
||||||
parent_item (QStandardItem): The parent item in the model.
|
|
||||||
depth (int): Current depth in the directory tree.
|
|
||||||
"""
|
|
||||||
if any(file.suffix == '.md' for file in directory.iterdir()):
|
|
||||||
dir_item = QStandardItem(directory.name)
|
|
||||||
dir_item.setData(str(directory))
|
|
||||||
dir_item.setIcon(QIcon('icons/folder_icon.png'))
|
dir_item.setIcon(QIcon('icons/folder_icon.png'))
|
||||||
parent_item.appendRow(dir_item)
|
parent_item.appendRow(dir_item)
|
||||||
self.populate_model(dir_item, directory, depth + 1)
|
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):
|
def on_directory_tree_clicked(self, index):
|
||||||
"""
|
"""
|
||||||
@ -671,64 +612,8 @@ class MarkdownEditor(QMainWindow):
|
|||||||
else:
|
else:
|
||||||
QMessageBox.critical(self, "Directory Selection Error", "Invalid directory path")
|
QMessageBox.critical(self, "Directory Selection Error", "Invalid directory path")
|
||||||
|
|
||||||
def change_theme(self, theme):
|
def change_theme(self):
|
||||||
if theme == "dark":
|
pass
|
||||||
self.apply_dark_theme()
|
|
||||||
elif theme == "light":
|
|
||||||
self.apply_light_theme()
|
|
||||||
elif theme == "system":
|
|
||||||
self.apply_system_theme()
|
|
||||||
|
|
||||||
def apply_dark_theme(self):
|
|
||||||
dark_stylesheet = """
|
|
||||||
QMainWindow {
|
|
||||||
background-color: #282c34; /* Dark gray background */
|
|
||||||
color: #abb2bf; /* Light gray text */
|
|
||||||
}
|
|
||||||
QMenuBar {
|
|
||||||
background-color: #21252b; /* Slightly darker gray for menu bar */
|
|
||||||
color: #abb2bf;
|
|
||||||
}
|
|
||||||
QMenuBar::item {
|
|
||||||
background-color: #21252b;
|
|
||||||
color: #abb2bf;
|
|
||||||
}
|
|
||||||
QMenuBar::item:selected { /* when selected using mouse or keyboard */
|
|
||||||
background-color: #282c34;
|
|
||||||
}
|
|
||||||
QTextEdit, QTreeView {
|
|
||||||
background-color: #282c34;
|
|
||||||
color: #abb2bf;
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
self.setStyleSheet(dark_stylesheet)
|
|
||||||
|
|
||||||
def apply_light_theme(self):
|
|
||||||
light_stylesheet = """
|
|
||||||
QMainWindow {
|
|
||||||
background-color: #fafafa; /* Very light gray, almost white */
|
|
||||||
color: #383a42; /* Dark gray text */
|
|
||||||
}
|
|
||||||
QMenuBar {
|
|
||||||
background-color: #f0f0f0; /* Light gray for menu bar */
|
|
||||||
color: #383a42;
|
|
||||||
}
|
|
||||||
QMenuBar::item {
|
|
||||||
background-color: #f0f0f0;
|
|
||||||
color: #383a42;
|
|
||||||
}
|
|
||||||
QMenuBar::item:selected {
|
|
||||||
background-color: #fafafa;
|
|
||||||
}
|
|
||||||
QTextEdit, QTreeView {
|
|
||||||
background-color: #fafafa;
|
|
||||||
color: #383a42;
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
self.setStyleSheet(light_stylesheet)
|
|
||||||
|
|
||||||
def apply_system_theme(self):
|
|
||||||
self.setStyleSheet("")
|
|
||||||
|
|
||||||
def open_settings(self):
|
def open_settings(self):
|
||||||
pass
|
pass
|
||||||
@ -770,7 +655,6 @@ class MarkdownEditor(QMainWindow):
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
""" Main function to run the Markdown Editor application. """
|
""" Main function to run the Markdown Editor application. """
|
||||||
logging.info("Launching the application...")
|
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
editor = MarkdownEditor()
|
editor = MarkdownEditor()
|
||||||
editor.show()
|
editor.show()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user