Skip to content

Commit

Permalink
feat: updating demo (#34)
Browse files Browse the repository at this point in the history
* wip: working on app

* wip

* feat: just add all the stuff

* style: [pre-commit.ci] auto fixes [...]

* feat: cache qactions [wip]

* test: fix test

* feat: add findAction and test

* fix: fix for pyqt

* fix: add close method

* docs: demo closer

* chore: formatting

* fix: working context

* style: [pre-commit.ci] auto fixes [...]

* test: move demo, add test

* fix: lint

* fix: fix fonticon

* build: fix manifest

* test: fix qapp creation

* test: move test

* test: add fixture to test qapp

* test: ignore deprecation

* test: fix ignore

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
tlambert03 and pre-commit-ci[bot] committed Jul 13, 2022
1 parent 169f637 commit a76097a
Show file tree
Hide file tree
Showing 24 changed files with 773 additions and 63 deletions.
255 changes: 255 additions & 0 deletions demo/model_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
from typing import List

from fonticon_fa6 import FA6S
from qtpy.QtCore import QFile, QFileInfo, QSaveFile, Qt, QTextStream
from qtpy.QtWidgets import QApplication, QFileDialog, QMessageBox, QTextEdit

from app_model import Application, types
from app_model.backends.qt import QModelMainWindow
from app_model.expressions import create_context


class MainWindow(QModelMainWindow):
def __init__(self, app: Application):
super().__init__(app)

self._cur_file: str = ""
self._text_edit = QTextEdit()
self._text_edit.copyAvailable.connect(self._update_context)

self.setCentralWidget(self._text_edit)
self.setModelMenuBar([MenuId.FILE, MenuId.EDIT, MenuId.HELP])
self.addModelToolBar(MenuId.FILE, exclude={CommandId.SAVE_AS, CommandId.EXIT})
self.addModelToolBar(MenuId.EDIT)
self.statusBar().showMessage("Ready")

self.set_current_file("")

self._ctx = create_context(self)
self._ctx.changed.connect(self._on_context_changed)
self._ctx["copyAvailable"] = False

def _update_context(self, available: bool):
self._ctx["copyAvailable"] = available

def _on_context_changed(self):
self.menuBar().update_from_context(self._ctx)

def set_current_file(self, fileName: str) -> None:
self._cur_file = fileName
self._text_edit.document().setModified(False)
self.setWindowModified(False)

if self._cur_file:
shown_name = QFileInfo(self._cur_file).fileName()
else:
shown_name = "untitled.txt"

self.setWindowTitle(f"{shown_name}[*] - Application")

def save(self):
return self.save_file(self._cur_file) if self._cur_file else self.save_as()

def save_as(self):
fileName, filtr = QFileDialog.getSaveFileName(self)
if fileName:
return self.save_file(fileName)

return False

def save_file(self, fileName):
error = None
QApplication.setOverrideCursor(Qt.WaitCursor)
file = QSaveFile(fileName)
if file.open(QFile.OpenModeFlag.WriteOnly | QFile.OpenModeFlag.Text):
outf = QTextStream(file)
outf << self._text_edit.toPlainText()
if not file.commit():
reason = file.errorString()
error = f"Cannot write file {fileName}:\n{reason}."
else:
reason = file.errorString()
error = f"Cannot open file {fileName}:\n{reason}."
QApplication.restoreOverrideCursor()

if error:
QMessageBox.warning(self, "Application", error)
return False

def maybe_save(self):
if self._text_edit.document().isModified():
ret = QMessageBox.warning(
self,
"Application",
"The document has been modified.\nDo you want to save " "your changes?",
QMessageBox.StandardButton.Save
| QMessageBox.StandardButton.Discard
| QMessageBox.StandardButton.Cancel,
)
if ret == QMessageBox.StandardButton.Save:
return self.save()
elif ret == QMessageBox.StandardButton.Cancel:
return False
return True

def new_file(self):
if self.maybe_save():
self._text_edit.clear()
self.set_current_file("")

def open_file(self):
if self.maybe_save():
fileName, _ = QFileDialog.getOpenFileName(self)
if fileName:
self.load_file(fileName)

def load_file(self, fileName):
file = QFile(fileName)
if not file.open(QFile.OpenModeFlag.ReadOnly | QFile.OpenModeFlag.Text):
reason = file.errorString()
QMessageBox.warning(
self, "Application", f"Cannot read file {fileName}:\n{reason}."
)
return

inf = QTextStream(file)
QApplication.setOverrideCursor(Qt.WaitCursor)
self._text_edit.setPlainText(inf.readAll())
QApplication.restoreOverrideCursor()

self.set_current_file(fileName)
self.statusBar().showMessage("File loaded", 2000)

def about(self):
QMessageBox.about(
self,
"About Application",
"The <b>Application</b> example demonstrates how to write "
"modern GUI applications using Qt, with a menu bar, "
"toolbars, and a status bar.",
)

def cut(self):
self._text_edit.cut()

def copy(self):
self._text_edit.copy()

def paste(self):
self._text_edit.paste()

def close(self):
super().close()


# Actions defined declaratively outside of QMainWindow class ...
# menus and toolbars will be made and added automatically


class MenuId:
FILE = "file"
EDIT = "edit"
HELP = "help"


class CommandId:
SAVE_AS = "save_file_as"
EXIT = "exit"


ACTIONS: List[types.Action] = [
types.Action(
id="new_file",
icon=FA6S.file_circle_plus,
title="New",
keybindings=[types.StandardKeyBinding.New],
status_tip="Create a new file",
menus=[{"id": MenuId.FILE, "group": "1_loadsave"}],
callback=MainWindow.new_file,
),
types.Action(
id="open_file",
icon=FA6S.folder_open,
title="Open...",
keybindings=[types.StandardKeyBinding.Open],
status_tip="Open an existing file",
menus=[{"id": MenuId.FILE, "group": "1_loadsave"}],
callback=MainWindow.open_file,
),
types.Action(
id="save_file",
icon=FA6S.floppy_disk,
title="Save",
keybindings=[types.StandardKeyBinding.Save],
status_tip="Save the document to disk",
menus=[{"id": MenuId.FILE, "group": "1_loadsave"}],
callback=MainWindow.save,
),
types.Action(
id=CommandId.SAVE_AS,
title="Save As...",
keybindings=[types.StandardKeyBinding.SaveAs],
status_tip="Save the document under a new name",
menus=[{"id": MenuId.FILE, "group": "1_loadsave"}],
callback=MainWindow.save_as,
),
types.Action(
id=CommandId.EXIT,
title="Exit",
keybindings=[types.StandardKeyBinding.Quit],
status_tip="Exit the application",
menus=[{"id": MenuId.FILE, "group": "3_launchexit"}],
callback=MainWindow.close,
),
types.Action(
id="cut",
icon=FA6S.scissors,
title="Cut",
keybindings=[types.StandardKeyBinding.Cut],
enablement="copyAvailable",
status_tip="Cut the current selection's contents to the clipboard",
menus=[{"id": MenuId.EDIT}],
callback=MainWindow.cut,
),
types.Action(
id="copy",
icon=FA6S.copy,
title="Copy",
keybindings=[types.StandardKeyBinding.Copy],
enablement="copyAvailable",
status_tip="Copy the current selection's contents to the clipboard",
menus=[{"id": MenuId.EDIT}],
callback=MainWindow.copy,
),
types.Action(
id="paste",
icon=FA6S.paste,
title="Paste",
keybindings=[types.StandardKeyBinding.Paste],
status_tip="Paste the clipboard's contents into the current selection",
menus=[{"id": MenuId.EDIT}],
callback=MainWindow.paste,
),
types.Action(
id="about",
title="About",
status_tip="Show the application's About box",
menus=[{"id": MenuId.HELP}],
callback=MainWindow.about,
),
]


# Main setup

if __name__ == "__main__":
app = Application(name="my_app")
for action in ACTIONS:
app.register_action(action)
qapp = QApplication.instance() or QApplication([])
qapp.setAttribute(Qt.ApplicationAttribute.AA_DontShowIconsInMenus)
main_win = MainWindow(app=app)

app.injection_store.register_provider(lambda: main_win, MainWindow)
main_win.show()
qapp.exec_()
File renamed without changes.
12 changes: 12 additions & 0 deletions demo/multi_file/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import pathlib
import sys

sys.path.append(str(pathlib.Path(__file__).parent.parent))

from multi_file.app import MyApp # noqa: E402
from qtpy.QtWidgets import QApplication # noqa: E402

qapp = QApplication.instance() or QApplication([])
app = MyApp()
app.show()
qapp.exec_()
16 changes: 8 additions & 8 deletions demo_app/actions.py → demo/multi_file/actions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import List

from fonticon_fa5 import FA5S
from fonticon_fa6 import FA6S

from app_model.types import Action, KeyBindingRule, KeyCode, KeyMod, MenuRule

Expand All @@ -11,31 +11,31 @@
Action(
id=CommandId.OPEN,
title="Open",
icon=FA5S.folder_open,
icon=FA6S.folder_open,
callback=functions.open_file,
menus=[MenuRule(id=MenuId.FILE)],
keybindings=[KeyBindingRule(primary=KeyMod.CtrlCmd | KeyCode.KeyO)],
),
Action(
id=CommandId.CLOSE,
title="Close",
icon=FA5S.window_close,
icon=FA6S.window_close,
callback=functions.close,
menus=[MenuRule(id=MenuId.FILE)],
keybindings=[KeyBindingRule(primary=KeyMod.CtrlCmd | KeyCode.KeyW)],
),
Action(
id=CommandId.UNDO,
title="Undo",
icon=FA5S.undo,
icon=FA6S.undo,
callback=functions.undo,
menus=[MenuRule(id=MenuId.EDIT, group="1_undo_redo")],
keybindings=[KeyBindingRule(primary=KeyMod.CtrlCmd | KeyCode.KeyZ)],
),
Action(
id=CommandId.REDO,
title="Redo",
icon=FA5S.redo,
icon=FA6S.rotate_right,
callback=functions.redo,
menus=[MenuRule(id=MenuId.EDIT, group="1_undo_redo")],
keybindings=[
Expand All @@ -45,23 +45,23 @@
Action(
id=CommandId.CUT,
title="Cut",
icon=FA5S.cut,
icon=FA6S.cut,
callback=functions.cut,
menus=[MenuRule(id=MenuId.EDIT, group="3_copypaste")],
keybindings=[KeyBindingRule(primary=KeyMod.CtrlCmd | KeyCode.KeyX)],
),
Action(
id=CommandId.COPY,
title="Copy",
icon=FA5S.copy,
icon=FA6S.copy,
callback=functions.copy,
menus=[MenuRule(id=MenuId.EDIT, group="3_copypaste")],
keybindings=[KeyBindingRule(primary=KeyMod.CtrlCmd | KeyCode.KeyC)],
),
Action(
id=CommandId.PASTE,
title="Paste",
icon=FA5S.paste,
icon=FA6S.paste,
callback=functions.paste,
menus=[MenuRule(id=MenuId.EDIT, group="3_copypaste")],
keybindings=[KeyBindingRule(primary=KeyMod.CtrlCmd | KeyCode.KeyV)],
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit a76097a

Please sign in to comment.